Links Powered by MicMac
Cocoa-C-Porting
Fink's translation team
Mac OSX, Fink and Unix
Client/Server Architecture
PowerPC Architecture - UML
System programming
Merise
Pierre-Loïc and Pascaline's wedding

Programming System 04

Last modified: 22/04/2004 05:29:12 CEST
Author: Michèle Garoche contact

Contents - Process scheduling - Fork and Wait - Pthread and Exec - Exec - Pipes - Message queues - Memory management - Paging and segmentation - Synchronization (1) - Synchronization (2) - File System Management - Socket Programming (1) - Revision

Programming System

Exec

Tutorial class by Philippe Décogné

16/11/2002 (dd/mm/yy)

Problem 2

Consider the second program studied in Programming System 2.

We wish to replace the code in the child process by a call to an exec primitive. Give the necessary changes in the program and the code of the program called by the child process.

Solution

Changes in the code

First we have to pass the id of the text file to be read in the arguments of the program called by the child process. This argument should be a pointer to a character string.

To go deeper in the understanding of the behaviour of exec primitives, we chose to store the program called by the child process in the user's HOME/bin directory. Therefore we have to set up a global variable to retrieve the path of the user's HOME directory and to build the path of the program called by the child process from this former path.

Consequence in the program called by the child program

We must convert the character string corresponding to the text file's id to an int, so that we can use it within the primitive read.

Primitives to be used:

Getenv: char * getenv(const char *name)

Retrieves an environment variable from the host environment list. Returns a NULL pointer if the variable does not exist.

Library to be used: stdlib.h

Man page: getenv(3) - subroutines.

Strcat: char *strcat(char *s, const char *append)

Adds the character string append to the character string s. Returns a pointer to s.

Library to be used: string.h

Man page: strcat(3) - subroutines.

Snprintf: int snprintf(char *str, size_t size, const char *format, ...)

Writes into the character string str the result of formating according to the string format (identical to printf format) the arguments following the string format. The string str is truncated to at most size-1 characters and '\0' is added at the end of the string. Returns the number of characters which would have written if the string str were unlimited.

Library to be used: stdio.h

Man page: snprintf(3) - subroutines.

Strtol: long strtol (const char *nptr, char **endptr, int base)

Converts the string nptr written in base base into a long. endptr is used to validate the conversion. Returns the result of the conversion or 0 if the conversion is impossible; if it overflows returns LONG_MAX and LONG_MIN if it underflows.

Libraries to be used: stdlib.h, limits.h

Man page: strtol(3) - subroutines.

Main program

/* liref.c
   Read a file named FICHIER, which contains this message:
   Coucou, les petits amis !

   Each read is four bytes long. The program creates a child process which calls lire_fichier.c with execlp.

   Both processes read the file opened before the fork.
   
   The program purposely exceeds the limits 
   of the file to test the error end of file encountered.

   The errors are managed with strerrno and child process exit with exit. */

/* Includes */
#include <stdio.h>		/* for standard io, printf, snprintf */
#include <fcntl.h>		/* for open */
#include <unistd.h>		/* for fork, execlp, read, sleep, close */
#include <stdlib.h>		/* for exit, getenv */
#include <sys/wait.h>	/* for wait */
#include <string.h>		/* for strerror, strcat */
#include <sys/errno.h>	/* for ECHILD */

/* Global variables */
extern int errno;					/* for standard errors */
static char *pathToLireFichier;		/* for path to second program called with execlp */

