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 中包含该信号。