Home Linux Admin Introduction Directory Tree Disks/File Systems Memory Mgmt Monitoring Startup/ShutDown Logging in/out User Accounts Backups Processes Cron Packages Books

Process Part 2


Contents

This chapter discusses processes in a Linux Systems.

Process Creation

A process creates another process except the very first process that is created by the kernel when the system starts and has a process id of 1. There are 2 system calls that can be used to mangage processes: fork() and exec() . We have already seen how "fork()" is used in "f1.c". It duplicates itself and creates a new process whose execution starts from the line "fork()". In the parent process the id returned by "fork()" is 0 for the child and non-zero for the parent. The "exec()" calls another program that is loaded into the current program ( replacing the code in the current program ) and the other program is executed.To explain how "exec" works copy the following 2 files in the prcess folder on the hills server.

File: exec1.c
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>


int main()
{
   printf("exec1 is executing.\n" ) ;


    return 0;
}

File: exec2.c
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>


int main()
{
   printf("exec2 is starting to execute.\n" ) ;
   char  *args[] = { "./exec1" , NULL } ;
   execv( "./exec1" , args  ) ;
   printf("exec2 is ending.\n" ) ;

    return 0;
}



[amittal@hills process]$ gcc exec1.c -o exec1
[amittal@hills process]$ gcc exec2.c -o exec2
[amittal@hills process]$ ./exec2
exec2 is starting to execute.
exec1 is executing.
[amittal@hills process]$




We compile the 2 c programs to the executables "exec1" and "exec2" . We run the program "exec2" and notice that it prints the first line starting with "exec2 is starting" . It then proceeds to execute the "exec" call. There are several exec calls in the exec family and we are using the "execv" one. Now the current program "exec2" is replaced with "exec1" and we see the line "exec1 is executing". Notice that the line "exec2 is ending" is never executed. This is because at this point in time the "exec1" has completely replaced the "exec2" process. Normally this is not what we want. We want the "exec2" to keep going. The normal technique that is used is to create a child process and have the "exec" call in the child process. The "exec" program will replace the child process and the parent process keeps going.

File: exec3.c
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>


int main()
{
   printf("exec3 is starting to execute.\n" ) ;

    pid_t pid;
    switch(  pid = fork()  )
    {
        case -1:
            printf("fork failed");
            break;
        case 0:  //Child
            printf("\n In the child process. About to run exec1");
            fflush(stdout);
            char  *args[] = { "./exec1" , NULL } ;
            execv( "./exec1" , args  ) ;

            break;
        default:    //parent
             printf("exec3 is ending.\n" ) ;
             fflush(stdout);
    }


    return 0;
}


[amittal@hills process]$ gcc exec3.c -o exec3
[amittal@hills process]$ ./exec3
exec3 is starting to execute.
exec3 is ending.

 In the child process. About to run exec1
[amittal@hills process]$ exec1 is executing.

[amittal@hills process]$


In the above the "fork()" call creates a child process and returns an integer that is assigned to "pid". The switch statement looks at the id and then executes the correct "case" statement. For the parent the "default" case is executed and for the child the "case 0" is executed. We now have 2 processes. The child process calls "exec1" which replaces the child process. The parent process keeps going.

The concept of "fork" and "exec" is important in understanding how the shell works. Let's say we run the "ls" command.


[amittal@hills process]$ ls
a.out  exec1  exec1.c  exec2  exec2.c  exec3  exec3.c  f1.c  f2  f2.c  f3.c
[amittal@hills process]$



When we run the "ps" command the "ps" will list itself in the output and once the output is printed out; the "ps" process will exit. If we run another "ps" command; it's process id will be different because a new process will be created by "fork" to execute the command.

[amittal@hills ~]$
[amittal@hills ~]$ ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
amittal  3052861 3052860  0 Apr04 pts/92   00:00:00 -bash
amittal  3116939 3052861  0 09:25 pts/92   00:00:00 ps -f

The id of "ps" is "3116939" .

[amittal@hills ~]$ ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
amittal  3052861 3052860  0 Apr04 pts/92   00:00:00 -bash
amittal  3116940 3052861  0 09:25 pts/92   00:00:00 ps -f
[amittal@hills ~]$

The id of "ps" is "3116940" .


Zombie Process

Linux/Unix has a history of colorful terms in it's lingo.No we are not discussing horror movies here but the analogy of a zombie is appropriate. Let's say we have a parent process that creates a child process. The parent process should have a "wait" call which waits for the child process to complete. The "wait" call will do some cleaning up.The best way to understand this is through an example.

File: zombie1.c
// A C program to demonstrate Zombie Process.
// Child becomes Zombie as parent is sleeping
// when child process exits.
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
	// Fork returns process id
	// in parent process
	pid_t child_pid = fork();

	// Parent process
	if (child_pid > 0)
          {
              printf("\n In the parent process. Waiting for 60 seconds.\n ");
              fflush(stdout);
              sleep(60);
          }
	// Child process
	else
          {
              printf("\n In the child process. Waiting for 30 seconds. \n");
              fflush(stdout);
              sleep(30);
              printf("\n Child process exiting. Becomes a zombie for the next 30 seconds. \n");
              fflush(stdout);
          }

	return 0;
}


Compile the above program using the below command.

