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

Programming System 02

Last modified: 22/04/2004 05:33:42 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

Fork and Wait

Tutorial class by Philippe Décogné

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

Problem 1

  1. Describe the format for creating process on UNIX systems.
  2. Describe the mechanism for creating process on UNIX systems.
  3. Describe the boot steps on UNIX systems.
  4. Comment the following traces

Progsys 02-04

Solution

1 - Format for creating process on UNIX systems

Fork: pid_t fork()

Creates a child process. Returns the PID of the child process to the parent process and O to the child process. As a result, the parent and child branches are distinguished in the program with an if on the PID's value (pid = 0, we're in the child process).

Under Mac OS X pid_t is an int32_t.

If the creation of the child process failed, fork() returns -1. You can use perror() to retrieve the error message.

Libraries to be included: sys/types.h for pid_t and unistd.h for fork.

Man page: fork(2) - system calls

Exit: void _exit(int status)

Terminates a process (useful while debugging). The value of status is sent to the parent process.

Other possibility: void exit(int status)

In this case, use EXIT_SUCCESS and EXIT_FAILURE as value for status.

Libraries to be included: unistd.h for _exit, stdlib_h for exit and the values of status.

Man pages: _exit(2) - system calls, exit(3) - subroutines

Wait: pid_t wait(int *status)

Suspends the execution of the calling process until status information is available for a terminated child process. Returns -1 to the parent process if there is no child process previously waiting for termination, otherwise returns the pid of the terminated or stopped child process to the parent process (in the latter case, the child process becomes a zombie). Validates the end of the child process, frees the entry in the process table and gives status information on the end of the child process.

Other possibility: pid_t waitpid(pid_t wpid, int *status, int options) to suspend a named process or pid_t wait4(pid_t wpid, int *status, int options, struct rusage *rusage) to retrieve information on the resources of the suspended process.

Libraries to be included: sys/types.h for pid_t and sys/wait.h for wait and waitpid, sys/time.h and sys/resource.h for wait4.

Man pages: wait(2) - system calls

2 - Mechanism for creating process on UNIX systems

During the execution of fork():

The child process inherits all the attributes from the parent process except:

Caption

Progsys 02-01

At child process's creation time, a file already opened points to the same offset in both processes (child and parent). Therefore, it should be wise to open files after the fork if you want to have distinct pointers for a given file in both processes, or to open files before the fork if you want to share resources.

A flag in the table of open files indicates the number of processes that opened the file. The flag is incremented/decremented when a process opens/closes the file. The file is closed only when the flag reaches null value (there is no process anymore in the table of open files which are listed as having opened the file).

3 - Boot system goal

Getting a copy of the operating system in main memory and launching its execution.

Main steps:

Caption

Progsys 02-02

Transitions while forking

Caption

Progsys 02-03

A process can be executed in user mode or in kernel mode. Typically a user program executes in user mode, unless it makes system calls (low-level primitives) or during interrupts. In this case, it switches to kernel mode then returns to user mode.

When returning to user mode, the process can be preempted in favor of another ready process with higher priority.

At the end of the child process, all its resources are freed and the process becomes a zombie, i.e. it is no more accessible. Only its identification remains in the process table. This identification is used to send a signal to its parent whenever the parent asks for it (during a wait). At that time the identification is removed from the process table. If the parent process terminates before the child process has sent its termination signal, the child process becomes an orphaned process and is adopted by process 1. When issuing a ps -aux, this process is shown as a zombie (Z in STAT column).

4 - Comments on traces

Processes created at boot time

Init

Last boot step. Created by process 0. Parent of all other processes. Initialize the process control. See /etc/rc for further information about operations launched at boot time.

update

Update file system data.

mingetty

Open and initialize the line tty, read the name of login, and invoque it.

xdm

X display manager.

xsession

Launch the X session.

Processus démons

kflushd

Update data.

kswapd

Control pages swapping.

syslogd

Read and write log messages onto the system console.

crond

Execute scheduled commands.

inetd

Listen to connexions on some Internet sockets.

Shell

-bash

Suspended commands

nedit

Running commands

ps ax

Variations on the primitive open

Problem: read a text file 4 characters at once using fork.

The text file does not contain necessarily a number of characters multiple of 4. Try to test all possible errors you may think of and recover from them either with perror or strerrno.

One of the variant includes the creation of the text file.

Solution

Primitives used in the code:

Open: int open(const char *path, int flags, mode_t mode)

Opens a file named path for reading and/or writing according to the arguments flags and optionally the mode mode. The primitive returns the descriptor of the open file or -1 if it failed. The pointer is positionned at the beginning of the file.

