fork和exec中的信號繼承探索
- 一、結論
- 二、代碼驗證
- 2.1 代碼編寫
- 2.2 代碼執行
- 三、linux源碼驗證
- 四、APUE中的驗證
- 五、其他
一、結論
fork
時子進程會繼承父進程的信號處理方式,包括父進程設置信號為SIG_DFL
或SIG_IGN
或捕獲后設置自定義處理函數。exce
時子進程會繼承父進程設置為SIG_DFL
或SIG_IGN
的信號。對于捕獲后設置自定義處理函數的信號則不繼承這個處理函數。
二、代碼驗證
2.1 代碼編寫
此處針對exec
的情況進行驗證,對于單純fork
后的情況不進行說明。
- 編寫
main.c
。此處捕獲SIGCHLD
自定義handle
處理方式。當main
函數執行后,將fork
子進程,同時子進程會exec
加載son
可執行程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>void handler(int sig) {int status;int pid = waitpid(-1, &status, WNOHANG);if (pid > 0) {printf(">>>>>>>>>>>>>>>A child process has exited, pid: %d\n", pid);}
}int main() {// 設置信號處理函數signal(SIGCHLD, handler);pid_t pid = fork();if (pid == -1) {// fork失敗perror("fork");exit(EXIT_FAILURE);} else if (pid == 0) {// 子進程sleep(3);execlp("./son", "son", NULL);} else {// 父進程繼續執行,不等待子進程while(1) {printf("Parent process, PID: %d, son PID: %d\n", getpid(), pid);sleep(3);}}return 0;
}
執行 gcc main.c -o main
進行編譯
- 編寫
son.c
,調用fork
創建孫子,孫子進程創建后會exec
加載grandson
可執行程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t pid = fork();if (pid == -1) {// fork失敗perror("fork");exit(EXIT_FAILURE);} else if (pid == 0) {// 子進程sleep(3);execlp("./grandson", "grandson", NULL);} else {// 父進程繼續執行,不等待子進程while(1) {printf("son process, PID: %d, grandson PID: %d\n", getpid(), pid);sleep(3);}}return 0;
}
執行 gcc son.c -o son
進行編譯
- 編寫
grandson.c
,代碼中定期輸出語句,類比執行業務代碼。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{while(1){printf("I am grandson, running!..., pid=%d\n", getpid());sleep(3);}return 0;
}
執行 gcc grandson.c -o grandson
進行編譯
2.2 代碼執行
輸入 ./main 執行程序,首先會觀察到有兩個 ./main程序,業務此時fork了
三秒后500721號進程會執行exec,替換為son程序。son程序加載后會fork一次,故此時有兩個son進程
三秒后500770號進程會執行exec,替換為grandson程序。此時有main,son和grandson進程存在
當孫子進程退出時(執行kill -9殺死),它將會變為僵尸進程,因為它的父進程即son進程沒有調用wait,waitpid或忽略SIGCHLD信號
結論:驗證了父進程捕獲信號后自定義處理邏輯,是不繼承到子進程的。
此外,當我們將main.c的signal(SIGCHLD, handler);替換為signal(SIGCHLD, SIG_IGN);后,按上述流程執行,殺死孫子進程時,現象如下:
一旦執行殺死孫子進程,則ps檢測不到了,證明它被真正殺死,而不會陷入僵尸狀態。
結論:驗證了父進程忽略某個信號后,會繼承到子進程,子進程同樣忽略此信號。
三、linux源碼驗證
此處從源碼角度論證fork
時子進程會繼承父進程的信號處理方式,包括父進程設置信號為SIG_DFL
或SIG_IGN
或捕獲后設置自定義處理函數。
閱讀fork.c源碼可知:
copy_process函數調用copy_sighand函數©_signal函數
- 信號處理函數的繼承:在copy_process函數中,當創建新進程(線程)時,如果clone_flags中設置了CLONE_SIGHAND,則共享信號處理函數。如果沒有設置,則會復制父進程的信號處理函數。這可以通過copy_sighand函數實現,該函數通過memcpy復制信號處理動作,即sig->action。
- 信號狀態的復制:在copy_signal函數中,如果clone_flags中沒有設置CLONE_THREAD,則會為新進程分配一個新的signal_struct結構體,并且復制父進程的信號限制和一些其他的信號狀態。
四、APUE中的驗證
- 本文的結論1為:
fork
時子進程會繼承父進程的信號處理方式,包括父進程設置信號為SIG_DFL
或SIG_IGN
或捕獲后設置自定義處理函數。APUE對結論1的闡述如下:
- 本文的結論2為:
exce
時子進程會繼承父進程設置為SIG_DFL
或SIG_IGN
的信號。對于捕獲后設置自定義處理函數的信號則不繼承這個處理函數。APUE對結論2的闡述如下:
五、其他
上文探究了fork和exec對信號的繼承情況,那么對于fd的繼承情況如何呢?有待后續探索…
ref:
https://www.cnblogs.com/yiyide266/p/13706799.html
《UNIX環境高級編程》