SIGCHLD信號

1SIGCHLD信號產生的條件

1.子進程終止時會向父進程發送SIGCHLD信號,告知父進程回收自己,但該信號的默認處理動作為忽略,因此父進程仍然不會去回收子進程,需要捕捉處理實現子進程的回收;

2.子進程接收到SIGSTOP(19)信號停止時;

3.子進程處在停止態,接受到SIGCONT后喚醒時。

綜上:子進程結束、接收到SIGSTOP停止(掛起)和接收到SIGCONT喚醒時都會向父進程發送SIGCHLD信號。父進程可以捕捉該信號,來實現對子進程的回收,或者了解子進程所處的狀態。

2)借助SIGCHLD信號回收子進程

子進程結束運行,其父進程會收到SIGCHLD信號。該信號的默認處理動作是忽略。可以捕捉該信號,在捕捉函數中完成子進程狀態的回收。

//分析例子:結合 17)SIGCHLD 信號默認動作,掌握父使用捕捉函數回收子進程的方法

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>void sys_err(char *str)
{perror(str);exit(1);
}void do_sig_child(int signo)
{int status;pid_t pid;//    if ((pid = waitpid(0, &status, WNOHANG)) > 0) {while ((pid = waitpid(0, &status, WNOHANG)) > 0) {if (WIFEXITED(status))printf("------------child %d exit with %d\n", pid, WEXITSTATUS(status));else if (WIFSIGNALED(status))printf("child %d killed by the %dth signal\n", pid, WTERMSIG(status));}
}int main(void)
{pid_t pid;int i;//阻塞SIGCHLDfor (i = 0; i < 10; i++) {if ((pid = fork()) == 0)break;else if (pid < 0)sys_err("fork");}if (pid == 0) {     //10個子進程int n = 1;while (n--) {printf("child ID %d\n", getpid());sleep(1);}return i+1;      //子進程結束狀態依次為1、2、??????、10} else if (pid > 0) {   struct sigaction act;act.sa_handler = do_sig_child;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGCHLD, &act, NULL);//解除對SIGCHLD的阻塞while (1) {printf("Parent ID %d\n", getpid());sleep(1);}}return 0;
}

[root@localhost 01_signal_test]# ./sigchild

child ID 11129

child ID 11130

child ID 11131

child ID 11132

child ID 11133

child ID 11134

child ID 11135

child ID 11136

child ID 11137

Parent ID 11128? //表明在此時父進程肯定完成了對信號的注冊工作

child ID 11138

------------child 11129 exit with 1

------------child 11130 exit with 2

------------child 11132 exit with 4

------------child 11133 exit with 5

------------child 11131 exit with 3

Parent ID 11128

------------child 11134 exit with 6

------------child 11137 exit with 9

Parent ID 11128

------------child 11136 exit with 8

Parent ID 11128

------------child 11135 exit with 7

Parent ID 11128

------------child 11138 exit with 10 //可見父進程的確完成了對全部子進程的回收

Parent ID 11128

Parent ID 11128

分析如下:

1.在上述程序中,每個子進程在結束之前都會睡眠1s,這是為了留出足夠的時間保證在所有子進程結束之前,父進程能夠完成對SIGCHLD信號的注冊。否則所有子進程都結束了,父進程還沒有完成注冊,則此時信號都被忽略,從而子進程不能被父進程回收。但是,只要有一個子進程在信號注冊后結束,所有子進程都可以被回收,因為使用了while循環回收子進程。除了使用sleep函數來實現這一點外,其實更加高效而又精確的辦法(其實在負載較大的 情況下,sleep函數也不能確保能夠做到)就是在父進程注冊函數之前就將SIGCHLD信號設置為阻塞(通過信號集操作函數加入到屏蔽字中),在注冊完成時立即解除對該信號的阻塞即可。

2.不可以將捕捉函數內部的while替換為if。因為在執行捕捉函數期間,發送了多次SIGCHLD信號,未決信號集只是記錄了一次,因此下一次再調用捕捉函數時,if只能完成對一個子進程的回收(即使有多個子進程都發了信號,但是只是調用一次捕捉函數)。而while循環則可以對所有結束了的子進程都完成回收。因此對于多個子進程的回收,最好采用循環的方式,不采用if。

