sigaction:修改信号对应的handler方法
act输入型参数,oldact输出型参数
void (*sa_handler) (int) //修改的自定义函数
sigset_t sa_mask //
void handler(int signo)
{
cout<<"catch a signal, signal number: "<<signo<<endl;
}
int main()
{
// 创建结构体变量
struct sigaction act,oact;
//清空
memset(&act,0,sizeof(act));
memset(&oact,0,sizeof(oact));
//设置自定义方法
act.sa_handler=handler;
sigaction(2,&act,&oact);
while(true)
{
cout<<" I am a process: "<<getpid()<<endl;
sleep(1);
}
return 0;
}
信号递达时,pending位图什么时候变成0的,是在调用函数前还是调用函数后
void PrintPending()
{
sigset_t set;
//获得pending位图
sigpending(&set);
for(int signo=1;signo<=31;signo++)
{
if(sigismember(&set,signo)) cout<<"1";
else cout<<" 0 ";
}
cout<<"\n";
}
void handler(int signo)
{
PrintPending();
cout<<"catch a signal, signal number: "<<signo<<endl;
}
在执行信号捕捉方法之前先清零再调用
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时,才恢复原来的信号屏蔽。通俗点就是调用处理函数时,block表的值为1,屏蔽相同信号的递达。
void handler(int signo)
{
cout<<"function start";
//一直在调用自定义函数
while(true)
{//PrintPending();
cout<<"catch a signal, signal number: "<<signo<<endl;
}
}
没有function start只打印了一次
sigset_t 信号屏蔽:调用自定义函数时同时屏蔽其他信号
//初始化
sigemptyset(&act.sa_mask);
//屏蔽信号1,2,3,4
sigemptyset(&act.sa_mask,1);
sigemptyset(&act.sa_mask,2);
sigemptyset(&act.sa_mask,3);
sigemptyset(&act.sa_mask,4);
可重入函数的问题
main执行流进入某个函数时,接收到信号调用同一个函数,此时函数被进入了两次。
当insert的函数时执行到head=p时,又调用了sighandler函数p->next=head代码。
当执行完新调用的代码后才会执行head=p。
可能导致内存泄漏问题
如果一个函数被重复进入出错了,或者可能出错,不可重入函数,否则叫可重入函数。main函数和signal hander函数也有自己的栈帧。
volatile 关键字:保证从内存而不是从寄存器中获取变量
int flag=0;
void handler(int signo)
{
cout<<"catch a signal: "<<signo<<endl;
flag=1;
}
int main()
{
signal(2,handler);
while(!flag);
cout<<"process quit normal"<<endl;
return 0;
}
尽管调用了handler函数,但是程序依然死循环
main和handler是不同的栈帧
cpu扫描main栈帧发现flag没有被修改,直接放在寄存器中,且从寄存器中读取,不再每次从内存中读取。
读取变量到寄存器,进行逻辑检测
而hander执行流只能修改内存中的flag值,而cpu不会从内存中读取。
volatile int flag=0 // 防止编译器过渡优化,保存内存可见性!
基于信号捕捉来进行进程回收
子进程在退出的时候,会主动向父进程发送信号17
子进程在进行等待的时候,我们可以采用基于信号的方式等待
进程等待的好处:
获取子进程的退出状态,释放子进程僵尸态
虽然不知道父子谁运行,但是父进程一定是最后退出的
void handler(int signo)
{
pid_t rid=waitpid(-1,nullptr,0); //接收17信号,回收进程
cout<<"I am process: "<<getpid()<<"catch a signo: "<<signo<<endl;
}
int main()
{
signal(17,handler);
pid_t id=fork();
if(id==0)
{
while(true)
{
cout<<"I am child process: "<<getpid()<<" , ppid: "<<getppid()<<endl;
sleep(1);
break;
}
exit(0);
}
while(true)
{
cout<<" I am father process: "<<getpid()<<endl;
sleep(1);
}
}
当有多个子进程利用信号回收
void handler(int signo)
{
sleep(5);
pid_t rid;
// rid的值为0 说明进程全部回收,rid的值大于0为回收进程的pid
// 轮询回收,防止被某个进程回收卡住
while((rid==waitpid(-1,nullptr,WNOHANG))>0){
cout<<"I am process: "<<getpid()<<"catch a signo: "<<signo<<endl;
}
}
不产生僵尸进程,将sigaction将SIGCHLD的处理动作设置为SIG_IGN或者将signal(17,SIG_IGN)
显性忽略信号17,子进程将不产生僵尸进程