Tips on Linux Process and Subprocess

Here are some tips on Linux process and subprocess, such as orphan process, zombie process, process exit and close on exec flag.

1 Orphan process

1.1 What is orphan process?

In Linux system, when parent process exits and child process is still running, the child process becomes an orphan process. Any orphaned process will be immediately adopted by the special init system process.

Here is a demo code to make a orphan process.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    switch(fork()) {
    case -1:
	printf("fail to create subprocess\n");
	break;
    case 0:
	printf("child process, pid = %d\n", getpid());
	sleep(30);
	break;
    default:
	printf("parent process, pid = %d\n", getpid());
	sleep(10);
	break;
    }
    return 0;
}

1.2 How to make child process exit after parent process exits?

  1. Child process can ask kernel to send a signal when its parent dies by prctl() system call. This works in Linux only.

Here is the demo code:

prctl(PR_SET_PDEATHSIG, SIGHUP);
  1. As child process id is changed to 1 (the init pid) after parent process exits, the child process could poll its parent process id by getppid() . When its parent process id becomes 1, the child process is an orphan process.

Function to get process identification.

// get process id
pid_t getpid();
// get parent process id
pid_t getppid();

2 Zombie process

2.1 What is a zombie process?

Zombie process is also called defunct process. When child process has finished the execution (via the exit system call) but still has entry in the process table: It is a process in the "Terminated state". The parent process should read (via the wait system call) its child's exit staus from process table, then the child process entry is removed from the process table and is said to be "reaped".

A child process always first becomes a zombie before being removed from the resource table. In most cases, under normal system operation zombies are immediately waited on by their parent and then reaped by the system – processes that stay zombies for a long time are generally an error and cause a resource leak.

Here is a demo code to make a zombie process.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    switch(fork()) {
    case -1:
	printf("fail to create subprocess\n");
	break;
    case 0:
	printf("child process, pid = %d\n", getpid());
	sleep(10);
	break;
    default:
	printf("parent process, pid = %d\n", getpid());
	sleep(30);
	break;
    }
    return 0;
}

Check out process tree using the following command.

ps -efx

2.2 How to kill a zombie process?

As a zombie process is already dead, so you can not kill it. The kill command has no effect on a zombie process.

To clean up a zombie, it must be waited on by its parent, so killing the parent should work to eliminate the zombie. (After the parent dies, the zombie will be inherited by init process (pid 1), which will wait on it and clear its entry in the process table).

The following command will kill all the parent processes that has a zombie child process. As this might kill processes that is running normally, it is not recommended. The better solution is to find out the very zombie process and stop its parent.

kill $(ps -A -ostat,ppid | awk '/[zZ]/ && !a[$2]++ {print $2}')

3 Process exit

3.1 exit function

exit() function performs some cleaning before termination of the program, such as the atexit() registered method and buffer flushing.

Here is the demo code:

#include <stdio.h>
#include <stdlib.h>

void cleanup() {
    printf("cleanup ...\n"); // this will be printed
}

int main(int argc, char* argv[]) {
    printf("process running ...\n");
    atexit(cleanup);
    printf("buffer information ..."); // this will be fulshed
    exit(0);
    return 0;
}

3.2 _exit or _Exit function

_exit() is equivalent to _Exit() . They all cause normal program termination to occur without completely cleaning the resources. The minor difference is _Exit is from C99 and _exit is from POSIX.

Use _exit (or _Exit ) in child process to avoid unintended calling atexit() handlers and flushing buffers from parent process.

Here is the demo code:

#include <stdio.h>
#include <stdlib.h>

void cleanup() {
    printf("cleanup ...\n"); // this will not be printed
}

int main(int argc, char* argv[]) {
    printf("process running ...\n");
    atexit(cleanup);
    printf("buffer information ...");  // this will not be fulshed
    _Exit(0);
    return 0;
}

4 Close on exec flag

The close-on-exec flag is set on file descriptor to indicate that the descriptor should be closed when an exec function is invoked. The flag is initially disabled on new descriptors, the descriptor will survive into the new program after exec , and resource is leaked. This would happen unintentionally when parent process forks a child and the child calls exec .

It is a good programming practice to use this flag in order to close the descriptor by default.

int fd1 = open(path, O_CLOEXEC | flags);
int fd2 = socket(DOMAIN, SOCK_CLOEXEC | type, PROTOCOL);
int fd3 = accept4(int sockfd, struct sockaddr *addr,
		  socklen_t *addrlen, SOCK_CLOEXEC | flags);
int fd4 = fopen(path, "re");

If you want to modify flags on an existing file descriptor, you should get the current flags with F_GETFD and modify the value. Don’t assume that the flags listed here are the only ones that are implemented; your program may be run years from now and more flags may exist then. For example, here is a function to set or clear the flag FD_CLOEXEC without altering any other flags:

/* Set the FD_CLOEXEC flag of desc if value is nonzero,
   or clear the flag if value is 0.
   Return 0 on success, or -1 on error with errno set. */
int set_cloexec_flag (int desc, int value) {
    int oldflags = fcntl (desc, F_GETFD, 0);
    /* If reading the flags failed, return error indication now. */
    if (oldflags < 0)
	return oldflags;
    /* Set just the flag we want to set. */
    if (value != 0)
	oldflags |= FD_CLOEXEC;
    else
	oldflags &= ~FD_CLOEXEC;
    /* Store modified flag word in the descriptor. */
    return fcntl (desc, F_SETFD, oldflags);
}

Comments

Comments powered by Disqus