信号产生
1、终端产生
SIGINT(2号信号)默认动作就是终止信号,SIGQUIT(3号)终止进程并且Core Dump(核心转储)
ctrl+c也就是给进程发2号信号
一个进程只有一个前台信号,可以有多个后台信号
ctrl+c给前台进程发信号,所以无法终止后台信号
Core Dump是什么?
进程退出时,status为位图中存放Core Dump的标志,1表示发生核心转储
2、调用系统函数向进程发信号
kill -l可以查看所有信号
kill给指定进程发信号,raise给自己发信号
3、由软件条件产生信号
4、硬件异常产生信号
硬件异常被硬件以某种方式检测到并通知内核,内核向当前进程发出适当信号
如下就是除0错误导致的CPU运算单元造成的异常,内核将这种异常解释为SIGFPE(8号信号)发送给进程
阻塞信号
信号常见的相关概念
信号递达:实际执行信号的处理动作
信号未决:信号产生与递达之间状态
进程可以选择阻塞信号
被阻塞的信号保持在未决状态,直到进程解除对此信号的阻塞,该信号才能够递达
信号处理
对信号的处理方式
1、忽略信号
2、执行信号默认处理动作
3、要求内核在处理信号时转换到用户态来处理,这种方式被称为信号捕捉(后面会提到)
信号在什么时候处理?
当进程从内核态返回用户态,进行信号检测处理
内核态:允许访问操作系统代码和数据
用户态:只能访问自己的代码和数据
信号在内核中
block和pending表是位图,handler是函数指针表
pending:1表示进程收到信号,0没有收到
block:1表示阻塞,0没有阻塞
handler:处理信号方式
pending什么时候从1变0?
执行信号捕捉方法之前,先变0,然后调用
sigset_t信号集表示对block和pending表做处理
sigset_t类型对于每种信号用一个比特位表示有效还是无效
信号集操作函数
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
sigemptyset函数初始化信号集set所有信号对应的比特位为0,表示信号集不包含任何信号
sigfiillset函数初始化信号集set所有信号对应的比特位为1,表示信号集包含所有信号
sigaddset函数使信号集signo信号对应的比特位变为1
sigdelset函数使信号集signo信号对应的比特位变为0
sigprocmask
读取或修改信号阻塞集合集
how
参数用于指定对信号屏蔽字的操作:SIG_BLOCK
:将set
中指定的信号添加到当前信号屏蔽字中。SIG_UNBLOCK
:从当前信号屏蔽字中移除set
中指定的信号。SIG_SETMASK
:用set
中的信号集合替换当前的信号屏蔽字。
oldset
参数是一个指向sigset_t
类型的指针,用于存储调用该函数之前的信号屏蔽字。- 返回值成功0,失败-1
sigpending
获取未决信号集
返回值成功0,失败-1
信号捕捉
sigaction函数
获取修改信号的处理方式
signum参数是要设置信号处理程序的信号的编号,可以是任何有效的信号编号,如 SIGINT
、SIGTERM
等。
struct sigaction只需要注意函数指针handler和sa_mask这两个就行
handler:跟signal一样都是信号的处理方式
sa_mask:阻塞信号集,执行期间阻塞其他信号,防止其它信号中断当前信号处理程序的执行。
oldset:储存之前信号处理方式
struct sigaction {
void (*sa_handler)(int); // 指向信号处理函数的指针
void (*sa_sigaction)(int, siginfo_t *, void *); // 用于指定信号处理函数的另一种形式,通常与 SA_SIGINFO 标志一起使用
sigset_t sa_mask; // 用于阻塞的信号集合,即在执行信号处理函数期间要阻塞的其他信号
int sa_flags; // 用于指定信号处理的标志
void (*sa_restorer)(void); // 废弃字段,在一些旧的系统中可能有作用
};
signal函数
两者之间的区别就在于sigaction更为灵活和安全,能够跟细致的对信号进行处理
volatile关键字
用来告诉编译器,变量的值可能会被程序之外的因素改变,因此编译器在优化时不能假设该变量的值不会突然改变。
volatile int flag=0;
void handler(int sig)
{
printf("chage flag 0 to 1\n");
flag = 1;
}
int main()
{
signal(2, handler);
while(!flag);
printf("process quit normal\n");
return 0;
}
上述代码中如果没有volatile,那么while判断可能会被优化,flag变量可能直接被优化到CUP寄存器中
这导致在收到信号2之后,handler函数处理时改变的flag,不会影响while判断中flag是0,所以出现如下情况
加上volatile之后,就不会优化,保证外部因素可以改变flag