SIGCHLD 信號
SIGCHLD 的產生條件
- 子進程終止時
- 子進程接收到 SIGSTOP 信號停止時
- 子進程處在停止態,接受到 SIGCONT 后喚醒時
借助 SIGCHLD 信號回收子進程
子進程結束運行,其父進程會收到 SIGCHLD 信號。該信號的默認處理動作是忽略。可以捕捉該信號,在捕捉函 數中完成子進程狀態的回收。
linux系統根據未決信號集來處理信號,多個信號進入未決信號集只處理一次。
#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;//進程ID//status子進程退出狀態while((pid = waitpid(0,&status,WNOHANG))>0){if(WIFEXITED(status))printf("--------child %d exit %d\n",pid,WEXITSTATUS(status));else if(WIFSIGNALED(status))printf("child %d cancel signal %d\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){int n=1;while(n--){printf("child ID %d\n",getpid());sleep(1);}return i+1;}else if(pid >0 ){//有可能再注冊信號過程中子進程死亡,所以要先對SIGCHILD信號進程阻塞//SIGCHILD阻塞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;
}
子進程結束 status 處理方式
pid_t waitpid(pid_t pid,int* status,int options)
- options
WNOHANG
沒有子進程結束,立即返回
WUNTRACED
如果子進程由于被停止產生的 SIGCHLD,waitpid 則立即返回
WCONTINUED
如果子進程由于被 SIGCONT 喚醒而產生的 SIGCHLD,waitpid 則立即返回 - 獲取 status
WIFEXITED(status)
子進程正常 exit 終止,返回真
WEXITSTATUS(status)返回子進程正常退出值
WIFSIGNALED(status)
子進程被信號終止,返回真
WTERMSIG(status)
返回終止子進程的信號值
WIFSTOPPED(status) 子進程被停止,返回真
WSTOPSIG(status)返回停止子進程的信號值 WIFCONTINUED(status)
SIGCHLD 注意
- 子進程繼承了父進程的信號屏蔽字和信號處理動作,但子進程沒有繼承未決信號集 spending。
- 注意注冊信號捕捉函數的位置。
- 應該在 fork 之前,阻塞 SIGCHLD 信號。注冊完捕捉函數后解除阻塞。
信號傳參
發送信號傳參
sigqueue 函數對應 kill 函數,但可在向指定進程發送信號的同時攜帶參數 int sigqueue(pid_t pid,int sig,const union sigvalvalue);
成功:0;失敗:-1,設置
errno union sigval
{ int sival_int;void*sival_ptr;//這個地址給本進程使用
};
向指定進程發送指定信號的同時,攜帶數據。但,如傳地址,需注意,不同進程之間虛擬地址空間各自獨立, 將當前進程地址傳遞給另一進程沒有實際意義。
捕捉函數傳參
int sigaction (int signum,const struct sigaction*act,struct sigaction*oldact);structsigaction{ void (*sa_handler)(int); void (*sa_sigaction)(int,siginfo_t*,void*); sigset_t sa_mask;int sa_flags; void (*sa_restorer)(void);};
當注冊信號捕捉函數,希望獲取更多信號相關信息,不應使用 sa_handler 而應該使用 sa_sigaction。但此時的 sa_flags 必須指定為 SA_SIGINFO。siginfo_t 是一個成員十分豐富的結構體類型,可以攜帶各種與信號相關的數據。
中斷系統調用
系統調用可分為兩類:慢速系統調用和其他系統調用。
慢速系統調用:
可能會使進程永遠阻塞的一類。如果在阻塞期間收到一個信號,該系統調用就被中斷,不再 繼續執行(早期);也可以設定系統調用是否重啟。如,read、write、pause、wait…
其他系統調用:
getpid、getppid、fork…
結合 pause,
回顧慢速系統調用:
慢速系統調用被中斷的相關行為,實際上就是 pause 的行為:
如,read
- 想中斷 pause,信號不能被屏蔽。
- 信號的處理方式必須是捕捉 (默認、忽略都不可以)
- 中斷后返回-1,
設置 errno 為 EINTR(表“被信號中斷”) 可修改 sa_flags 參數來設置被信號中斷后系統調用是否重啟。SA_INTERRURT 不重啟。 SA_RESTART 重啟。
sa_flags 還有很多可選參數,適用于不同情況。如:捕捉到信號后,在執行捕捉函數期間,不希望自動阻塞該 信號,可將 sa_flags 設置為 SA_NODEFER,除非 sa_mask 中包含該信號。