3.之前在管道進程間通信,父子進程實現ls | wc -l時(父進程exec函數族執行ls,子進程exec執行wc -l),父進程無法完成對子進程的回收,因為父進程執行exec函數族就離開了,不能回來。但是現在可以利用信號捕捉實現,在父進程調用exec函數族之前就注冊SIGCHLD的捕捉函數,但是需要注意以下幾點:1.仍然要考慮對SIGCHLD信號的屏蔽和解除屏蔽操作;2.要考慮子進程向父進程發送信號時,父進程已經結束了,此時父進程仍然無法對子進程回收,可以讓父進程睡眠等待,讓其后結束(但這在實際中是沒有意義的,父進程結束了,子進程變為孤兒進程,會被init進程回收,因此實際編程不需要考慮這一點);3.在ls | wc -l這個例子中,由于使用了標準輸出的重定向(dup2),因此用戶處理函數輸出的回收狀態信息不能顯示在屏幕上,因此在用戶處理函數中還需要再次進行重定向,恢復輸出重定向為屏幕。

總結:

1.子進程繼承了父進程的信號屏蔽字和信號處理動作,但子進程沒有繼承未決信號集spending。

2.注意注冊信號捕捉函數的位置。

3.應該在fork之前,阻塞SIGCHLD信號。注冊完捕捉函數后解除阻塞。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/385317.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/385317.shtml
英文地址,請注明出處:http://en.pswp.cn/news/385317.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

信號傳參

&#xff08;1&#xff09;發送信號傳參 前面已經知道從一個進程向另一個進程發送信號可以使用kill函數&#xff0c;但是kill函數在向進程發送信號的時候不能攜帶除了信號以外的其他信息&#xff0c;這時可以使用與kill相對應的sigqueue函數&#xff0c;該函數也是向一個進程發…

【Leetcode | 52】257. 二叉樹的所有路徑

給定一個二叉樹&#xff0c;返回所有從根節點到葉子節點的路徑。 說明: 葉子節點是指沒有子節點的節點。 示例: 輸入: 1 / \ 2 3 \ 5 輸出: ["1->2->5", "1->3"] 解釋: 所有根節點到葉子節點的路徑為: 1->2->5, 1->3 解法一&a…

623. 在二叉樹中增加一行

給定一個二叉樹&#xff0c;根節點為第1層&#xff0c;深度為 1。在其第 d 層追加一行值為 v 的節點。 添加規則&#xff1a;給定一個深度值 d &#xff08;正整數&#xff09;&#xff0c;針對深度為 d-1 層的每一非空節點 N&#xff0c;為 N 創建兩個值為 v 的左子樹和右子樹…

終端的概念

操作系統接口&#xff1a;用戶接口和程序接口。用戶接口分為聯機用戶接口和脫機用戶接口。脫機用戶接口出現在早期的批處理系統中&#xff08;將作業提前交給操作系統&#xff0c;作業完成的過程中用戶無法交互&#xff09;&#xff1b;聯機用戶接口即為終端&#xff08;所有輸…

終端的啟動流程

在Linux操作系統啟動時&#xff0c;首先加載的進程就是init進程&#xff08;ID為1&#xff09;&#xff0c;其余進程都是init進程產生的&#xff08;fork&#xff0c;然后exec金蟬脫殼&#xff09;&#xff0c;因此系統中所有進程都可以看成是init進程的子孫進程。可以通過ps a…

進程組(作業)

&#xff08;1&#xff09;概念和特性 進程組&#xff0c;也稱之為作業。BSD于1980年前后向Unix中增加的一個新特性。代表一個或多個進程的集合。每個進程都屬于一個進程組。在waitpid函數和kill函數的參數中都曾使用到。操作系統設計的進程組的概念&#xff0c;是為了簡化對多…

437. 路徑總和 III

給定一個二叉樹&#xff0c;它的每個結點都存放著一個整數值。 找出路徑和等于給定數值的路徑總數。 路徑不需要從根節點開始&#xff0c;也不需要在葉子節點結束&#xff0c;但是路徑方向必須是向下的&#xff08;只能從父節點到子節點&#xff09;。 二叉樹不超過1000個節…

會話(session)

一組進程形成一個進程組&#xff0c;一組進程組形成一個會話&#xff0c;即一個會話中可以包括多個進程組。 &#xff08;1&#xff09;創建會話 創建一個會話需要注意以下6點注意事項&#xff1a;1.調用進程不能是進程組組長&#xff08;不能是父進程&#xff09;&#xff0…

508. 出現次數最多的子樹元素和

給出二叉樹的根&#xff0c;找出出現次數最多的子樹元素和。一個結點的子樹元素和定義為以該結點為根的二叉樹上所有結點的元素之和&#xff08;包括結點本身&#xff09;。然后求出出現次數最多的子樹元素和。如果有多個元素出現的次數相同&#xff0c;返回所有出現次數最多的…