[amittal@hills process]$ gcc zombie1.c -o zombie1
[amittal@hills process]$ ./zombie1


 In the parent process. Waiting for 60 seconds.
 In the child process. Waiting for 30 seconds.

 Child process exiting. Becomes a zombie for the next 30 seconds.
[amittal@hills process]$

Open another putty/terminal session. We attach another letter called "s" to our
"ps" command. This means state of the process.
The states can be.
       D    uninterruptible sleep (usually IO)
       R    running or runnable (on run queue)
       S    interruptible sleep (waiting for an event to complete)
       T    stopped, either by a job control signal or because it is being traced.
       W    paging (not valid since the 2.6.xx kernel)
       X    dead (should never be seen)
       Z    defunct ("zombie") process, terminated but not reaped by its parent.

In the beginning we have 2 processes are in the sleeping state. Which one is
the parent ? We see the second one has a parent id of "3368016" so the
second one is the child and the first is the parent.

The "S' indicates a sleeping state.
[amittal@hills process]$ ps  xo pid,ppid,pgid,sid,comm,s | grep "zomb"
3368016 3367076 3368016 3367076 zombie1         S
3368017 3368016 3368016 3367076 zombie1         S

Wait for the line in the first terminal to show up:
"Child process exiting. ". Run the command again.
Now the child is in a zombie state. It has exited but it has an entry in the kernel.
The state is now "Z" and the word "defunct" also means it is a
zombie process.

[amittal@hills process]$ ps  xo pid,ppid,pgid,sid,comm,s | grep "zomb"
3368016 3367076 3368016 3367076 zombie1         S
3368017 3368016 3368016 3367076 zombi  Z
[amittal@hills process]$ ps  xo pid,ppid,pgid,sid,comm,s | grep "zomb"

After 60 seconds the parent process exits. Now the zombie processes will
also end.

[amittal@hills process]$ ps  xo pid,ppid,pgid,sid,comm,s | grep "zomb"
[amittal@hills process]$


In the above program we don't have a "wait" call in the parent process. First a child process is created with the "fork" call and we have 2 processes in the system. They are normal processes right now. After 30 seconds the child process exits and becomes a zombie process. We cannot use the "kill" command to kill the zombie process as it not currently running. We need to wait for the parent process to die so that the zombie processes can be cleaned up. A better way is for the parent to use the "wait" call.

File: zombie2.c
// A C program to demonstrate Zombie Process.
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
int main()
{
	// Fork returns process id
	// in parent process
	pid_t child_pid = fork();

	// Parent process
	if (child_pid > 0)
          {
              printf("\n In the parent process. Waiting for child process to complete.\n ");
              wait((int *)0);
              fflush(stdout);
              printf("\n In the parent process. Waiting for 60 seconds.\n ");
              sleep(60);
          }
	// Child process
	else
          {
              printf("\n In the child process. Waiting for 30 seconds. \n");
              fflush(stdout);
              sleep(30);
              printf("\n Child process exiting.\n");
              fflush(stdout);
          }

	return 0;
}


[amittal@hills process]$ gcc zombie2.c -o zombie2
[amittal@hills process]$ ./zombie2

 In the parent process. Waiting for child process to complete.

 In the child process. Waiting for 30 seconds.

 Child process exiting.

 In the parent process. Waiting for 60 seconds.

 [amittal@hills process]$



We run the "ps" command at the beginning. The child process is
waiting for 30 seconds and both the processes are in the "Sleep" state.


[amittal@hills process]$ ps  xo pid,ppid,pgid,sid,comm,s | grep "zomb"
3375393 3367076 3375393 3367076 zombie2         S
3375394 3375393 3375393 3367076 zombie2         S

After 30 seconds the "ps" shows that the child process exited gracefully
and we only have a parent process.

[amittal@hills process]$ ps  xo pid,ppid,pgid,sid,comm,s | grep "zomb"
3375393 3367076 3375393 3367076 zombie2         S



Transfer Files

For the assignment on this chapter we will need to transfer a file to the hills server. On Windows we can do that using "WinScp". This tool is explained in the Introduction/Setup section at: Setup We can also use the Cygwin simulator do transfer a file using commands. The same procedure can be used on a Mac( Terminal app ) or a local Linux system. The below has been done on a Cygwin system.

Use the command "sftp" and then username@ hills.ccsf.edu
It is assumed that there is a folder called "transfer" where we want to transfer
the file from our local system.

$ sftp amittal@hills.ccsf.edu
amittal@hills.ccsf.edu's password:
Connected to hills.ccsf.edu.

This will list the files on the remote server. If we want the same
command to work for our local machine then we need to prepend the letter "l" .
So if we want to list the files on our local then the command is "lls".
sftp> ls

Change the directory on the remote hills server.

sftp> cd transfer
sftp> pwd
Remote working directory: /users/amittal/transfer
sftp> lpwd
Local working directory: c:\cygwin64\home\deller

Change the local folder.
sftp> lcd C:\WebSite\Learn\2\linuxa\process


Transfer the file to the remove hills server.
sftp> put zombie2.c
Uploading zombie2.c to /users/amittal/transfer/zombie2.c
zombie2.c                                                                    100%  840    74.3KB/s   00:00
sftp> exit




Sample video that shows how to transfer files to the remote hills server.