简体   繁体   中英

Process synchronization with signals

Nowadays, I'm learning signals by own efforts with concurrency. However, I have some problems while using them in action. In this program, I expect that there are two processes (one child - one parent), parent sends SIGHUP , SIGINT , SIGTSTP signals to its child respectively. I want the child to handle them in same order. I'm trying to apply synchronization with signals here. I have two major problems that

  • When parent runs at first prior its child, it works but it misses SIGINT , not go into its handler.
  • When child runs at first prior its parent, it blocks. I expect in either way the program runs well.

I really appreciate as well if you let me inform the other issues and their solutions along with the major ones'.

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


char const * hupMsg = "CHILD: I have received a SIGHUP\n";
int hupMsgLen;
void sighup(int x) {
    write(STDERR_FILENO, hupMsg, hupMsgLen);

}

char const * intMsg = "CHILD: I have received a SIGINT\n";
int intMsgLen;
void sigint(int x) {
    write(STDERR_FILENO, intMsg, intMsgLen);
}

char const * die = "CHILD: My DADDY has Killed me!!!\n";
int dieLen;
void sigtstp(int x) {
    write(STDERR_FILENO, die, dieLen);
    exit(111);     // <-- child dies with 111 exit code
}


char const * sigusr1Msg = "SIGUSR1 is being caught\n";
int sigusr1MsgLen;
void sigusr1(int x) {
    write(STDERR_FILENO, sigusr1Msg, sigusr1MsgLen);
}




int main()
{
    dieLen = strlen(die);
    sigusr1MsgLen = strlen(sigusr1Msg);
    hupMsgLen = strlen(hupMsg);
    intMsgLen = strlen(intMsg);

    int         pid;
    sigset_t    allBlockedMask, prevMask, parentUSR1Mask,
                sighupSet, sigintSet, sigtstpSet;

    sigfillset(&sighupSet);
    sigdelset(&sighupSet, SIGHUP);
    sigfillset(&sigintSet);
    sigdelset(&sigintSet, SIGINT);
    sigfillset(&sigtstpSet);
    sigdelset(&sigtstpSet, SIGTSTP);


    sigfillset(&allBlockedMask);

    sigfillset(&parentUSR1Mask);
    sigdelset(&parentUSR1Mask, SIGUSR1);


    struct sigaction saINT, saHUP, saTSTP;
    memset(&saINT, 0, sizeof(saINT));
    memset(&saHUP, 0, sizeof(saHUP));
    memset(&saTSTP, 0, sizeof(saTSTP));
    saINT.sa_flags = 0;
    saINT.sa_handler = sigint;
    saHUP.sa_flags = 0;
    saHUP.sa_handler = sighup;
    saTSTP.sa_flags = 0;
    saTSTP.sa_handler = sigtstp;



    struct sigaction saUSR1;
    memset(&saUSR1, 0, sizeof(saUSR1));
    saUSR1.sa_flags = 0;
    saUSR1.sa_handler = sigusr1;
    sigaction(SIGUSR1, &saUSR1, 0);



    /* get child process */

    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) {   /* child */

        fprintf(stderr, "child pid %i\n", getpid());

        /* critical section to set signal handlers */
        sigprocmask(SIG_BLOCK, &allBlockedMask, &prevMask);
        fprintf(stderr, "child in blocking\n");

        /* set function calls */
        sigaction(SIGHUP, &saHUP, 0);
        sigaction(SIGINT, &saINT, 0);
        sigaction(SIGTSTP, &saTSTP, 0);

        /* let parent know, you can continue your process */
        kill(getppid(), SIGUSR1);

        fprintf(stderr, "child now UNblocked \n");
        sigprocmask(SIG_SETMASK, &prevMask, 0);

        /* here, I want to receive signals in turn */
        sigsuspend(&sighupSet);
        sigsuspend(&sigintSet);
        sigsuspend(&sigtstpSet);


    }
    else {
        /* parent */
        //sleep(2);
        fprintf(stderr, "parent pid %i\n", getpid());


        fprintf(stderr, "parent is waiting\n");
        sigsuspend(&parentUSR1Mask);
        fprintf(stderr, "parent is running\n");


        fprintf(stderr, "\nPARENT: sending SIGHUP\n");
        kill(pid, SIGHUP);

        fprintf(stderr, "\nPARENT: sending SIGINT\n");
        kill(pid, SIGINT);

        fprintf(stderr, "\nPARENT: sending SIGTSTP\n\n");
        kill(pid, SIGTSTP);

        wait(0);
    }

    return 0;
}

Sample output when parent runs at first:

parent pid 4628
parent is waiting
child pid 4629
child in blocking
child now UNblocked 
SIGUSR1 is being caught
parent is running

PARENT: sending SIGHUP

PARENT: sending SIGINT

PARENT: sending SIGTSTP

CHILD: I have received a SIGHUP
CHILD: My DADDY has Killed me!!!

Process finished with exit code 0

Sample output when child runs at first:

child pid 4657
child in blocking
child now UNblocked 
SIGUSR1 is being caught
parent pid 4656
parent is waiting
.
.
.

Issues:

  • As mentioned in comments, signal handlers must not call exit() , but they may call _exit() .

  • Your signal handlers do not block signals. This may in fact be responsible for some of your problems, as your signal handlers can themselves be interrupted by signals. In the "parent runs first" case, for example, the write() calls in the child's signal handlers may be interrupted by new signals. Solve this by using the sa_mask members of your struct sigaction objects to specify masks of signals to block during execution of your handlers.

  • As you have observed, you have a race condition between delivery to the parent of the child's SIGUSR1 and the parent's call to sigsuspend() . Solve this by blocking all signals in the parent before the fork instead of in the child afterward. The parent will then not receive the child's SIGUSR1 until it calls sigsuspend() (with a mask that allows it). The parent can restore the original mask after its sigsuspend() returns; the child can continue to restore its signal mask where it now does.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM