目录
进程什么时候检测处理信号?以及内核如何实现信号的捕捉?
sigaction
volatile
信号由操作系统发送给相应的进程,进程保存信号,最后再捕捉处理信号。
进程什么时候检测处理信号?以及内核如何实现信号的捕捉?
操作系统执行状态由内核态返回到用户态的时候,进行信号的检测和处理!
内核态和用户态是操作系统中两种不同的执行状态,限制不同程序的访问权限。由于系统调用是内核的数据,因此OS要切换到内核态(CPU内部的esc寄存器也会切换到内核态),两者配合这管理运行该程序。
上图是信号被检测处理和捕捉的示意图。
我们对于该示意图有如下分析:
如果信号的处理动作是用户自定义函数,
在信号递达时就调用这个函数,
这称为
捕捉信号
。由于信号处理函数的代码是在用户空间的,
处理过程比较复杂,
举例如下:
用户程序定义了
SIGQUIT
信号的处理函
数sighandler
。当前正在执行main
函数,
这时如果碰到系统调用(这里最常见)、中断或异常OS切换到内核态。在系统调用完毕后要返回用户态的
main
函数之前检查到有信号SIGQUIT
递达。内核决定返回用户态后不是恢复
main
函数的上下文继续执行,
而是执行
sighandler
函数
,sighandler和
main
函数使用不同的堆栈空间
,
它们之间不存在调用和被调用的关系,
是两个独立的控制流程。
sighandler
函数返回后自动执行特殊的系统调用
sigreturn
再次进入内核态。如果没有新的信号要递达
,
这次再返回用户态就是恢复main
函数的上下文继续执行了。
sigaction
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
头文件:<signal.h>
功能:sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回-1。
参数:signo 是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体。
struct sigaction结构体:
volatile
今天我们站在信号的角度重新理解一下该关键字:
#include <stdio.h>
#include <signal.h>
int flag = 0;
void handler(int sig)
{
printf("chage flag 0 to 1\n");
flag = 1;//在优化条件下, flag变量可能被直接优化到CPU内的寄存器中
}
int main()
{
signal(2, handler);
while(!flag);
printf("process quit normal\n");
return 0;
}
标准情况下,键入 CTRL-C ,2号信号被捕捉,执行自定义动作,修改 flag=1 , while 条件不满足,退出循环,进程退出。
makefile里的形成可执行文件时:gcc -o signal signal.c -O1/2/3,优化flag变量
volatile 作用:保持内存的可见性,告知编译器,被该关键字修饰的变量,不允许被优化,对该变量的任何操作,都必须在真实的内存中进行操作。