idiot one
idiot one

Reputation: 389

Parent Process exit when using sigaction(SIGCHLD, &sigchld_action, NULL)

I am learning how sigaction SIGCHLD handles zombie process. My expectation is child process sleep for 5-30 seconds (based on random number), parent process sleep for 30 seconds.

But it's weird that parent process exit before child process exit. Why?

#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

sig_atomic_t child_exit_status;

void clean_up_child_process(int signal_number){
  int status;
  wait(&status);
  child_exit_status = status;
}

int main(){
  struct sigaction sigchld_action;
  int random_number;
  pid_t child_pid;

  memset(&sigchld_action, 0, sizeof(sigchld_action));
  sigchld_action.sa_handler = &clean_up_child_process;
  sigaction(SIGCHLD, &sigchld_action, NULL);

  for (int i = 0; i < 5; i++) {

    child_pid = fork();

    if (child_pid < 0) {
        perror("fork");
        exit(1);
    }

    if (child_pid == 0) {
        // Child process
        printf("Child %d created\n", i);

        srand(time(NULL)^getpid());
        random_number = (rand()%25)+5;;
        printf("random number is %d\n", random_number);

        sleep(random_number); // Simulate some work in the child
        exit(0);
    }
  }

  sleep(30);

  return 0;
}

Upvotes: 1

Views: 56

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 754860

Your problem is that the sleep() call returns when interrupted, telling you how many seconds of sleep time were left. If the result is 0, it wasn't interrupted. Otherwise, there was some time left. When the first child exits at, say, 10 seconds, the sleep() call returns 20, and you should loop:

unsigned naptime = 30;
unsigned napleft;
while ((napleft = sleep(naptime)) != 0)
{
    printf("%5d: woke up with %u seconds left - leave me in peace!\n",
           getpid(), napleft);
    naptime = napleft;
}
printf("%5d: I'm awake again\n", getpid());

int status;
int corpse;
while ((corpse = wait(&status)) >= 0)
{
    print("Parent %5d: child %5d exited with status 0x%.4X\n",
          getpid(), corpse, status);
}

Note that in multi-process code, it is usually helpful to identify which process is printing each message by including the PID. Do not try to optimize getpid() calls by saving the value — that can easily lead to misleading diagnostics, which are worse than no diagnostics. (In any case, getpid() is one of the fastest system calls; the overhead won't be measurable.)

In a comment, I mentioned the SA_RESTART flag for sigaction(). I don't think that'll help because sleep() is not restartable.

Think about how you can report the exit status of the child that exits, but be aware of How to avoid using printf() in a signal handler?. Consider whether to replace exit(0) in the child code with exit(i + 10) so that you can tell from the exit status which child it was that reported it.

Upvotes: 1

Related Questions