0

I am trying to play "ping-pong" with 2 processes and 2 user defined signal handlers.

The problem is that for some reason, after I raise(SIGSTOP) the child process in order to let the parent process to start, I can't continue the run of the child process, even if I send a kill(getppid(), SIGUSR1 which, for my opinion, should re-run the process again.

What is the problem here?

void handle_siguser1(int signal_id);

void handle_siguser2(int signal_id);


enum {CHILD = 0, PARENT = 1};

/************************* Functions  Implementations *************************/
int main()
{
    /*  create a child process  */
    pid_t pid = fork();
    
    while (1)
    {
        /*  returned to the newly created child process. */
        if (CHILD == pid)
        {
            printf("Child registers its signal handler\n");
            signal(SIGUSR1, handle_siguser1);
            
            printf("Child stops to return the control to the parent\n");
            raise(SIGSTOP);
        }
        
        /* returned to parent */
        else
        {
            printf("Parent process started running\n");
            
            printf("Parent stops to let child to register signal\n");
            sleep(1);
            
            printf("Parent registers its signal handler\n");
            signal(SIGUSR2, handle_siguser2);
            
            printf("Parent waits child to be stopped\n");
            waitpid(getpid(), NULL, WUNTRACED);
            
            printf("Parent starts child process\n");
            kill(getpid(), SIGUSR1);
        }
    }
        
    return 0;   
}
/******************************************************************************/
void handle_siguser1(int signum)
{
    printf("PONG\n");
    sleep(2);
    kill(getppid(), SIGUSR2);
}
/******************************************************************************/
void handle_siguser2(int signum)
{
    printf("PING\n");
    sleep(2);
    kill(getpid(), SIGCONT);
}
/******************************************************************************/

The output of this code is:

Parent process started running
Parent stops to let child to register signal
Child registers its signal handler
Child stops to return the control to the parent
Parent registers its signal handler
Parent waits child to be stopped
Parent starts child process
User defined signal 1

Thanks.

NoobCoder
  • 513
  • 3
  • 18

1 Answers1

1

You have quite a variety of problems. Ignoring the issue that you should not use printf() in a signal handler, it turns out that the use of SIGUSR1 and SIGUSR2 is unnecessary and confusing, not least because the wrong processes are signalled. With the signal handling shown, you need to make the pid variable global, and send signals to that, not getpid() or getppid() (especially as getppid() would return the PID of the process that launches the program).

In fact, though, SIGUSR1 and SIGUSR2 are irrelevant. Here is some working code. It uses some of my code which is available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory. I've used err_setlogopts() so that the processes writing messages via err_remark() and err_sysrem() identify the PID and also the time to milliseconds when invoked. This is tremendously helpful. One thing it showed me was that the signal handlers were never invoked.

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include "stderr.h"

enum {CHILD = 0, PARENT = 1};

static pid_t pid;

int main(void)
{
    err_setarg0("sigcont43");
    err_setlogopts(ERR_PID|ERR_MILLI);

    pid = fork();

    for (int i = 0; i < 3; i++)
    {
        if (CHILD == pid)
        {
            err_remark("Child stops to return the control to the parent\n");
            err_remark("Child about to invoke raise(SIGSTOP)\n");
            raise(SIGSTOP);
            err_remark("Child back from raise(SIGSTOP)\n");
        }
        else
        {
            err_remark("Parent process started running\n");
            err_remark("Parent stops to let child to register signal\n");
            sleep(1);
            err_remark("Parent returns from sleep(1)\n");

            err_remark("Parent waits child to be stopped\n");
            int status = 0;
            int corpse = waitpid(pid, &status, WUNTRACED);

            err_remark("Parent collects stopped child: corpse = %d, status = 0x%.4X\n", corpse, status);
            err_remark("Parent starts child process\n");
            err_remark("Parent sends SIGCONT to PID %d\n", pid);

            errno = 0;
            int rc = kill(pid, SIGCONT);
            err_sysrem("Parent sent SIGCONT to PID %d (rc = %d): ", pid, rc);
        }
    }

    return 0;
}

The code has kept the original messages not related to the signal handlers (as there no longer are any signal handlers), but has added more informative messages. It also traps and reports the return values from many system functions. This helped diagnose some of the problems. I also changed the infinite while loop into a 3-iteration for loop.

Sample output (test program name sigcont43):

