when called with a command-line argument of 4.

Team-FLY

3.3 UNIX Process Creation and fork

A process can create a new process by calling fork . The calling process becomes the parent , and the created process is called the child . The fork function copies the parent's memory image so that the new process receives a copy of the address space of the parent. Both processes continue at the instruction after the fork statement (executing in their respective memory images).

  SYNOPSIS  #include <unistd.h>    pid_t fork(void);  POSIX  

Creation of two completely identical processes would not be very useful. The fork function return value is the critical characteristic that allows the parent and the child to distinguish themselves and to execute different code. The fork function returns 0 to the child and returns the child's process ID to the parent. When fork fails, it returns “1 and sets the errno . If the system does not have the necessary resources to create the child or if limits on the number of processes would be exceeded, fork sets errno to EAGAIN . In case of a failure, the fork does not create a child.

Example 3.5 simplefork.c

In the following program, both parent and child execute the x = 1 assignment statement after returning from fork .

 #include <stdio.h> #include <unistd.h> int main(void) {    int x;    x = 0;    fork();    x = 1;    printf("I am process %ld and my x is %d\n", (long)getpid(), x);    return 0; } 

Before the fork of Example 3.5, one process executes with a single x variable. After the fork, two independent processes execute, each with its own copy of the x variable. Since the parent and child processes execute independently, they do not execute the code in lock step or modify the same memory locations. Each process prints a message with its respective process ID and x value.

The parent and child processes execute the same instructions because the code of Example 3.5 did not test the return value of fork . Example 3.6 demonstrates how to test the return value of fork .

Example 3.6 twoprocs.c

After fork in the following program, the parent and child output their respective process IDs.

 #include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(void) {    pid_t childpid;    childpid = fork();    if (childpid == -1) {       perror("Failed to fork");       return 1;    }    if (childpid == 0)                              /* child code */       printf("I am child %ld\n",  (long)getpid());    else                                           /* parent code */       printf("I am parent %ld\n",  (long)getpid());    return 0; } 

The original process in Example 3.6 has a nonzero value of the childpid variable, so it executes the second printf statement. The child process has a zero value of childpid and executes the first printf statement. The output from these processes can appear in either order, depending on whether the parent or the child executes first. If the program is run several times on the same system, the order of the output may or may not always be the same.

Exercise 3.7 badprocessID.c

What happens when the following program executes?

 #include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(void) {    pid_t childpid;    pid_t mypid;    mypid = getpid();    childpid = fork();    if (childpid == -1) {       perror("Failed to fork");       return 1;    }    if (childpid == 0)                                   /* child code */       printf("I am child %ld, ID = %ld\n", (long)getpid(), (long)mypid);    else                                                /* parent code */       printf("I am parent %ld, ID = %ld\n", (long)getpid(), (long)mypid);    return 0; } 

Answer:

The parent sets the mypid value to its process ID before the fork. When fork executes, the child gets a copy of the process address space, including all variables . Since the child does not reset mypid , the value of mypid for the child does not agree with the value returned by getpid .

Program 3.1 creates a chain of n processes by calling fork in a loop. On each iteration of the loop, the parent process has a nonzero childpid and hence breaks out of the loop. The child process has a zero value of childpid and becomes a parent in the next loop iteration. In case of an error, fork returns “1 and the calling process breaks out of the loop. The exercises in Section 3.8 build on this program.

Figure 3.2 shows a graph representing the chain of processes generated for Program 3.1 when n is 4. Each circle represents a process labeled by its value of i when it leaves the loop. The edges represent the is-a-parent relationship. A B means process A is the parent of process B.

Figure 3.2. Chain of processes generated by Program 3.1 when called with a command-line argument of 4.

graphics/03fig02.gif

Program 3.1 simplechain.c

A program that creates a chain of n processes, where n is a command-line argument .

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main (int argc, char *argv[]) {    pid_t childpid = 0;    int i, n;    if (argc != 2){   /* check for valid number of command-line arguments */       fprintf(stderr, "Usage: %s processes\n", argv[0]);       return 1;    }    n = atoi(argv[1]);    for (i = 1; i < n; i++)       if (childpid = fork())          break;    fprintf(stderr, "i:%d  process ID:%ld  parent ID:%ld  child ID:%ld\n",            i, (long)getpid(), (long)getppid(), (long)childpid);    return 0; } 
Exercise 3.8

