Showing posts with label Linux. Show all posts
Showing posts with label Linux. Show all posts

Sunday, September 2, 2007

Zombie Processes

0 comments;Click here for request info on this topic

Using fork to create processes can be very useful, but you must keep track of child processes.

There is some Information related to termination of child process—such as whether it exited normally and, if so, what its exit status.

When a child process terminates, an association with its parent survives until the parent in turn either terminates normally or calls wait.

If a child process terminates while its parent is calling a wait function, the child process vanishes and its termination status is passes to it parent via the wait call.

But what happens when a child process terminates and the parent is not calling wait? Does it simply vanish? No, because then information about its termination—such as whether it exited normally and, if so, what its exit status is—would be lost. Instead, when a child process terminates, is becomes a zombie process.

The child process entry in the process table is therefore not freed up immediately. Although no longer active, the child process is still in the system because its exit code needs to be stored

in case the parent subsequently calls wait. It becomes what is known as defunct, or a zombie process. If the child prints fewer messages than the parent, it will finish first and will exist as a zombie until the parent has finished.

If the parent then terminates abnormally, the child process automatically gets the process with PID 1 (init) as parent. The child process is now a zombie that is no longer running but has been inherited by init because of the abnormal termination of the parent process. When a program exits, its children are inherited by a special process, the init program, which always runs with process ID of 1 (it’s the first process started when Linux boots). The init process automatically cleans up any zombie child processes that it inherits. The zombie will remain in the process table until collected by the init process. The bigger the table, the slower this procedure. You need to avoid zombie processes, as they consume resources until init cleans them up.

A zombie process is a process that has terminated but has not been cleaned up yet. It is the responsibility of the parent process to clean up its zombie children. The wait functions do this, too, so it’s not necessary to track whether your child process is still executing before waiting for it. Suppose, for instance, that a program forks a child process, performs some other computations, and then calls wait. If the child process has not terminated at that point, the parent process will block in the wait call until the child process finishes. If the child process finishes before the parent process calls wait,the child process becomes a zombie.When the parent process calls wait, the zombie child’s termination status is extracted, the child process is deleted, and the wait call returns immediately.

(zombie.c) Making a Zombie Process

#include

#include

int main ()

{

pid_t child_pid;

/* Create a child process. */

child_pid = fork ();

if (child_pid > 0)

{

/* This is the parent process. Sleep for a minute. */

sleep (60);

}

else { /* This is the child process. Exit immediately. */

exit (0);

}

return 0;

}

Cleaning Up Children Asynchronously

If you’re using a child process simply to exec another program, it’s fine to call wait immediately in the parent process, which will block until the child process completes. But often, you’ll want the parent process to continue running, as one or more children execute synchronously. How can you be sure that you clean up child processes that have completed so that you don’t leave zombie processes, which consume system resources, lying around?

One approach would be for the parent process to call wait3 or wait4 periodically, to clean up zombie children. Calling wait for this purpose doesn’t work well because, if no children have terminated, the call will block until one does. However, wait3 and wait4 take an additional flag parameter, to which you can pass the flag value WNOHANG. With this flag, the function runs in nonblocking mode—it will clean up a terminated child process if there is one, or simply return if there isn’t.The return value of the call is the process ID of the terminated child in the former case, or zero in the latter case.

A more elegant solution is to notify the parent process when a child terminates. There are several ways to do this using the methods discussed in “Interprocess Communication,” but fortunately Linux does this for you, using signals. When a child process terminates, Linux sends the parent process the SIGCHLD signal. The default disposition of this signal is to do nothing, which is why you might not have noticed it before. Thus, an easy way to clean up child processes is by handling SIGCHLD. Of course, when cleaning up the child process, it’s important to store its termination status if this information is needed, because once the process is cleaned up using wait, that information is no longer available. Listing 3.7 is what it looks like for a program to use a SIGCHLD handler to clean up its child processes.

