Search code examples
clinuxforkwaitchild-process

How to kill 2 child processes in a given order


I want to terminate child processes in a given order, but my program sometimes output the correct result, sometimes no.

Can anybody please what I'm doing wrong

The Correct Order: 1st C-process has terminated. 2nd C-process has terminated. P-process has terminated.

Getting this output sometimes: 2nd C-process has terminated. 1st C-process has terminated. P-process has terminated.

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

pid_t child_pid = -1 ; //Global
pid_t child_pidb = -1 ; //Global

void kill_child(int sig) {

    kill(child_pid,SIGKILL);
}

void kill_childb(int sig){

    kill(child_pidb,SIGKILL);
}

int main(int argc, char *argv[])
{
   pid_t child_pid = fork();

    if (child_pid > 0) {
        
        pid_t child_pidb = fork();
        
        if(child_pidb>0) { 
            signal(SIGSTKFLT,(void (*)(int))kill_child);
            sleep(3);
            signal(SIGCHLD,(void (*)(int))kill_childb);
            wait(NULL);
             printf("P-process has terminated.");
             exit(0);
        }
        else if (child_pidb ==0 ) {
            printf("2nd C-process has terminated.");
        }
    }
    else if (child_pid == 0){
        printf("1st C-process has terminated.");
    }
}

Solution

  • Read carefully both signal(7) and signal-safety(7). And of course Advanced Linux Programming

    Consider having simpler signal handlers, each would just set some global variable declared as volatile sigatomic_t gotsig;

    Then code some finite state machine around sigpause(2) and/or waitpid(2)

    Another possibility might be to use signalfd(2) with some event loop around poll(2).

    The important thing is to draw on paper your finite automaton graph and to think in terms of a transition system. You might view your signals (and other events like a process ending) as input to some parsing algorithm, probably an LL parser.

    Study for inspiration the code of some open source shell, e.g. GNU bash or sash, and use strace(1) to understand its behavior in terms of syscalls(2)

    BTW, you forgot to handle an important case: a fork(2) can fail. See errno(3) and perror(3).

    Remember that several milliseconds (or more, on a very loaded Linux system - see proc(5)) could happen between a process being terminated, and the corresponding waitpid to succeed. And with multi-core processors, you could have two different processes running on two different cores.

    Read credentials(7).