Modes are defined in chmod(2). They are used only for a file creation with the flag O_CREAT, which creates a new file unless it already exists.

Libraries to be included: fcntl.h for open, sys/stat.h for the modes.

Man pages: open(2), chmod(2) - system calls

Read: ssize_t read(int d, void *buf, size_t nbytes)

Attempts to read nbytes of data from the object referenced by the descriptor d and put the result into the buffer pointed to by buf. Returns the number of bytes read or 0 upon reading end of file, or -1 if read failed.

Libraries to be included: unidstd.h for read and sys/types.h for ssize_t and size_t.

Man page: read(2) - system calls

Write: ssize_t write(int d, const void *buf, size_t nbytes)

Attempts to write nbytes of data to the object referenced by the descriptor d at the current location from the buffer pointed to by buf. Returns the number of bytes written, or -1 if write failed.

Libraries to be included: unidstd.h for write, sys/types.h for ssize_t and size_t.

Man page: write(2) - system calls

Close: int close(int d)

Deletes the descriptor d from the per-process object reference table. The object is deactivated unless there is another reference to the object (if the object is a file, the file is closed or more accurately the seek pointer is deleted). The primitive returns -1 if it failed, otherwise 0.

Library to be included: unidstd.h

Man page: close(2) - system calls

Perror: void perror(const char *string)

Retrieves the error message corresponding to the last error issued. Sends to the standard error file descriptor the character string string followed by a semi-colon, a space, the error message, and a newline.

This allows you to customize a standard error message. Be aware though, that this primitive should be called just after the function which could send an error message. On certain occasions, you should have to reset errno to 0 before calling a function which could send an error message (see the man pages corresponding to the calling function for further details).

Other possibility: char *strerror(int errnum)

Returns a pointer to the error message corresponding to errnum. You have to print it with printf. If you want to print the error numbers, you should have declared the external global variable beforehand.

Libraries to be included: stdio.h for perror, string.h for strerror.

Man pages: perror(3), strerror(3) - subroutines

Sleep: unsigned int sleep(unsigned int seconds)

Suspends the execution of the calling process for seconds seconds or until a signal stops the function or terminates the process. Returns 0 if seconds seconds have elapsed, otherwise returns the requested time minus the slept time.

Library to be included: unidstd.h

Man page: sleep(3) - subroutines

First program

/* pidessai1.c

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

Each read is four bytes long. The program creates a child process.
Both processes read the file opened before the fork.

The for loops are purposely used with counters greater that the number of
4-characters blocks in the file, in order to test the redirection
of errors.

The errors are managed with perror and child process exits with _exit. */

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

int main()
{
	int fp, i, j, numbread, closestatus, status;
	pid_t pid, waitsignal;
	unsigned int numbsecond;
	char ch[4], sch[5];

	fp = open("Fichier", O_RDWR);

	if (fp == -1)
    {
		perror("open Fichier");
		_exit(1);
    }

	pid = fork();

	if (pid == -1)
    {
		perror("Fork");
    }
	else if (pid == 0)
    {
		for (i = 0; i < 4; i++)
		{
			printf("i in the child: %d\n", i);
			if (fp != -1)
			{
				numbread = read(fp, ch, 4);
				if (numbread == -1)
				{
					perror("read Fichier in the child process");
					break;
				}
				else if (numbread == 0)
				{
					perror("End of Fichier encountered in the child process");
					break;
				}
				else
				{
					strcpy(sch, ch);
					printf("Hello fils: %s\n", sch);
					numbsecond = sleep(4);
					printf("Number of seconds: %d in the child process\n", numbsecond);
				}
			}
		}
		_exit(0);
    }
	else
    {
		for (j = 0; j < 4; j++)
		{
			printf("j in the parent: %d\n", j);
			if (fp != -1)
			{
				numbread = read(fp, ch, 4);
				if (numbread == -1)
				{
					perror("read Fichier in the parent process");
					break;
				}

				else if (numbread == 0)
				{
					perror("End of Fichier encountered in the parent process");
					break;
				}
				else
				{
					strcpy(sch, ch);
					printf("Hello pere: %s\n", sch);
					numbsecond = sleep(5);
					printf("Number of seconds: %d in the parent process\n", numbsecond);
				}
			}
		}
		waitsignal = wait(&status);
		if (waitsignal == -1)
		{
			perror("wait");
			_exit(1);
		}
		else
		{
			printf("Wait from child: %d succeeded.\n", (int)waitsignal);
		}
		closestatus =  close(fp);
		if (closestatus == -1)
		{
			perror("close Fichier");
			_exit(1);
		}
		else
		{
			printf("File Fichier closed\n");
		}
    }
	return 0;
}

