
Programming System
Fork and Wait
Tutorial class by Philippe Décogné
02/11/2002 (dd/mm/yy)

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

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

Transitions while forking
Caption

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
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.
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;
}