i FORK SYSTEM CALL AND REMOVE ZOMBIE PROCESS IN LINUX – All things in moderation

FORK SYSTEM CALL AND REMOVE ZOMBIE PROCESS IN LINUX

If you’re a Linux user, you may have seen zombie processes shambling around your processes list. You can’t kill a zombie process because it’s already dead – like an actual zombie.

1. THE CONCEPTS:

Parent process: In computing, a parent process is a process that has created one or more child process. In the Linxu kernel, there are two kinds of parent processes, namely real parent and parent. Parent is the process that receives the SIGCHLD signal on child’s termination, whereas real parent is the thread that actually created this child process in a multithreaded environment. For a normal process, both these two values are same, but for a POSIX thread which acts as a process, these two values may be different.
Child process: is a process created by another process (the parent process). This technique pertains to multitasking operating systems, and is sometimes called a subprocess or traditionally a subtask. A child process inherits most of its attributes, such as file descriptors, from its parent. In Unix, a child process is typically created as a copy of the parent, using the fork system call. The child process can then overlay itself with a different program (using exec) as required.
Zombie process: A process which has finished the execution but still has entry in the process table to report to its parent process is known as a zombie process. A child process always first becomes a zombie before being removed from the process table. The parent process reads the exit status of the child process which reaps off the child process entry from the process table.
Orphan process: are an opposite situation to zombie processes, referring to the case in which a parent process terminates before its child processes, which are said to become “orphaned”. Unlike the asynchronous child-to-parent notification that happens when a child process terminates (via the SIGCHLD signal), child processes are not notified immediately when their parent finishes. Instead, the system simply redefines the “parent PID” field in the child process’s data to be the process that is the “ancestor” of every other process in the system, whose PID generally has the value of 1 (one), and whose name is traditionally “init”. Thus, it was said that init “adopts” every orphan process on the system.

2. FORK SYSTEM CALL:

Fork system call use for creates a new process, which runs concurrently with process (which process called system call fork) and this process is called parent process. After a new child process created, both processes will execute the next instruction following the fork() system call. It takes no parameters and returns an integer value:
* Negative Value: creation of a child process was unsuccessful.
* Zero: Returned to the newly created child process.
* Positive value: Returned to parent or caller. The value contains process ID of newly created child process.
For a process to start the execution of a different program, it first forks to create a copy of itself. Then, the copy, called the “child process”, calls the exec system call to overlay itself with the other program: it ceases execution of its former program in favor of the other. The fork operation creates a separate address space for the child. The child process has an exact copy of all the memory segments of the parent process. This optimization is important in the common case where fork is used in conjunction with exec to execute a new program: typically, the child process performs only a small set of actions before it ceases execution of its program in favour of the program to be started, and it requires very few, if any, of its parent’s data structures. When a process calls fork, it is deemed the parent process and the newly created process is its child. After the fork, both processes not only run the same program, but they resume execution as though both had called the system call. They can then inspect the call’s return value to determine their status, child or parent, and act accordingly.
C script use fork():

#include 
#include 
int main(){
    pid_t pid;
    pid = fork();
    if (pid == 0){
        // do something in child process
        exit(0);
    }else{
        // do something in parent process
    }
return 0;
}

In the above code, if the script doesn’t have “exit(0)” line then child process will execute the code lines after “if” command.
Examples of using multiple fork():

#include 
#include 
int main(){
    fork(); // F1
    fork(); // F2
    fork(); // F3
    // do something
    return 0;
}

After running this program, we have 8 processes. Total number of processes = 2^n where n is number of fork system calls. So here n = 3. The image below illustrates the cloning process of fork() in this program.

3. Remove the zombie process:

3.1. Using wait() system call:

When the parent process calls wait(), after the creation of child, it indicates that, it will wait for the child to complete and it will reap the exit status of the child. The parent process is suspended(waits in a waiting queue) until the child is terminated. It must be understood that during this period, the parent process does nothing just waits.

#include 
#include 
#include 
#include 
int main(){
    pid_t = fork();
    if (pid == 0){
        printf("I am Child!\n");
                exit(0);
    }else{
        wait(NULL);
        printf("I am Parent!\n);
                while(1);
    }
}

3.2. Using double fork:

The solution is to use a double fork(). It means, your main process A will create new process B, which will start process C. Process B ends immediately, process A can receive its return code right after starting it. Process C will do whatever is needed (printing labels in your case) and when it ends, it will sent a return code. Its parent process B is already dead, so system will take care of it (process with pid 1 in the past, not sure how that works now). And it will not become a zombie.

...
pid_t pid1, pid2;
int status;

pid1 = fork(); // fork1
if( pid1 == 0){

    pid2 = fork(); // fork2
    if(pid2 == 0){
        // do something in process C
        exit(0);
    }else{
        exit(0);
    }
        
}else{
    wait(&status);
}
...

3.3. Using command:

ps axu | awk '"[Zz] ~ $8 { system(sprintf("kill -HUP %d", $2)); }'

This will work in most cases
* Check if the zombie process still exists; if so, run the same command but change -HUP to -TERM.
* If the zombie is still there, use -9 instead of -HUP or -TERM
* -9 will terminate it for sure but it’s considered very bad practice
* FYI – The above is way too much work. Use killall e.g. killall firefox

Conclusion:

fork() creates a new process by duplicating the calling process. Zombies process are basically the leftover bits of dead processes that haven’t been cleaned up properly. A program that creates zombie processes isn’t programmed properly – programs aren’t supposed to let zombie processes stick around.

Leave a Reply