int main(int argc, char **argv)
{
  int i ;						/* counter for reading loop */
  int fp;						/* file id */
  int convertint;				/* result of snprintf */
  int numbread;				/* number of chars read */
  int closestatus;			/* result of close */
  int status;      			/* exit status of child process */
  pid_t pid;					/* id of child process; */
  pid_t waitsignal;			/* result of wait */
  unsigned int numbsecond;	/* result of sleep function */
  char ch[4];   				/* array of chars for read */

  /* For the first argument of snprintf, which holds the conversion from int to string; as fp is an int-32 on Mac, 
  we should have 10 digits (2 raised to power 32 contains 10 digits) plus the null terminator */
  char conv[11];

  /* Neutral args for main */
  (void) argc;
  (void) argv;

  /* Open the file for reading */
  fp = open("Fichier", O_RDONLY);

  /* Open fails */
  if (fp == -1)
    {
      printf("Error while opening the file FICHIER: %s\n", strerror(errno));
      exit(EXIT_FAILURE);
    }

  printf("fp value in main process: %d\n", fp);

  /* Get the path to second program */
  pathToLireFichier = getenv("HOME");
  strcat(pathToLireFichier, "/bin/lire_fichier");
	
  /* Create the child process */
  pid = fork();

  /* Fork fails */
  if (pid == -1)
    {
      printf("Error while creating child process: %s\n", strerror(errno));
    }
  /* Fork succeeds */
  else if (pid == 0)
    {
      /* Convert the fp value to string in order to pass it as argument to execlp */
      convertint = snprintf(conv, sizeof(conv), "%d", fp);

      /* Call to lire_fichier.c */
      execlp(pathToLireFichier, "lire_fichier", conv, NULL);

      /* Call to execlp fails */
      printf("Call to execlp has failed: %s\n", strerror(errno));

      /* Pass the status to parent process */
      exit(EXIT_FAILURE);
    }
  else
    {
      printf("Child process number: %d\n", (int)pid);

      for (i = 0; i < 6; i++)
	{
	  /* Read 4 bytes from the file */
	  numbread = read(fp, ch, 4);

	  /* Read fails */
	  if (numbread == -1)
	    {
	      printf("Error while reading the file in the parent process: %s.\n", strerror(errno));
	      break;
	    }

	  /* End of file encountered */
	  else if (numbread == 0)
	    {
	      printf("End of file encountered in the parent process.\n");
	      break;
	    }

	  /* Read succeeds */
	  else
	    {
	      printf("Hello pere: %s\n", ch);
	      numbsecond = sleep(5);
	    }
	}
    }

  /* Wait for child process terminating */
  waitsignal = wait(&status);

  /* No child process */
  if (waitsignal == -1 && errno == ECHILD)
    {
      printf("No child process awaiting.\n");
    }

  /* Wait fails */
  else if (waitsignal ==  -1 && errno != ECHILD)
    {
      printf("Wait failed: %s.\n", strerror(errno));
    }

  /* Wait succeeds */
  else
    {
      printf("Child process number %d exits normally. Wait succeeded.\n", waitsignal);
    }

  /* Close the file */
  closestatus =  close(fp);

  /* Close fail */
  if (closestatus == -1)
    {
      printf("Error  while closing the file: %s.\n", strerror(errno));
    }

  /* Close succeeds */
  else
    {
      printf("File Fichier closed.\n");
    }
  return 0;
}

Program called by the child process

/* lire_fichier.c

Child process called from liref.c with execlp

Read a file named FICHIER, which contains this message:
Coucou, les petits amis !

Each read is four bytes long.

The program purposely exceeds the limits of the file to test the error end of file encountered.

The errors are managed with strerrno and child process exit with exit. */

/* includes */
#include <stdio.h>		/* for standard io, printf */
#include <unistd.h>		/* for read, sleep */
#include <stdlib.h>		/* for strtol, exit */
#include <string.h>		/* for strerror */

/* Global variables */
extern int errno;

int main(int argc, char **argv)
{
  int fp;			/* file id */
  int i;			/* counter for reading loop */
  int numbread;	/* number of chars read */
  char ch[4];     /* char array for read */

  /* Retrieve the id of file */
  fp = (int)strtol(argv[1],(char **)NULL, 10);
  printf("fp value in child process: %d\n", fp);

  for (i = 0; i < 5; i++)
    {
      /* Read 4 bytes from the file */
      numbread = read(fp, ch, 4);

      /* Read fails */
      if (numbread == -1)
	{
	  printf("Error while reading the file in the child process: %s\n", strerror(errno));
	  exit(EXIT_FAILURE);
	}
      /* End of file encountered while reading */
      else if (numbread == 0)
	{
	  printf("End of file encountered in the child process: %s\n", strerror(errno));
	  break;
	}
      /* Read succeeds */
      else
	{
	  printf("hello fils: %s\n", ch);
	  sleep(4);
	}
    }
  return 0;
}

Problem 3

Write a pseudo-code corresponding to the algorithm for a shell. You should take into account the following steps:

The shell waits for the end of a foreground command before accepting a new command; on the contrary, while a background command executes, the shell accepts new commands.

Solution

Execution loop for the shell

/* shell.c */

Begin
    Loop
        read a command from the standard input
        analyse the command (name, arguments, &)
        
        if the last character of the command is &
            then BACKGROUND = true
        else
            then BACKGROUND = false
        endif
        
        pid = fork()
        
        if pid ==0
            then
                execlp("command", "   ", "   ", ..., NULL);
                perror("execlp");
        endif
        
        if BACKGROUND = false
            then wait(&status)
        endif
    endloop
end

Contents - Process scheduling - Fork and Wait - Pthread and Exec - Exec - Pipes - Message queues - Memory management - Paging and segmentation - Synchronization (1) - Synchronization (2) - File System Management - Socket Programming (1) - Revision

Powered by Apache/1.3.41 (Darwin) PHP/4.4.8 on Mac OS X bluefish distributed.net Cssed icon Conglomerate icon Valid HTML 4.0.1 Valid CSS
Local date (dd/mm/yyyy): 29/08/2008 06:10:05 CEST