简体   繁体   中英

Trouble sending signal between parent and child process in C

I am doing project for my studies according to guidelines and I have a problem. I have to do communication using signals and pipes as follows:

  • process 2 receive signal and send it to parent
  • parent process save value of signal in pipe and send signal to process 1 to read pipe
  • process 1 read pipe and send signal to process 2 to read pipe
  • process 2 read pipe and send signal to process 3 to read pipe
  • process 3 read pipe

Unfortunately when I send one of the supported signals (eg 20) to process 2 signals are looped and program is terminated by SIGUSR1.

int stopm=0;
int stop1=0;
int stop2=0;
int stop3=0;
int termm=1;
int term1=1;
int term2=1;
int term3=1;

void obslugam(int sig){
    signal(15,obslugam);
    signal(18,obslugam);
    signal(20,obslugam);

    sigset_t mask_set;
    sigfillset(&mask_set);
    sigdelset(&mask_set,SIGUSR1);
    sigdelset(&mask_set,SIGTERM);
    sigdelset(&mask_set,SIGCONT);
    sigdelset(&mask_set,SIGTSTP);
    sigprocmask(SIG_BLOCK,&mask_set,NULL);
    printf("Jestem M i odebralem sygnal %d\n",sig);
    if(sig==15){
        write(pm1[1],"15",2);
        write(pm2[1],"15",2);
        write(pm3[1],"15",2);
    }
    if(sig==18){
        write(pm1[1],"18",2);
        write(pm2[1],"18",2);
        write(pm3[1],"18",2);
    }
    if(sig==20){
        write(pm1[1],"20",2);
        write(pm2[1],"20",2);
        write(pm3[1],"20",2);
    }
    kill(PID1,10);

}
void obsluga1(int sig){
    signal(10,obsluga1);

    sigset_t mask_set;
    sigfillset(&mask_set);
    sigdelset(&mask_set,SIGUSR1);
    sigprocmask(SIG_BLOCK,&mask_set,NULL);
    printf("Jestem 1 i odebralem sygnal %d\n",sig);
    char bufor_p1[2]={0};   
    if(read(pm1[0],&bufor_p1,2)==-1)perror("PIPE1\n");
    if(strchr(bufor_p1,53)!=NULL) printf("Jestem 1 i dostałem 15\n");
    else if(strchr(bufor_p1,56)!=NULL) printf("Jestem 1 i dostałem 18\n");
    else if(strchr(bufor_p1,48)!=NULL) printf("Jestem 1 i dostałem 20\n");
    kill(PID2,10);
}
void obsluga2(int sig){
    signal(10,obsluga2);
    signal(15,obsluga2);
    signal(18,obsluga2);
    signal(20,obsluga2);

    sigset_t mask_set;
    sigfillset(&mask_set);
    sigdelset(&mask_set,SIGUSR1);
    sigdelset(&mask_set,SIGTERM);
    sigdelset(&mask_set,SIGCONT);
    sigdelset(&mask_set,SIGTSTP);
    sigprocmask(SIG_BLOCK,&mask_set,NULL);
    printf("Jestem 2 i odebralem sygnal %d\n",sig);
    if(sig==10){
        char bufor_p2[2]={0};
        if(read(pm2[0],&bufor_p2,2)==-1)perror("PIPE2\n");
        if(strchr(bufor_p2,53)!=NULL) printf("Jestem 2 i dostałem 15\n");
        else if(strchr(bufor_p2,56)!=NULL) printf("Jestem 2 i dostałem 18\n");
        else if(strchr(bufor_p2,48)!=NULL) printf("Jestem 2 i dostałem 20\n");  
        kill(PID3,10);
    }
    else if(sig==15) kill(getppid(),15);
    else if(sig==18) kill(getppid(),18);
    else if(sig==20) kill(getppid(),20);
}
void obsluga3(int sig){
    signal(10,obsluga3);

    sigset_t mask_set;
    sigfillset(&mask_set);
    sigdelset(&mask_set,SIGUSR1);
    sigprocmask(SIG_BLOCK,&mask_set,NULL);
    printf("Jestem 3 i odebralem sygnal %d\n",sig);
    char bufor_p3[2]={0};
    if(read(pm3[0],&bufor_p3,2)==-1)perror("PIPE3\n");
    if(strchr(bufor_p3,53)!=NULL) printf("Jestem 3 i dostałem 15\n");
    else if(strchr(bufor_p3,56)!=NULL) printf("Jestem 3 i dostałem 18\n");
    else if(strchr(bufor_p3,48)!=NULL) printf("Jestem 3 i dostałem 20\n");
}

