0

The Linux Programming Interface says

22.3 Interruptible and Uninterruptible Process Sleep States

We need to add a proviso to our earlier statement that SIGKILL and SIGSTOP always act immediately on a process. At various times, the kernel may put a process to sleep, and two sleep states are distinguished:

  • TASK_INTERRUPTIBLE : The process is waiting for some event. For example, it is waiting for terminal input, for data to be written to a currently empty pipe, or for the value of a System V semaphore to be increased. A process may spend an arbitrary length of time in this state. If a signal is generated for a process in this state, then the operation is interrupted and the process is woken up by the delivery of a signal. When listed by ps(1), processes in the TASK_INTERRUPTIBLE state are marked by the letter S in the STAT (process state) field.

  • TASK_UNINTERRUPTIBLE : The process is waiting on certain special classes of event, such as the completion of a disk I/O. If a signal is generated for a process in this state, then the signal is not delivered until the process emerges from this state. Processes in the TASK_UNINTERRUPTIBLE state are listed by ps(1) with a D in the STAT field.

So it says that a process in TASK_INTERRUPTIBLE state is waiting for some event, and is woken up by the delivery of a signal generated for the process.

  • What is the relation between an event awaited by the process and a signal which wakes up the process? Must the signal signifies the occurrence of the event? Or can the signal be unrelated to the event?

  • Is a process in TASK_INTERRUPTIBLE state waken up only by the delivery of a signal?

For example, when a process calls wait() or waitpid() to wait for the termination of any child process,

  • does its state become TASK_INTERRUPTIBLE?
  • what can make the call to wait()/waitpid() return? Is it only the delivery of SIGCHLD? When a child terminates, does that necessarily generate SIGCHLD and deliver it to the process?

Thanks.

1 Answer 1

3

What is the relation between an event awaited by the process and a signal which wakes up the process? Must the signal signifies the occurrence of the event? Or can the signal be unrelated to the event

There need not be any relationship between the signal and the event for which the process is waiting. Using your example, a process can be in an interruptible sleep state waiting for a child to terminate, and it can get woken up to respond to any signal.

Consider the following example program:

#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

static void signal_handler(int signo)
{
#define msg "received signal\n"
    write(STDOUT_FILENO, msg, sizeof(msg));
#undef msg
}

int main(void)
{
    const struct sigaction act = {
        .sa_handler = signal_handler,
        // Not including SA_RESTART in sa_flags
    };

    if (sigaction(SIGHUP, &act, NULL) < 0) {
        perror("sigaction");
        return 1;
    }

    const int pid = fork();
    if (pid < 0) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // Child -- hang out a while
        sleep(60);
    } else {
        // parent
        if (syscall(SYS_wait4, pid,  NULL, 0, NULL) < 0) {
            perror("wait");
        }
    }

    return 0;
}

This program begins by setting up a signal handler to respond to the SIGHUP signal. I do not include the SA_RESTART flag, which causes some library functions to automatically restart interrupted system calls.

The program then uses fork() to create a new process; the child sleeps for a minute and the parent waits for the child to terminate.

Note here that I use syscall(SYS_wait4,... instead of waitpid(). Why? I want to eliminate any behaviors implemented in the waitpid() C library function -- I'm skipping the C library function and going directly to the kernel.

Now, I can run this program:

$ ./a.out

Then in another terminal, I see:

$ ps aux | grep a.out
username     7839  0.0  0.0   2128   692 pts/0    S+   22:28   0:00 ./a.out
username     7840  0.0  0.0   2128    76 pts/0    S+   22:28   0:00 ./a.out

Note the the S means interruptible sleep, so yes, a process blocked in a call to wait() is in the interruptible sleep state. (Note that a process blocked in a call to sleep() is also in an interruptible sleep state.)

Now, if I send that process the SIGHUP signal:

$ kill -HUP 7839

I see this output from the program:

received signal
wait: Interrupted system call

The received signal comes from the signal_handler() function, and the Interrupted system call comes from the call to perror() in response to the error returned by the call to waitpid() (really wait4()). The delivery of the HUP signal interrupted the sleep, executed the signal handler, then caused the call to wait to return EINTR (interrupted system call).

Is a process in TASK_INTERRUPTIBLE state waken up only by the delivery of a signal?

Delivery of a signal is one way, but is not the only way that a process in the TASK_INTERRUPTIBLE state can be woken up. Consider, for example, a process blocked waiting on input. When the input becomes available, the process gets woken up. In that case, there are no signals involved in the wake up.

does its [a process blocked in a call to wait()] state become TASK_INTERRUPTIBLE?

Yes, see above.

what can make the call to wait()/waitpid() return? Is it only the delivery of SIGCHLD? When a child terminates, does that necessarily generate SIGCHLD and deliver it to the process?

There are two things going on here: (1) behavior from the kernel's system call interface, and (2) behavior from the C library function. Normally, your program calls the C library function, and the C library function calls the system call. The library function can examine the return value of the system call. It can, for example, invoke the system call again if the previous call returned EINTR and you'd never know.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .