Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get inconsistent signals for orphaned process group?

Tags:

c

linux

system

I'm working through example in Advanced Programming in the UNIX Environment:

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

static void
sig_hup(int signo)
{
    printf("SIGHUP received, pid = %ld\n", (long)getpid());
}

static void
pr_ids(char *name)
{
    printf("%s: pid = %ld, ppid = %ld, pgrp = %ld, tpgrp = %ld\n",
        name, (long)getpid(), (long)getppid(), (long)getpgrp(),
        (long)tcgetpgrp(STDIN_FILENO));
    fflush(stdout);
}

int
main(void)
{
    char    c;
    pid_t   pid;

    pr_ids("parent");
    if ((pid = fork()) < 0) {
        exit(1);
    } else if (pid > 0) {   /* parent */
        sleep(5);       /* sleep to let child stop itself */
    } else {            /* child */
        pr_ids("child");
        signal(SIGHUP, sig_hup);    /* establish signal handler */
        printf("test\n");
        kill(getpid(), SIGTSTP);    /* stop ourself */
        pr_ids("child");    /* prints only if we're continued */
        if (read(STDIN_FILENO, &c, 1) != 1)
            printf("read error %d on controlling TTY\n", errno);
    }
    exit(0);
}

The idea is that:

  • We create child process
  • It has a handler for SIGHUP
  • We stop the child with SIGTSTP
  • Parent terminates and child is orphaned (member of orphaned process group)
  • POSIX.1 requires that stopped processed should be sent SIGHUP (which we handle) followed by SIGCONT
  • This should run the child's SIGHUP handler and continue process

When running this program from shell, I get flaky results. The example in book show this output:

$ ./a.out
parent: pid = 6099, ppid = 2837, pgrp = 6099, tpgrp = 6099
child: pid = 6100, ppid = 6099, pgrp = 6099, tpgrp = 6099
$ SIGHUP received, pid = 6100
child: pid = 6100, ppid = 1, pgrp = 6099, tpgrp = 2837
read error 5 on controlling TTY

However, my results are:

  • either no message that SIGHUP was received
$ ./a.out
parent: pid = 294729, ppid = 291330, pgrp = 294729, tpgrp = 294729
child: pid = 294730, ppid = 294729, pgrp = 294729, tpgrp = 294729
  • or SIGHUP was received but child didn't print it's pid output (so it didn't continue correctly)
$ ./a.out
parent: pid = 295709, ppid = 291330, pgrp = 295709, tpgrp = 295709
child: pid = 295710, ppid = 295709, pgrp = 295709, tpgrp = 295709
SIGHUP received, pid = 295710
  • or child did print it's output after SIGCONT, but I didn't get an error on tty.
$ ./a.out
parent: pid = 294722, ppid = 291330, pgrp = 294722, tpgrp = 294722
child: pid = 294723, ppid = 294722, pgrp = 294722, tpgrp = 294722
SIGHUP received, pid = 294723
child: pid = 294723, ppid = 2065, pgrp = 294722, tpgrp = 294722

I'd like to get an idea on why there is such variance between the results.

like image 434
3Rafal Avatar asked Nov 30 '25 10:11

3Rafal


1 Answers

I think your issue is happening due to a race condition a process terminating and the signal's delivery in the kernel. Whenever the process is terminated, all signal processing is stopped (except for SIGCONT and SIGKILL). So the SIGHUP is handled when a process is resumed, which can only happen after SIGCONT is received and handled. Take a look at this other stackoverflow answer.

I think you need to add the following call to waitpid() within else if (pid > 0) check

In your else block you should add a call to fflush(stdout) after signal(SIGHUP, sig_hup) and again after your call to read() , so something like this:

else {            /* child */
        pr_ids("child");
        signal(SIGHUP, sig_hup);    /* establish signal handler */
        fflush(stdout); /* flush everything before stopping */
        printf("test\n");
        kill(getpid(), SIGTSTP);    /* stop ourself */
        pr_ids("child");    /* prints only if we're continued */
        if (read(STDIN_FILENO, &c, 1) != 1)
            printf("read error %d on controlling TTY\n", errno);
       fflush(stdout);
    }

In your sig_hup() function, you should also add fflush(stdout) like this:

The calls to fflush() make sure the output is visible immediately (not buffered), thus hopefully leading to more consistent output.

like image 117
Pratik Mathur Avatar answered Dec 02 '25 23:12

Pratik Mathur



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!