0

I am trying to create a child process through fork() system call, then trying to send a signal to parent and print out something on the screen.

Here is my code:-

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

void func1(int signum) {
    if(signum == SIGUSR2) {
        printf("Received sig from child\n");
    }
}

int main() {
    signal(SIGUSR2, func1);

    int c = fork();
    if(c > 0) {
        printf("parent\n");
    }
    else if(c == -1) {
        printf("No child");
    }
    else {
        kill(getppid(), SIGUSR2);
        printf("child\n");
    }

}

When I execute my program all I get is:-

child
Segmentation fault (core dumped)

I am a novice to C language system calls, and don't get why this is happening, and how to get the desired output, which would be printing of all the three printf statements. Any help for the same would be appreciated.

P.P
  • 117,907
  • 20
  • 175
  • 238
firstlegagain1
  • 117
  • 2
  • 12
  • 2
    `c` should be a `pid_t` not an `int` and the parent process doesn't stick around to wait for the signal either – Chris Turner Jan 07 '19 at 15:14
  • 3
    Calling `printf()` from within a signal handler is not safe. Per [footnote 188 of the C standard](https://port70.net/~nsz/c/c11/n1570.html#note188): "Thus, a signal handler cannot, in general, call standard library functions." POSIX allows the calling of async-signal-safe functions from withing a signal handler. `printf()` is not async-signal-safe. – Andrew Henle Jan 07 '19 at 15:15
  • @ChrisTurner but if parent is running completely why isn't "parent" being printed on the output? Also, changing 'c''s type to pid_t isn't solving the issue – firstlegagain1 Jan 07 '19 at 15:16
  • You are not handling the case of a `fork()` failure, in which event `-1` will be returned *in the parent*, and no child will be created. At minimum, your program will erroneously output "child" in that case. – John Bollinger Jan 07 '19 at 15:20
  • 1
    @firstlegagain1 I'm not saying it is running completely, just pointing out that it could exit before the child tried sending the signal – Chris Turner Jan 07 '19 at 15:21
  • @JohnBollinger Added it in – firstlegagain1 Jan 07 '19 at 15:24
  • 2
    It is considered bad practice to change your question with things you learn from the answers because then the answers and comments no longer make sens: you added `else if(c == -1) ` – Paul Ogilvie Jan 07 '19 at 15:33
  • 1
    Have you (re)compiled & tested the code you have posted? It's hard to see this could result in segfault despite some issues. – P.P Jan 07 '19 at 15:34
  • @usr, I can image killing the child and then having the child print something causes a seg fault: `kill(getppid(), SIGUSR2); printf();` – Paul Ogilvie Jan 07 '19 at 15:36
  • 1
    In the event that the parent process receives the child's signal before it terminates, it produces undefined behavior by calling `printf` from a signal handler. That certainly could manifest as a segfault. In practice, however, that particular UB often manifests as producing the expected output, especially in simple cases such as this one. Is it really the exact code you have presented that exhibits the behavior you describe? – John Bollinger Jan 07 '19 at 15:37
  • 1
    @PaulOgilvie That (`kill()`) doesn't kill the child though. It sends the signal to the parent. – P.P Jan 07 '19 at 15:40
  • For me, the code consistently prints "parent" and then "child". It has not yet printed "Received sig from child" in any of my trials, nor has it segfaulted. – John Bollinger Jan 07 '19 at 15:41
  • @JohnBollinger Yes, this is exactly the code. I just ran it – firstlegagain1 Jan 07 '19 at 15:44
  • 1
    OT: regarding: `printf("No child");` 1) Error messages should be output to `stderr`, not `stdout` 2) when an error indication comes from a C library function, should also output (to `stderr`) the text reason the system thinks the error occurred. Suggest: `perror( "fork failed" );` – user3629249 Jan 07 '19 at 16:02
  • a parent must call `wait()` or `waitpid()` so the child has exited before the parent exits. In modern OSs, when the parent exits first, then the child becomes a child of (in linux) the `init()` process. Then sending a signal to the (in this case) the `init()` process results in the seg fault event – user3629249 Jan 07 '19 at 16:05

1 Answers1

3

Your code has a number of minor issues and certainly has undefined behaviour i.e., you can't call printf or other async-signal unsafe functions from a signal handler. This is the code with fixes (see comments in code). This should work as expected (with no particular order of print statements) and see if still get a segfault with this code.

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

void func1(int signum)
{
    /* write is asyc-signal-safe */
    write(1, "Received sig from child\n", sizeof "Received sig from child\n" - 1);
}

int main()
{
    signal(SIGUSR2, func1);

    /* fork returns a pid_t */
    pid_t c = fork();
    if(c > 0) {
        printf("parent\n");
        /* Wait for the child to exit; otherwise, you may not receive the signal */
        if (wait(NULL) == -1) {
            printf("wait(2) failed\n");
            exit(1);
        }
    } else if (c == -1) {
        printf("fork(2) error\n");
        exit(1);
    } else {
        if (kill(getppid(), SIGUSR2) == -1) {
            /* In case kill fails to send signal... */
            printf("kill(2) failed\n");
            exit(1);
        }
        printf("child\n");
    }
}
P.P
  • 117,907
  • 20
  • 175
  • 238
  • 1
    The key thing here is the call to wait, if you don't do this your parent process will almost certainly have terminated by the time the child tries to send it a signal and on Unix you will end up trying to signal the init process. See https://stackoverflow.com/questions/15183427/getpid-and-getppid-return-two-different-values/15183445#15183445 – Jackson Jan 07 '19 at 15:56
  • So, I tried this, this worked. The trick is the write instead of printf in the signal handler. As I remove that, my original code is working as well – firstlegagain1 Jan 07 '19 at 16:00