一,信号是如何被捕捉的
二,sigaction函数
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
![](https://img-blog.csdnimg.cn/direct/a9bbb64a76294715b9823e26896e9b39.png)
上图是
struct sigaction中的成员
sa_handler是处理信号的方法
sa_mask是信号堵塞表(当正在处理一个信号时,系统又向该进程发送了一个信号,如果该信号在这个表中,进程会处理完当前信号,然后再处理新发送过来的信号)
返回值:成功则返回0,失败返回-1,并设置相关错误码
下面是sigaction在代码中的具体使用
#include <iostream>
#include <csignal>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
void handler(int signo)
{
cout << signo << endl;
int c=0;
while (true)
{
sigset_t set;
sigpending(&set);
for (int i = 0; i <= 31; i++)
{
if (sigismember(&set, i) == 1)
cout << 1;
else
cout << 0;
}
cout<<getpid()<<endl;
c++;
if(c==10)
{
break;
}
sleep(1);
}
}
int main()
{
cout<<getpid()<<endl;
struct sigaction sig;
sig.sa_flags = 0;
sigemptyset(&(sig.sa_mask));
sigaddset(&sig.sa_mask,3);
sig.sa_handler = handler;
int n = sigaction(2, &sig, nullptr);
while(true)
{
sleep(1);
}
return 0;
}
三,可重入函数
-
main 函数调用 insert 函数向一个链表 head 中插入节点 node1, 插入操作分为两步 , 刚做完第一步的 时候 , 因 为硬件中断使进程切换到内核, 再次回用户态之前检查到有信号待处理 , 于是切换 到 sighandler 函 数,sighandler 也调用 insert 函数向同一个链表 head 中插入节点 node2, 插入操作的 两步都做完之后从 sighandler返回内核态 , 再次回到用户态就从 main 函数调用的 insert 函数中继续 往下执行 , 先前做第一步 之后被打断, 现在继续做完第二步。结果是 ,main 函数和 sighandler 先后 向链表中插入两个节点 , 而最后只 有一个节点真正插入链表中了
-
像上例这样 ,insert 函数被不同的控制流程调用 , 有可能在第一次调用还没返回时就再次进入该函数 , 这称 为重入,insert 函数访问一个全局链表 , 有可能因为重入而造成错乱 , 像这样的函数称为 可重入函数 , 反之 , 如果一个函数只访问自己的局部变量或参数, 则称为可重入 (Reentrant) 函数想一下 , 为什么两个不同的 控制流程调用同一个函数, 访问它的同一个局部变量或参数就不会造成错乱 ?
如果一个函数符合以下条件之一则是不可重入的
:
-
调用了 malloc 或 free, 因为 malloc 也是用全局链表来管理堆的
-
调用了标准 I/O 库函数。标准 I/O 库的很多实现都以不可重入的方式使用全局数据结构