[英]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);
}
輸出:
這是競爭條件。 您的代碼假定子進程先運行並且不會被父進程搶占,直到它安裝所有信號處理程序並開始永遠循環。
如果不是這種情況,父母可能會在孩子有機會接收信號之前向孩子發出信號。 因此,子進程被終止,因為SIGHUP
, SIGINT
和SIGQUIT
的默認操作是終止。
在您的具體情況下,您永遠不會看到孩子的任何輸出。 這意味着父級已將SIGHUP
發送給子級,並且在子級更改默認行為之前已傳遞SIGHUP
。 所以孩子被殺了。
實際上,如果你對kill(2)
的返回值做了一些錯誤檢查 - 你應該 - 你會在嘗試發送SIGINT
和SIGQUIT
時看到父進程中的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.