0

I have a problem sending multiple signals to parent process in c. I've been trying for a long time now and I still end up in a deadlock or a waitlock. But I think it shouldn't be that complicated, I simply can't understand what is going on..

So I have a parent process and two child processes. The children are signaling to the parent, it accepts them, then sends a message to the children in pipe. They write it out to console.

My code so far:

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

void handler(int signumber) {
    printf("Signal with number %i has arrived\n", signumber);
}

int main() {


    signal(SIGUSR1, handler);
    signal(SIGUSR2, handler);

    int pipefd[2];
    char sz[100];
    if (pipe(pipefd) == -1)
    {
        perror("Pipe open error");
        exit(EXIT_FAILURE);
    }
    pid_t child, child2;
    child = fork();
    if (child > 0)
    {
        pause();
        printf("Signal1 arrived\n", SIGUSR1);

        //pause();
        printf("Signal2 arrived\n", SIGUSR2);

        close(pipefd[0]); //Usually we close unused read end
        write(pipefd[1], "Message", 13);
        printf("Pipe write complete\n");
        int status;
        waitpid(child2, &status, 0);
        waitpid(child, &status, 0);

        printf("Parent process ended\n");
    }
    else
    {
        child2 = fork();
            if(child2>0) //child 1
            { 
                printf(" child 1 Waits 3 seconds, then send a SIGTERM %i signal\n", SIGUSR1);
                sleep(3);
                signal(SIGUSR1, handler);
                close(pipefd[1]);  //Usually we close the unused write end
                printf("Child1 read start\n");
                read(pipefd[0], sz, sizeof(sz)); // reading max 100 chars
                printf("Child1 read end, Message: %s", sz);
                printf("\n");
                close(pipefd[0]); // finally we close the used read end
                printf("Child1 process ended\n");
                kill(child, SIGTERM);
            }
            else //child 2
            {
                printf("child 2 Waits 3 seconds, then send a SIGTERM %i signal\n", SIGUSR2);
                sleep(3);
                signal(SIGUSR2, handler);
                close(pipefd[1]);  //Usually we close the unused write end
                printf("Child2 read start\n");
                read(pipefd[0], sz, sizeof(sz)); // reading max 100 chars
                printf("Child2 read end, Message: %s", sz);
                printf("\n");
                close(pipefd[0]); // finally we close the used read end
                printf("Child2 process ended\n");
                kill(child2, SIGTERM);
            }
    }
    return 0;
}```
Shapperd
  • 155
  • 8
  • See [How to avoid using `printf()` in a signal handler?](https://stackoverflow.com/q/16891019/15168) for information on why you shouldn't use `printf()` in a signal handler, and for information on what you can use instead. – Jonathan Leffler May 19 '20 at 13:18
  • [kprow](https://stackoverflow.com/users/13570616/kprow) makes [valid points](https://stackoverflow.com/a/61891414/15168). Note that if Child 1 reads the information from the pipe, Child 2 cannot, and vice versa. If both children need a message, the parent must write two messages. To be reliable (especially if you must send multiple messages), you need two pipes, one for each child. With a single pipe, the children must know in advance how much data to read for a message, and must only read that much data. Worry about null terminating strings (`write()` deals in byte arrays, not strings). – Jonathan Leffler May 19 '20 at 13:29

1 Answers1

1

I think your code is laced with race conditions and memory errors. You're also not sending SIGUSR1 or SIGUSR2 to the parent. Speaking subjectively, your solution is implemented in a manner is that is difficult to follow. If you ordered your statements differently, I think you'd also spot the same errors I'm seeing (I mention this later).

I highly recommend starting small and building your way towards the goal you are aiming towards. Step 1, for instance, could be getting a process to fork, having the parent send a message over the pipe to the child, having the child exit and having the parent reap the child. In Step 2, add the signal handler you have in your example code above to the parent, only, and have the child send that signal (the exact signal the parent will handle) to the parent using kill(). At some later step, add another fork call to create another child.

As far as race conditions are concerned, your code is ordered such that after the first fork you have a parent process that is going to try to handle two child processes but only one child process has been created. Thus, you're trying to do something that is a no-no. Also, the first child is creating a second child. This means the parent of the first child doesn't even know the second child exists because the first child has a completely different memory space. The first child doesn't have a waitpid to wait for the second child that it created it because its "entry point" so to speak is after the waitpid call earlier in the code.

Also, beware of your write(pipefd[1], "Message", 13); is scary. The last parameter of write() is count. You supplied 13 but the length of the string "Message" is less than 13. The system call is going to spew garbage to the reader, causing more undesirable problems.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
kprow
  • 84
  • 3