Listing 3.7 (sigchld.c) Cleaning Up Children by Handling SIGCHLD

#include

#include

#include

#include

sig_atomic_t child_exit_status;

void clean_up_child_process (int signal_number)

{

/* Clean up the child process. */

int status;

wait (&status);

/* Store its exit status in a global variable. */

child_exit_status = status;

}

int main ()

{

/* Handle SIGCHLD by calling clean_up_child_process. */

struct sigaction sigchld_action;

memset (&sigchld_action, 0, sizeof (sigchld_action));

sigchld_action.sa_handler = &clean_up_child_process;

sigaction (SIGCHLD, &sigchld_action, NULL);

/* Now do things, including forking a child process. */

/* ... */

return 0;

}

Note how the signal handler stores the child process’s exit status in a global variable, from which the main program can access it. Because the variable is assigned in a signal handler, its type is sig_atomic_t.

SIGCHLD can be useful for managing child processes. It’s ignored by default. The remaining signals cause the process receiving them to stop, except for SIGCONT, which causes the process to resume. They are used by shell programs for job control and are rarely used by user programs.

Read full story

Tuesday, August 28, 2007

Linux: Process, Signal & System Overview

0 comments;Click here for request info on this topic

System Cause a program to run from inside another program and thereby create a

New process

Fork create a new process

Duplicating a process image

This system call duplicates the current process, creating a new entry in the process table with many of the same attributes as the current process. The new process is almost identical to the original, executing the same code but with its own data space, environment, and file descriptors. Combined with the exec functions, fork is all we need to create new processes.

Exec family Run a given program in the process

Replacing a process image

Wait, waitpid

Wait3, wait4 waiting for a process

Arrange for the parent process to wait until the child finishes before continuing by calling wait.

The wait system call causes a parent process to pause until one of its child processes is stopped.


Killing a Process

You can kill a running process with the kill command. Simply specify on the command line the process ID of the process to be killed. The kill command works by sending the process a SIGTERM, or termination, signal.1 This causes the process to terminate, unless the executing program explicitly handles or masks the SIGTERM signal. Signals are described in Section 3.3,“Signals.”

1.You can also use the kill command to send other signals to a process

What’s the difference? The SIGTERM signal asks a process to terminate; the process may ignore the request by masking or ignoring the signal.The SIGKILL signal always kills the process immediately because the process may not mask or ignore SIGKILL.

If a process receives one of these signals without first arranging to catch it, the process will be terminated immediately. Usually, a core dump file is created. This file, called core and placed in the current directory, is an image of the process that can be useful in debugging.

Signal

(signals are generated by software. interrupts are generated by interrupts so signals are software interrupts )

A signal is a special message sent to a process. Signals are asynchronous; when a process receives a signal, it processes the signal immediately, without finishing the current function or even the current line of code.There are several dozen different signals, each with a different meaning.

Signals are software generated interrupts that are sent to a process when a event happens. Signals can be synchronously generated by an error in an application, such as SIGFPE and SIGSEGV, but most signals are asynchronous.

Because signals are asynchronous, the main program may be in a very fragile state when a signal is processed and thus while a signal handler function executes. Therefore, you should avoid performing any I/O operations or calling most library and system functions from signal handlers. A signal handler should perform the minimum work necessary to respond to the signal, and then return control to the main program (or terminate the program). In most cases, this consists simply of recording the fact that a signal occurred.The main program then checks periodically whether a signal has occurred and reacts accordingly. It is possible for a signal handler to be interrupted by the delivery of another signal. While this may sound like a rare occurrence, if it does occur, it will be very difficult to diagnose and debug the problem. (This is an example of a race condition, discussed in Chapter 4,“Threads,” Section 4.4,“Synchronization and Critical Sections.”) Therefore, you should be very careful about what your program does in a signal handler.