1003 我要通過!(20)(20 分)

“答案正確”是自動判題系統給出的最令人歡喜的回復。本題屬于PAT的“答案正確”大派送 —— 只要讀入的字符串滿足下列條件&#xff0c;系統就輸出“答案正確”&#xff0c;否則輸出“答案錯誤”。 得到“答案正確”的條件是&#xff1a; 1. 字符串中必須僅有P, A, T這三種字符…

網絡終端

虛擬終端或串口終端的數目是有限的&#xff0c;虛擬終端&#xff08;字符控制終端&#xff09;一般就是/dev/tty1~/dev/tty6六個&#xff0c;串口終端的數目也不超過串口的數目。然而網絡終端或圖形終端窗口的數目卻是不受限制的&#xff0c;這是通過偽終端&#xff08;Pseudo…

線程的概念

線程&#xff08;LWP&#xff0c;light weight process&#xff09;是輕量級的進程&#xff0c;本質仍是進程&#xff08;在類unix環境下&#xff09;。進程有獨立地址空間&#xff0c;擁有PCB&#xff1b;線程也有PCB&#xff0c;但沒有獨立的地址空間&#xff08;共享&#x…

1001. 害死人不償命的(3n+1)猜想 (15)

卡拉茲(Callatz)猜想&#xff1a; 對任何一個自然數n&#xff0c;如果它是偶數&#xff0c;那么把它砍掉一半&#xff1b;如果它是奇數&#xff0c;那么把(3n1)砍掉一半。這樣一直反復砍下去&#xff0c;最后一定在某一步得到n1。卡拉茲在1950年的世界數學家大會上公布了這個猜…

海量數據處理 (一)

現有海量日志數據保存在一個超級大的文件中&#xff0c;該文件無法直接讀入內存&#xff0c;要求從中提取某天出訪問百度次數最多的那個IP。 從這一天的日志數據中把訪問百度的IP取出來&#xff0c;逐個寫入到一個大文件中;注意到IP是32位的&#xff0c;最多有2^32個IP。同樣可…

線程控制原語之pthread_self和pthread_create函數

注意&#xff1a;使用線程庫函數用gcc編譯時&#xff0c;要加參數&#xff1a;-lpthread&#xff08;libpthread.so&#xff09;&#xff0c;因為線程庫函數屬于第三方c庫函數&#xff0c;不是標準庫函數&#xff08;/lib、/usr/lib或者/usr/local/lib&#xff09;。 &#xf…

1005. 繼續(3n+1)猜想 (25)

卡拉茲(Callatz)猜想已經在1001中給出了描述。在這個題目里&#xff0c;情況稍微有些復雜。 當我們驗證卡拉茲猜想的時候&#xff0c;為了避免重復計算&#xff0c;可以記錄下遞推過程中遇到的每一個數。例如對n3進行驗證的時候&#xff0c;我們需要計算3、5、8、4、2、1&#…

C指針深度解析

&#xff08;1&#xff09;指針的概念 指針是一種數據類型&#xff0c;而內存地址是這種數據類型具體的值&#xff08;注意區分兩者的概念&#xff09;。先說一下什么是內存地址&#xff1a;假設CPU的尋址方式是以字節尋址的&#xff0c;即每一個字節對應一個地址編號&#xf…

1007. 素數對猜想

讓我們定義 dn 為&#xff1a;dn pn1 - pn&#xff0c;其中 pi 是第i個素數。顯然有 d11 且對于n>1有 dn 是偶數。“素數對猜想”認為“存在無窮多對相鄰且差為2的素數”。 現給定任意正整數N (< 105)&#xff0c;請計算不超過N的滿足猜想的素數對的個數。 輸入格式&…

線程共享全局變量(.data和.bbs)

線程默認共享數據段、代碼段等地址空間&#xff0c;常用的是全局變量。而進程不共享全局變量&#xff0c;只能借助mmap。 //代碼示例 #include <string.h> #include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <string.h> …

1008 數組元素循環右移問題 (20)

一個數組A中存有N&#xff08;N&gt0&#xff09;個整數&#xff0c;在不允許使用另外數組的前提下&#xff0c;將每個整數循環向右移M&#xff08;M>0&#xff09;個位置&#xff0c;即將A中的數據由&#xff08;A~0~ A~1~……A~N-1~&#xff09;變換為&#xff08;A~N-…