$ sigcont43
sigcont43: 2021-06-28 11:40:03.305 - pid=83078: Parent process started running
sigcont43: 2021-06-28 11:40:03.306 - pid=83078: Parent stops to let child to register signal
sigcont43: 2021-06-28 11:40:03.305 - pid=83079: Child stops to return the control to the parent
sigcont43: 2021-06-28 11:40:03.306 - pid=83079: Child about to invoke raise(SIGSTOP)
sigcont43: 2021-06-28 11:40:04.306 - pid=83078: Parent returns from sleep(1)
sigcont43: 2021-06-28 11:40:04.306 - pid=83078: Parent waits child to be stopped
sigcont43: 2021-06-28 11:40:04.306 - pid=83078: Parent collects stopped child: corpse = 83079, status = 0x117F
sigcont43: 2021-06-28 11:40:04.306 - pid=83078: Parent starts child process
sigcont43: 2021-06-28 11:40:04.306 - pid=83078: Parent sends SIGCONT to PID 83079
sigcont43: 2021-06-28 11:40:04.307 - pid=83079: Child back from raise(SIGSTOP)
sigcont43: 2021-06-28 11:40:04.307 - pid=83079: Child stops to return the control to the parent
sigcont43: 2021-06-28 11:40:04.307 - pid=83078: Parent sent SIGCONT to PID 83079 (rc = 0): error (0) Undefined error: 0
sigcont43: 2021-06-28 11:40:04.307 - pid=83078: Parent process started running
sigcont43: 2021-06-28 11:40:04.307 - pid=83078: Parent stops to let child to register signal
sigcont43: 2021-06-28 11:40:04.307 - pid=83079: Child about to invoke raise(SIGSTOP)
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent returns from sleep(1)
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent waits child to be stopped
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent collects stopped child: corpse = 83079, status = 0x117F
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent starts child process
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent sends SIGCONT to PID 83079
sigcont43: 2021-06-28 11:40:05.309 - pid=83079: Child back from raise(SIGSTOP)
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent sent SIGCONT to PID 83079 (rc = 0): error (0) Undefined error: 0
sigcont43: 2021-06-28 11:40:05.309 - pid=83079: Child stops to return the control to the parent
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent process started running
sigcont43: 2021-06-28 11:40:05.309 - pid=83079: Child about to invoke raise(SIGSTOP)
sigcont43: 2021-06-28 11:40:05.309 - pid=83078: Parent stops to let child to register signal
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent returns from sleep(1)
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent waits child to be stopped
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent collects stopped child: corpse = 83079, status = 0x117F
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent starts child process
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent sends SIGCONT to PID 83079
sigcont43: 2021-06-28 11:40:06.309 - pid=83078: Parent sent SIGCONT to PID 83079 (rc = 0): error (0) Undefined error: 0
sigcont43: 2021-06-28 11:40:06.309 - pid=83079: Child back from raise(SIGSTOP)
$
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Hey thanks. Can I somehow avoid `err_remark("Parent stops to let child to register signal\n");`? the `sleep`? because we can't know for sure (correct me if i'm wrong) that X process will gain control after Y process does `sleep`. I mean yes, it happens, 99.9% of the time, but we CAN NOT know that for sure. what if.... there are Z processes in the system that run in a current time. How can you know for sure, for 100%, that the child process is the one who will gain control right after its parent goes to sleep? – NoobCoder Jun 28 '21 at 19:52
  • Why aren't you using `pause`? – NoobCoder Jun 28 '21 at 19:55
  • 1
    I'm not using `pause()` because your code doesn't use `pause()`. You can eliminate any or all calls to the `err_remark()` function, though you probably need some to track what activity is going on. Until quite late in the processing, I had your `printf()` calls unchanged and added `err_remark()` calls, but I ended up mapping all the `printf()` calls to `err_remark()` to get the uniform program/time/PID prefix on the output lines. You can deduce easily which calls should be converted back to `printf()` to match your original code, and the other calls can be removed. – Jonathan Leffler Jun 28 '21 at 21:43
  • All sorts of other processes can be scheduled around the activity of the pair of processes running here. Those processes don't seriously affect these ones. The parent won't wake from `waitpid()` until the child stops itself; the child won't wake until the parent sends SIGCONT to the child. The synchronization between these two processes is deterministic (except for possible delays caused by other processes). You could add a `wait()` loop at the end (after the `for` loop) to wait for the child before the parent exits. – Jonathan Leffler Jun 28 '21 at 21:48