int main(){

    (...)

    pipe(pm1);
    pipe(pm2);
    pipe(pm3);
    signal(15,obslugam);
    signal(18,obslugam);
    signal(20,obslugam);

    if((PID1=fork())==0) {
        close(pm1[1]);
        signal(10,obsluga1);
    }
    else if((PID2=fork())==0) {
        close(pm2[1]);
        signal(10,obsluga2);
        signal(15,obsluga2);
        signal(18,obsluga2);
        signal(20,obsluga2);
    }
    else if((PID3=fork())==0){
        printf("\nJestem procesem nr 2. Oto moj PID: %d\n\n",PID2);
        close(pm3[1]);
        signal(10,obsluga3);
    }
    else{
        close(pm1[0]);
        close(pm2[0]);  
        close(pm3[0]);
    }

    (...)

}

I try but I can't find a place where I made a mistake. I will be grateful for your help.

I can't find a place where I made a mistake.

The mistake was ignoring the contents of the first paragraph in the signal (2) manual page.

The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead. See Portability below.

The manual page went on to describe various ways different Unixes implemented signal (). It described the original UNIX behavior, which was also adopted with System V Unix, namely that after installing a signal handler via signal (), as soon as the signal is delivered the signal gets reset to SIG_DFL .

The manual page then states the following:

The situation on Linux is as follows:

  • The kernel's signal() system call provides System V semantics.

Conclusions:

1) When using system calls, like signal (), it's important to read its manual page.

2) The first time SIGUSR1 invokes a custom signal handler on Linux, the signal handler gets reset to SIG_DFL .

3) Then, if nothing else happens,the second time the signal is received, the process will be terminated, for a signal whose SIG_DFL action is to terminate the process.

4) The SIG_DFL action for SIGUSR1 is to terminate the process. Therefore, the second time your process receives this signal, it will be terminated. This appears to be the behavior you are seeing.

5) It is possible to immediately reinstall the signal handler, upon receipt of a SIGUSR1 , of course, but as the manual page explains, nothing prevents the process from receiving a SIGUSR1 before it has the chance to reinstall the signal handler.

6) For these reasons, the manual page directs you to use sigaction (2) instead.

7) An even better option, on Linux, is to use signalfd(2) . This is, of course, a Linux-specific API.

Additionally, the sample code has at least two additional bugs:

   char bufor_p2[2]={0};
   if(read(pm2[0],&bufor_p2,2)==-1)perror("PIPE2\n");
   if(strchr(bufor_p2,53)!=NULL) printf("Jestem 2 i dostałem 15\n");

There are two bugs here. This code appears to read two characters from a pipe, and expects that they will be read in entirety.

The first bug is that when reading more than one byte from a pipe, or a device, you are not guaranteed that you will successfully read the number of bytes requested. In this case, you might end up reading a single byte. When the application reads fewer bytes than it requested, it's the application's responsibility to proceed accordingly, typically try again to read the remaining data.

There are some guarantees from the Linux kernel that this will never actually happen, on Linux, but technically this is a bug. The same goes for writing to a pipe or a device. Other parts of the shown code attempt to write more than one byte to a pipe, and assume that this will succeed.

In a robust application, all attempts to read or write from a pipe, a socket, or some other devices, should be prepared to deal with the possibility that fewer bytes were read or written, than requested, and try again.

The second bug here is that the two bytes are read from the pipe, and placed into a two character array, which are then checked with strchr ().

Those expected two bytes, according to the rest of the code, are two decimal digits representing the signal number.

strchr () expects a null terminated string. The two bytes in the two-byte character array will not be null-terminated, of course. This will result in undefined behavior.

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