Even assigning a value to a global variable can be dangerous because the assignment may actually be carried out in two or more machine instructions, and a second signal may occur between them, leaving the variable in a corrupted state. If you use a global variable to flag a signal from a signal-handler function, it should be of the special type sig_atomic_t. Linux guarantees that assignments to variables of this type are performed in a single instruction and therefore cannot be interrupted midway. In Linux, sig_atomic_t is an ordinary int; in fact, assignments to integer types the size of int or smaller, or to pointers, are atomic. If you want to write a program that’s portable to any standard UNIX system, though, use sig_atomic_t for these global variables.

Signals are not presented to the process immediately they are generated., they must wait until the process is running again. Every time a process exits from a system call its signal and blocked fields are checked and, if there are any unblocked signals, they can now be delivered. This might seem a very unreliable method but every process in the system is making system calls, for example to write a character to the terminal, all of the time. Processes can elect to wait for signals if they wish, they are suspended in state Interruptible until a signal is presented. The Linux signal processing code looks at the sigaction structure for each of the current unblocked signals.

system

We can cause a program to run from inside another program and thereby create a new process by using the system library function.

#include

int system (const char *string);

The system function runs the command passed to it as a string and waits for it to complete. The command is executed as if the command

$ sh -c string

has been given to a shell. system returns 127 if a shell can’t be started to run the command and -1 if another error occurs. Otherwise, system returns the exit code of the command.

We can use system to write a program to run ps for us. While this is not tremendously useful in and of itself, we’ll see how to develop this technique in later examples. We also don’t check that the system call actually worked for the sake of simplicity in the example.

#include

#include

int main()

{

printf(“Running ps with system\n”);

system(“ps -ax”);

printf(“Done.\n”);

exit(0);

}

When we compile and run this program, system1.c, we get the following:

$ ./system1

Running ps with system

PID TTY STAT TIME COMMAND

1 ? S 0:05 init

2 ? SW 0:00 [keventd]

...

1262 pts/1 S 0:00 /bin/bash

1273 pts/2 S 0:00 su -

1274 pts/2 S 0:00 -bash

1463 pts/1 S 0:00 oclock -transparent -geometry 135x135-10+40

1465 pts/1 S 0:01 emacs Makefile

1480 pts/1 S 0:00 ./system1

1481 pts/1 R 0:00 ps -ax

Done.

Because the system function uses a shell to start the desired program, we could put it in the background by changing the function call in system1.c to the following:

system(“ps -ax &”);

When we compile and run this version of the program, we get

$ ./system2

Running ps with system

PID TTY STAT TIME COMMAND

1 ? S 0:05 init

2 ? SW 0:00 [keventd]

...

Done.

$ 1246 ? S 0:00 kdeinit: klipper -icon klipper -miniicon klipper

1274 pts/2 S 0:00 -bash

1463 pts/1 S 0:00 oclock -transparent -geometry 135x135-10+40

1465 pts/1 S 0:01 emacs Makefile

1484 pts/1 R 0:00 ps -ax

How It Works

In the first example, the program calls system with the string “ps -ax”, which executes the ps program. Our program returns from the call to system when the ps command has finished. The system function can be quite useful but is also limited. Because our program has to wait until the process started by the call to system finishes, we can’t get on with other tasks.

In the second example, the call to system returns as soon as the shell command finishes. Because it’s a request to run a program in the background, the shell returns as soon as the ps program is started, just as would happen if we had typed

$ ps -ax &

at a shell prompt. The system1 program then prints Done. and exits before the ps command has had a chance to finish all of its output. The ps output continues to produce output after system1 exits. This kind of process behavior can be quite confusing for users. To make good use of processes, we need finer control over their actions. Let’s look at a lower-level interface to process creation, exec.

The system function can be quite useful but is also limited. Because program has to wait until the process started by the call to system finishes, we can’t get on with other tasks.

In general, using system is a far from ideal way to start other processes, because it invokes the desired program using a shell. This is both inefficient, because a shell is started before the program is started, and also quite dependent on the installation for the shell and environment that are used.

Read full story