簡體   English   中英

C中的父進程和子進程之間無法發送信號

[英]Trouble sending signal between parent and child process in C

我正在按照指導進行研究項目,但遇到了問題。 我必須使用信號和管道進行通信,如下所示:

  • 進程2接收信號並將其發送給父進程
  • 父進程將信號值保存在管道中並將信號發送到進程1以讀取管道
  • 進程1讀取管道並將信號發送到進程2以讀取管道
  • 進程2讀取管道並將信號發送到進程3以讀取管道
  • 過程3讀取管道

不幸的是,當我發送一個受支持的信號(例如20個)以處理2個信號時,信號被循環並且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]);
    }

    (...)

}

我嘗試過,但是找不到錯誤的地方。 感謝您的幫助。

我找不到我犯錯的地方。

錯誤是忽略了signal (2)手冊頁中第一段的內容。

signal()的行為在UNIX版本之間有所不同,並且在歷史上在Linux的不同版本中也有所不同。 避免使用它:改為使用sigaction(2)。 請參閱下面的可移植性。

手冊頁繼續描述了不同的Unix實現signal ()的各種方式。 它描述了原始的UNIX行為,該行為也被System V Unix所采用,即在通過signal ()安裝了信號處理程序之后,一旦傳遞了信號,信號便會重置為SIG_DFL

然后,手冊頁指出以下內容:

Linux上的情況如下:

  • 內核的signal()系統調用提供了System V語義。

結論:

1)使用系統調用時,如signal (),重要的是閱讀其手冊頁。

2) SIGUSR1第一次在Linux上調用自定義信號處理程序時,該信號處理程序將重置為SIG_DFL

3)然后,如果沒有其他事情發生,則第二次接收到該信號時,該過程將終止,因為其SIG_DFL動作將終止該過程的信號。

4) SIGUSR1SIG_DFL操作將終止該過程。 因此,您的進程第二次收到此信號時,它將終止。 這似乎是您所看到的行為。

5)當然可以在收到SIGUSR1后立即重新安裝信號處理程序,但是正如手冊頁所述,沒有什么阻止該進程在有機會重新安裝信號處理程序之前接收到SIGUSR1

6)由於這些原因,手冊頁指示您改用sigaction (2)。

7)在Linux上,更好的選擇是使用signalfd(2) 當然,這是特定於Linux的API。

此外,該示例代碼還具有至少兩個其他錯誤:

   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");

這里有兩個錯誤。 該代碼似乎從管道讀取了兩個字符,並希望它們將被完整讀取。

第一個錯誤是,當從管道或設備讀取多個字節時,不能保證您將成功讀取請求的字節數。 在這種情況下,您可能最終只讀取一個字節。 當應用程序讀取的字節數少於請求的字節數時,應用程序將負責繼續進行相應的操作,通常會再次嘗試讀取其余數據。

Linux內核可以保證這種情況在Linux上永遠不會發生,但是從技術上講這是一個錯誤。 寫入管道或設備也是如此。 所示代碼的其他部分嘗試向管道寫入一個以上的字節,並假定此操作將成功。

在健壯的應用程序中,應准備從管道,套接字或某些其他設備讀取或寫入的所有嘗試,以解決讀取或寫入的字節少於請求的字節的可能性,然后重試。

這里的第二個錯誤是從管道讀取了兩個字節,並將其放入兩個字符的數組中,然后使用strchr ()對其進行檢查。

根據代碼的其余部分,預期的兩個字節是兩個十進制數字,表示信號編號。

strchr ()需要以null結尾的字符串。 當然,兩字節字符數組中的兩個字節不會以空值結尾。 這將導致不確定的行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM