簡體   English   中英

從父母發送信號給孩子

[英]sending signal from parent to child

我正在使用http://www.code2learn.com/2011/01/signal-program-using-parent-child.html網站上的這個教程,並試圖了解為什么信號沒有被孩子收到?

這是代碼:

#include <stdio.h>  
#include <signal.h>
#include <stdlib.h>  
void sighup(); /* routines child will call upon sigtrap */  
void sigint();  
void sigquit();  
void main()  
{ int pid;  
    /* get child process */  
    if ((pid = fork()) < 0) {  
        perror("fork");  
        exit(1);  
    }  
    if (pid == 0)  
    { /* child */  
        signal(SIGHUP,sighup); /* set function calls */  
        signal(SIGINT,sigint);  
        signal(SIGQUIT, sigquit);  
        for(;;); /* loop for ever */  
    }  
    else /* parent */  
    { /* pid hold id of child */  
        printf("\nPARENT: sending SIGHUP\n\n");  
        kill(pid,SIGHUP);  
        sleep(3); /* pause for 3 secs */  
        printf("\nPARENT: sending SIGINT\n\n");  
        kill(pid,SIGINT);  
        sleep(3); /* pause for 3 secs */  
        printf("\nPARENT: sending SIGQUIT\n\n");  
        kill(pid,SIGQUIT);  
        sleep(3);  
    }  
}  
void sighup()  
{ signal(SIGHUP,sighup); /* reset signal */  
    printf("CHILD: I have received a SIGHUP\n");  
}  
void sigint()  
{ signal(SIGINT,sigint); /* reset signal */  
    printf("CHILD: I have received a SIGINT\n");  
}  
void sigquit()  
{ printf("My DADDY has Killed me!!!\n");  
    exit(0);  
}  

輸出:

在此輸入圖像描述

這是競爭條件。 您的代碼假定子進程先運行並且不會被父進程搶占,直到它安裝所有信號處理程序並開始永遠循環。

如果不是這種情況,父母可能會在孩子有機會接收信號之前向孩子發出信號。 因此,子進程被終止,因為SIGHUPSIGINTSIGQUIT的默認操作是終止。

在您的具體情況下,您永遠不會看到孩子的任何輸出。 這意味着父級已將SIGHUP發送給子級,並且在子級更改默認行為之前已傳遞SIGHUP 所以孩子被殺了。

實際上,如果你對kill(2)的返回值做了一些錯誤檢查 - 你應該 - 你會在嘗試發送SIGINTSIGQUIT時看到父進程中的ESRCH ,因為該子進程已經消失(假設沒有其他進程)系統啟動並同時分配了相同的PID。

那么,你如何解決它? 使用某種形式的同步來強制子進程先運行,只允許父進程在安裝所有信號處理程序后執行,或者分叉之前設置信號處理程序,然后在父進程中取消設置它們。 下面的代碼使用后一種方法:

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

void sighup(int); /* routines child will call upon sigtrap */
void sigint(int);
void sigquit(int);

int main(void) {

    int pid;

    signal(SIGHUP,sighup); /* set function calls */
    signal(SIGINT,sigint);
    signal(SIGQUIT, sigquit);

    /* get child process */
    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) {

        /* child */
        for(;;); /* loop for ever */

    } else {

        signal(SIGHUP, SIG_DFL);
        signal(SIGINT, SIG_DFL);
        signal(SIGQUIT, SIG_DFL);

        /* parent */
        /* pid hold id of child */
        printf("\nPARENT: sending SIGHUP\n\n");
        kill(pid,SIGHUP);
        sleep(3); /* pause for 3 secs */
        printf("\nPARENT: sending SIGINT\n\n");
        kill(pid,SIGINT);
        sleep(3); /* pause for 3 secs */
        printf("\nPARENT: sending SIGQUIT\n\n");
        kill(pid,SIGQUIT);
        sleep(3);
    }

    return 0;
}

void sighup(int signo) {
    signal(SIGHUP,sighup); /* reset signal */
    printf("CHILD: I have received a SIGHUP\n");
}

void sigint(int signo) {
    signal(SIGINT,sigint); /* reset signal */
    printf("CHILD: I have received a SIGINT\n");
}

void sigquit(int signo) {
    printf("My DADDY has Killed me!!!\n");
    exit(0);
}

此外,您不應該使用signal(2) :它在許多方面都不可靠,其確切的語義依賴於平台。 為確保最大的可移植性,您應該使用sigaction(2) 請參閱聯機幫助頁以了解更多信息。 這是使用sigaction(2)的相同代碼:

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

void sighup(int); /* routines child will call upon sigtrap */
void sigint(int);
void sigquit(int);

int main(void) {

    struct sigaction sigact;
    sigact.sa_flags = 0;
    sigemptyset(&sigact.sa_mask);

    sigact.sa_handler = sighup;
    if (sigaction(SIGHUP, &sigact, NULL) < 0) {
        perror("sigaction()");
        exit(1);
    }

    sigact.sa_handler = sigint;
    if (sigaction(SIGINT, &sigact, NULL) < 0) {
        perror("sigaction()");
        exit(1);
    }

    sigact.sa_handler = sigquit;
    if (sigaction(SIGQUIT, &sigact, NULL) < 0) {
        perror("sigaction()");
        exit(1);
    }

    pid_t pid;
    /* get child process */
    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) {

        /* child */
        for(;;); /* loop for ever */

    } else {

        sigact.sa_handler = SIG_DFL;
        sigaction(SIGHUP, &sigact, NULL);
        sigaction(SIGINT, &sigact, NULL);
        sigaction(SIGQUIT, &sigact, NULL);

        /* parent */
        /* pid hold id of child */
        printf("\nPARENT: sending SIGHUP\n\n");
        kill(pid,SIGHUP);
        sleep(3); /* pause for 3 secs */
        printf("\nPARENT: sending SIGINT\n\n");
        kill(pid,SIGINT);
        sleep(3); /* pause for 3 secs */
        printf("\nPARENT: sending SIGQUIT\n\n");
        kill(pid,SIGQUIT);
        sleep(3);
    }

    return 0;
}

void sighup(int signo) {
    signal(SIGHUP,sighup); /* reset signal */
    printf("CHILD: I have received a SIGHUP\n");
}

void sigint(int signo) {
    signal(SIGINT,sigint); /* reset signal */
    printf("CHILD: I have received a SIGINT\n");
}

void sigquit(int signo) {
    printf("My DADDY has Killed me!!!\n");
    exit(0);
}

最后,但並非最不重要的是,重要的是要提到您應該始終使用-Wall編譯。 你的程序有一些錯誤:

  • main()的返回類型應為int
  • 信號處理程序接收信號編號作為參數,請使用正確的原型和聲明。
  • fork(2)返回一個pid_t ,而不是一個int ,請使用正確的類型。
  • 你需要包含unistd.h來獲得fork(2)的正確原型。
  • printf(3)不是異步信號安全的,因此你不應該在信號處理程序中調用它。 在這個玩具程序中可以看到信號如何協同工作,但請記住,你不應該在現實世界中這樣做。 要查看異步信號安全功能列表以及每個信號的默認操作,請參閱man 7 signal

建議:停止從該網站學習。 如果您想學習這類內容,請閱讀UNIX環境中的高級編程 直接進入第10章,了解為什么signal(2)被認為是不可靠和過時的。 這是一本很大的書,但值得投入時間。

暫無
暫無

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

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