Tutorial 8
In this tutorial, we are going to familiarize ourselves with process forking. We will have some hands-on experience with ready to use C programs and shell scripts. Use Vi, Emacs or Pico for editing the files. Login to one of the following pcs:
cs1.utdallas.edu
cs2.utdallas.edu
Use PuTTY or Xmanager for logging on as you learnt in Tutorial 1.
Fork in shell script
In our first example, we will run a shell script which will fork a child process. The child process will be another script instead of the same script of calling process. First one is the script which will be parent process. Open your favorite Unix editor (Vi / Emacs / Pico) and type the script:
| #!/bin/bash #parent.sh # Fork a child process using the child.sh file # The child.sh file must reside in the same directory # Round parentheses mean creat a new process and run the commands # within the parentheses in the child # for example - run one command in the child # (command1) # for example - run three commands in the child # (command1; command2; command3)
echo Starting the parent process script ( child.sh; ) & |
Type the script in the editor and save the file as "parent.sh". The script for the child process is as follows:
| #!/bin/bash #child.sh echo Starting the child process script echo Entering the child loop
a=0 |
Type the script in the editor and save the file as "child.sh". Run parent.sh after assigning execute permission to both of them:
{cs1:~} chmod +x parent.sh
{cs1:~} chmod +x child.sh
{cs1:~} ./parent.sh
Starting the parent process script
About to create a child process
parent: 0
Starting the child process script
Entering the child loop
child: 0
parent: 1
child: 1
parent: 2
child: 2
parent: 3
child: 3
parent: 4
child: 4
parent: 5
child: 5
parent: 6
child: 6
parent: 7
child: 7
parent: 8
child: 8
parent: 9
child: 9
Exiting the parent loop
{cs1:~} Exiting the child loop
The output will be similar to above.
Fork in C program
In this example, we will write a simple C program which will fork a child process. The "fork()" function will be used to do this. fork() returns 0 to the child process and the child pid to the parent process. Our parent process and child process will count up to ten. For process switching by operating system, we will observe in the output that the processes are not printing serially, rather they are printing in turn.. Type in the following C program in your editor:
| // forking.c #include <stdio.h> |
Everything on the right of a "//" is a single line comment and everything in between a pair of "/*" and "*/" are multi-line comments. Comments are not part of the program. Save the file as "forking.c" and then from the terminal compile and build it with the following command:
{cs1:~} gcc -o forking forking.c
{cs1:~} ls -l
-rwx--x--x 1 mfh062000 sn 7080 Oct 15 13:58 forking
-rw-r----- 1 mfh062000 sn 937 Oct 15 13:51 forking.c
This will create an executable named "forking" with execute permissions. This can be seen from the output of "ls -l". Run the program::
{cs1:~} ./forking
parent: 0
child: 0
parent: 1
child: 1
parent: 2
child: 2
parent: 3
child: 3
parent: 4
child: 4
child: 5
parent: 5
parent: 6
child: 6
child: 7
parent: 7
child: 8
parent: 8
parent: 9
child: 9
Difference between bash sleep and sleep in C program
There is a major difference between how bash sleep command and sleep function of C program works. The sleep function of C programs is a simple function call. When called, it gives up the CPU to the operating system so that it can schedule other processes which need CPU. Then after the specified amount of seconds, the process which called sleep starts working again. That is what is evident from the output of the C program. After each sleep call, the calling process is yielding CPU to the operating system and the operating system is assigning the CPU to another process. In our program, when the parent program calls sleep function, child gets it turn to work. After child calls sleep printing one line, parent gets back the CPU. This is why, we see that they print the output in turn.
The sleep command bash works differently. When this command is invoked, it creates a child process of itself. It runs for the specified amount of seconds and then exits back to the calling process. Though the output is similar to the C prgram, the way sleep command works is different as it gets a process of itself. We can verify it by the ps command in another terminal. Open another terminal, run the parent.sh in the first terminal and simultaneously type the following command in the new terminal:
{cs1:~} ps -ef | grep mfh06200 | grep sleep
// Replace mfh06200 with your netid, you may need to drop the last digit of your netid
mfh06200 11608 11585 0 16:27:57 pts/24 0:00 sleep 1
mfh06200 11611 11465 0 16:27:58 pts/23 0:00 grep sleep
Here, you can see that the sleep command created a process of its own. If you could not see it, increase the time interval of sleep to something like 10 or 20 seconds. Each sleep command will create different processes. To verify this, run the parent.sh script again and issue the ps command twice with quick succession:
{cs1:~} ps -ef | grep mfh06200 | grep sleep
mfh06200 19720 19717 0 10:57:04 pts/25 0:00 sleep 1
mfh06200 19723 19491 0 10:57:05 pts/3 0:00 grep sleep
{cs1:~} ps -ef | grep mfh06200 | grep sleep
mfh06200 19726 19717 0 10:57:07 pts/25 0:00 sleep 1
mfh06200 19729 19491 0 10:57:08 pts/3 0:00 grep sleep
Forking multiple child processes in shell script
In our first script, we created only one child process. We can create multiple child process, as many as we want. We will learn this by creating two child processes with the following script. For the child process, we will use the child.sh script which we created before. The parent process script is as follows:
| #!/bin/bash # parent2.sh # Create two child processes using the child.sh file # The child.sh file must reside in the same directory
echo Starting the parent process script |
Type the script in editor and save as parent2.sh. Run the script after assigning execute permission:
{cs1:~} chmod +x parent2.sh
{cs1:~} ./parent2.sh
Starting the parent process script
Creating first child process
Creating second child process
Entering the parent loop
parent: 0
Starting the child process script
Entering the child loop
child: 0
Starting the child process script
Entering the child loop
child: 0
child: 1
child: 1
parent: 1
child: 2
child: 2
parent: 2
child: 3
child: 3
parent: 3
child: 4
child: 4
parent: 4
child: 5
child: 5
parent: 5
child: 6
child: 6
parent: 6
child: 7
child: 7
parent: 7
child: 8
child: 8
parent: 8
child: 9
child: 9
parent: 9
Exiting the child loop
Exiting the child loop
Exiting the parent loop
The output will be similar to above. As before, we can verify by ps command that the sleep command is creating a process of its own.
Forking multiple child processes in C program
In C program, the task of creating multiple child processes is simple. After
a pid check for parent process, we simply create another process. Here is an
example which creates two child processes:
| // forking2.c #include <stdio.h> int main(void) { // Variable to hold the process id int pid; // Fork the process and get the process id in "pid" pid = fork(); // Check the value of pid if(pid == 0) { /* Child process: * When fork() returns 0, we are in * the child process. * Here we count up to ten, one each second. */ int j; for(j=0; j < 10; j++) { printf("child: %d\n", j); // Force the process to sleep for one second sleep(1); } _exit(0); /* Note that we do not use exit() because it is not async-signal safe */ } else if(pid > 0) { /* Parent process: * Otherwise, we are in the parent process. * Again we create a child process. */ // Fork the process and get the process id in "pid" pid = fork(); // Check the value of pid if(pid == 0) { /* Child process: * When fork() returns 0, we are in * the child process. * Here we count up to ten, one each second. */ int j; for(j=0; j < 10; j++) { printf("child: %d\n", j); // Force the process to sleep for one second sleep(1); } _exit(0); /* Note that we do not use exit() because it is not async-signal safe */ } else if(pid > 0) { /* Parent process: * Otherwise, we are in the parent process. * Again we count up to ten. */ int i; for(i=0; i < 10; i++) { printf("parent: %d\n", i); // Force the process to sleep for one second sleep(1); } } } else { /* Error handling. */ fprintf(stderr, "couldn't fork"); exit(1); } } |
Type the code in editor and save as "forking2.c". Run forking2 after compiling and building the programs:
{cs1:~} gcc -o forking2 forking2.c
{cs1:~} ./forking2
child: 0
child: 0
parent: 0
child: 1
child: 1
parent: 1
parent: 2
child: 2
child: 2
child: 3
child: 3
parent: 3
parent: 4
child: 4
child: 4
child: 5
child: 5
parent: 5
parent: 6
child: 6
child: 6
child: 7
child: 7
parent: 7
parent: 8
child: 8
child: 8
child: 9
parent: 9
child: 9
The output will be similar to above. It may not be the same because it depends on process scheduling.
Threads in Java
Java supports sub-processes in the form of threads. Though threads and processes are not similar, they can achieve similar concurrency. Basically, threads are different execution paths running in the same process. They do not create different address space, so all of them share the same memory of the process. As threads are not processes, they don't have any process id. So, we cannot identify existence of any threads by ps command. Almost same level of parallelism as processes can be achieved by threads. To learn about how Java threads work and how to code them, you can visit the link to a brief introductory tutorial below:
http://www.javaworld.com/javaworld/jw-04-1996/jw-04-threads.html