Run Program 3.1 for large values of n . Will the messages always come out ordered by increasing i ?

Answer:

The exact order in which the messages appear depends on the order in which the processes are selected by the process scheduler to run. If you run the program several times, you should notice some variation in the order.

Exercise 3.9

What happens if Program 3.1 writes the messages to stdout , using printf , instead of to stderr , using fprintf ?

Answer:

By default, the system buffers output written to stdout , so a particular message may not appear immediately after the printf returns. Messages to stderr are not buffered, but instead written immediately. For this reason, you should always use stderr for your debugging messages.

Program 3.2 creates a fan of n processes by calling fork in a loop. On each iteration, the newly created process breaks from the loop while the original process continues. In contrast, the process that calls fork in Program 3.1 breaks from the loop while the newly created process continues for the next iteration.

Program 3.2 simplefan.c

A program that creates a fan of n processes where n is passed as a command-line argument .

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main (int argc, char *argv[]) {    pid_t childpid = 0;    int i, n;    if (argc != 2){   /* check for valid number of command-line arguments */       fprintf(stderr, "Usage: %s processes\n", argv[0]);       return 1;    }    n = atoi(argv[1]);    for (i = 1; i < n; i++)       if ((childpid = fork()) <= 0)          break;    fprintf(stderr, "i:%d  process ID:%ld  parent ID:%ld  child ID:%ld\n",            i, (long)getpid(), (long)getppid(), (long)childpid);    return 0; } 

Figure 3.3 shows the process fan generated by Program 3.2 when n is 4. The processes are labeled by the value of i at the time they leave the loop. The original process creates n “1 children. The exercises in Section 3.9 build on this example.

Figure 3.3. Fan of processes generated by Program 3.2 with a command-line argument of 4.

graphics/03fig03.gif

Exercise 3.10

Explain what happens when you replace the test

 (childpid = fork()) <= 0 

of Program 3.2 with

 (childpid = fork()) == -1 

Answer:

In this case, all the processes remain in the loop unless the fork fails. Each iteration of the loop doubles the number of processes, forming a tree configuration illustrated in Figure 3.4 when n is 4. The figure represents each process by a circle labeled with the i value at the time it was created. The original process has a 0 label. The lowercase letters distinguish processes that were created with the same value of i . Although this code appears to be similar to that of Program 3.1, it does not distinguish between parent and child after fork executes. Both the parent and child processes go on to create children on the next iteration of the loop, hence the population explosion.

Exercise 3.11

Run Program 3.1, Program 3.2, and a process tree program based on the modification suggested in Exercise 3.10. Carefully examine the output. Draw diagrams similar to those of Figure 3.2 through Figure 3.4, labeling the circles with the actual process IDs. Use to designate the is-a-parent relationship. Do not use large values of the command-line argument unless you are on a dedicated system. How can you modify the programs so that you can use ps to see the processes that are created?

Answer:

In their current form, the programs complete too quickly for you to view them with ps . Insert the sleep(30); statement immediately before return in order to have each process block for 30 seconds before exiting. In another command window, continually execute ps -l . Section 3.4 explains why some of the processes may report a parent ID of 1 when sleep is omitted.

Figure 3.4. Tree of processes produced by the modification of Program 3.2 suggested in Exercise 3.10.

graphics/03fig04.gif

The fork function creates a new process by making a copy of the parent's image in memory. The child inherits parent attributes such as environment and privileges. The child also inherits some of the parent's resources such as open files and devices.

Not every parent attribute or resource is inherited by the child. For instance, the child has a new process ID and of course a different parent ID. The child's times for CPU usage are reset to 0. The child does not get locks that the parent holds. If the parent has set an alarm, the child is not notified when the parent's alarm expires . The child starts with no pending signals, even if the parent had signals pending at the time of the fork .

Although a child inherits its parent's process priority and scheduling attributes, it competes for processor time with other processes as a separate entity. A user running on a crowded time-sharing system can obtain a greater share of the CPU time by creating more processes. A system manager on a crowded system might restrict process creation to prevent a user from creating processes to get a bigger share of the resources.

Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net