Second program

/* pidessai2.c

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

Each read is four bytes long. The program creates a child process.
Both processes read the file opened before the fork.

The for loops are purposely used with counters greater that the number of
4-characters blocks in the file, in order to test the redirection
of errors.

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

/* Includes */
#include <stdio.h>        /* for access to the standard i/o files, printf */
#include <sys/types.h>    /* for fork, wait, read */
#include <fcntl.h>        /* for open */
#include <unistd.h>       /* for fork, read, close, sleep */
#include <sys/wait.h>     /* for wait */
#include <stdlib.h>       /* for exit, EXIT_SUCCESS, EXIT_FAILURE */
#include <string.h>       /* for strerror */
#include <sys/errno.h>    /* for ECHILD*/

/* Extern global variables */
extern int errno;

int main()
{
	/* Variables */
	int fp;                  /* file descriptor returned from open */
	int i, j;                /* loop counters in child resp. parent process */
	int  numbread;           /* number of read bytes returned from read */
	int closestatus;         /* returned from close */
	int status;              /* returned when the child process terminates */
	int pid;                 /* returned from fork */
	int waitsignal;          /* returned from wait */
	unsigned int numbsecond; /* returned from sleep */
	char ch[4];              /* buffer for bytes stream */

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

	/* If open fails, print the error message and quit */
	if (fp == -1)
    {
		printf("Error number %d while opening the file Fichier with message error: %s.\n", errno, strerror(errno));
		return 0;
    }

	/* Create a child process */
	pid = (int)fork();

	/* If fork fails, print the error message and go to the parent process */
	if (pid == -1)
    {
		printf("Error number %d while creating the child process with error message: %s.\n", errno, strerror(errno));
    }

	/* Fork succeeds, Child process */
	else if (pid == 0)
    {
		/* Purposely exceed the number of bytes in the file */
		for (i = 0; i < 6; i++)
		{
			/* Read 4 bytes from the file */
			numbread = read(fp, ch, 4);

			/* If read fails, print the error message and exit */
			if (numbread == -1)
			{
				printf("Error number %d while reading the file during the child process with error message: %s.\n", errno, strerror(errno));
				exit(EXIT_FAILURE);
			}

			/* If end of file encountered while reading, print the error message and exit */
			else if (numbread == 0)
			{
				printf("End of file encountered in the child process.\n");
				exit(EXIT_FAILURE);
			}

			/* Read succeeds, print the bytes and sleep 4 seconds */
			else
			{
				printf("Hello fils: %s\n", ch);
				numbsecond = sleep(4);
			}
		}

		/* End of child process */
		exit(EXIT_SUCCESS);
    }

	/* Parent process */
	else
    {
		for (j = 0; j < 2; j++)
		{
			/* Read 4 bytes from the file */
			numbread = read(fp, ch, 4);

			/* If read fails, print the error message and go to wait for child process terminating */
			if (numbread == -1)
			{
				printf("Error number %d while reading the file in the parent process with error message: %s.\n", errno, strerror(errno));
			}

			/* If end of file encountered, print the error message and go to wait for child process terminating */
			else if (numbread == 0)
			{
				printf("End of file encountered in the parent process.\n");
			}

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

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

		/* If there is no child process, print the error message and go to close file */
		if (waitsignal == -1 && errno == ECHILD)
		{
			printf("No child process awaiting.\n");
		}

		/* If wait fails, print the error message and go to close file */
		else if (waitsignal ==  -1 && errno != ECHILD)
		{
			printf("Wait failed with error number: %d and error message: %s.\n", errno, strerror(errno));
		}

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

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

		/* If close fails, print the error message and quit */
		if (closestatus == -1)
		{
			printf("Error number %d while closing the file with error message: %s.\n", errno, strerror(errno));
		}

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

Third program

/* pidessai3.c

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

Each read is four bytes long. The program creates a child process.
Both processes read the file opened before the fork.

The for loops are purposely used with counters greater that the number of
4-characters blocks in the file, in order to test the redirection
of errors.

The errors are managed with strerrno and child process exits with exit. 

The file FICHIER is created if it does not exist.*/

/* Includes */
#include <stdio.h>        /* for access to the standard i/o files, printf */
#include <sys/types.h>    /* for fork, wait, read, write */
#include <sys/stat.h>	  /* for open */
#include <fcntl.h>        /* for open */
#include <unistd.h>       /* for fork, read, close, sleep, write */
#include <sys/wait.h>     /* for wait */
#include <stdlib.h>       /* for exit, EXIT_SUCCESS, EXIT_FAILURE */
#include <string.h>       /* for strerror */
#include <sys/errno.h>    /* for ECHILD*/
#include <sys/uio.h>	  /* for write */

/* Extern global variables */
extern int errno;

int main()
{
	/* Variables */
	int fp;                  /* file descriptor returned from open */
	int i, j;                /* loop counters in child resp. parent process */
	int  numbread;           /* number of read bytes returned from read */
	int closestatus;         /* returned from close */
	int status;              /* returned when the child process terminates */
	int pid;                 /* returned from fork */
	int waitsignal;          /* returned from wait */
	unsigned int numbsecond; /* returned from sleep */
	char ch[4], sch[5];              /* buffer for bytes stream */
	char wch[26] = "Coucou les petits amis !";            /* buffer for writing */
	ssize_t wbytes;			 /* number of bytes written */


	/* Open the file Fichier for reading and writing */
	fp = open("Fichier1", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);

	/* If open fails, print the error message and quit */
	if (fp == -1 && errno != EEXIST)
    {
		printf("Error number %d while opening the file Fichier with message error: %s.\n", errno, strerror(errno));
		return 0;
    }
	else if (fp == -1)
	{
		printf("The File Fichier already exists: %s.\n", strerror(errno));
		/*return 0;*/
	}
	else
	{
		wbytes = write(fp, wch, sizeof(wch));
		if (wbytes == -1)
		{
			printf("Error while writing: %s.\n", strerror(errno));
		}
		
		/* Close the file */
		closestatus =  close(fp);

		/* If close fails, print the error message and quit */
		if (closestatus == -1)
		{
			printf("Error number %d while closing the file with error message: %s.\n", errno, strerror(errno));
		}

		/* Close succeeds, print the message */
		else
		{
			printf("File Fichier closed.\n");
		}
	}

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

	/* If open fails, print the error message and quit */
	if (fp == -1)
    {
		printf("Error number %d while opening the file Fichier with message error: %s.\n", errno, strerror(errno));
		return 0;
    }

	/* Create a child process */
	pid = (int)fork();

	/* If fork fails, print the error message and go to the parent process */
	if (pid == -1)
    {
		printf("Error number %d while creating the child process with error message: %s.\n", errno, strerror(errno));
    }

	/* Fork succeeds, Child process */
	else if (pid == 0)
    {
		/* Purposely exceed the number of bytes in the file */
		for (i = 0; i < 6; i++)
		{
			/* Read 4 bytes from the file */
			numbread = read(fp, ch, 4);

			/* If read fails, print the error message and exit */
			if (numbread == -1)
			{
				printf("Error number %d while reading the file during the child process with error message: %s.\n", errno, strerror(errno));
				exit(EXIT_FAILURE);
			}

			/* If end of file encountered while reading, print the error message and exit */
			else if (numbread == 0)
			{
				printf("End of file encountered in the child process.\n");
				exit(EXIT_FAILURE);
			}

			/* Read succeeds, print the bytes and sleep 4 seconds */
			else
			{
				strcpy(sch, ch);
				printf("Hello fils: %s\n", sch);
				numbsecond = sleep(4);
			}
		}

		/* End of child process */
		exit(EXIT_SUCCESS);
	}
	/* Parent process */
	else
	{
		for (j = 0; j < 2; j++)
		{
			/* Read 4 bytes from the file */
			numbread = read(fp, ch, 4);

			/* If read fails, print the error message and go to wait for child process terminating */
			if (numbread == -1)
			{
				printf("Error number %d while reading the file in the parent process with error message: %s.\n", errno, strerror(errno));
			}

			/* If end of file encountered, print the error message and go to wait for child process terminating */
			else if (numbread == 0)
			{
				printf("End of file encountered in the parent process.\n");
			}

			/* Read succeeds, print the bytes and sleep 5 seconds */
			else
			{
				strcpy(sch, ch);
				printf("Hello pere: %s\n", sch);
				numbsecond = sleep(5);
			}
		}

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

		/* If there is no child process, print the error message and go to close file */
		if (waitsignal == -1 && errno == ECHILD)
		{
			printf("No child process awaiting.\n");
		}

		/* If wait fails, print the error message and go to close file */
		else if (waitsignal ==  -1 && errno != ECHILD)
		{
			printf("Wait failed with error number: %d and error message: %s.\n", errno, strerror(errno));
		}

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

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

		/* If close fails, print the error message and quit */
		if (closestatus == -1)
		{
			printf("Error number %d while closing the file with error message: %s.\n", errno, strerror(errno));
		}

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

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): 08/10/2008 13:20:06 CEST