一、信号的概念
1.信号:就是通知别人发生了某件事情。
在生活中,我们会订外卖,当配送员打电话告诉我们外卖到了的时候实际上就是在向我们发信号。
操作系统中的信号是在某件事情发生时操作系统对进程的通知机制,也可以叫做软件中断。与硬件中断不同的是,硬件中断程序会直接崩溃,而软件中断(例如死循环)时,操作系统会发现到这个异常,并向产生中断的进程发信号,进程从用户态切换为内核态去处理异常。
2.操纵系统的信号列表:用kill-l命令查看
普通信号(1-31)
实时信号(34-64)
二、信号的产生
信号的产生对于进程来说是异步的。
1.通过键盘产生:用户在终端按下了能够产生信号的组合键.比如Ctrl-C(中断SIGQUIT信号),Ctrl-Z(暂停SIGTSTP信号)。
2.硬件异常产生信号:硬件检测到异常并通知内核,内核向产生异常进程发对应的信号。
1>进程中出现了除0的错误或者是野指针的问题,进程进行除0指令时,CPU的运算单元产生异常,操作系统发现异常后就向进程发SIHGFPE;
2>出现野指针的问题时,越界访问,MMU无法将虚拟内存映射到物理内存上,操作系统就会向该进程发SIGSEGV信号。
3.调用系统函数kill发信号:
int kill(pid_t pid,int signo);//给指定进程发指定信号
int raise(int signo);//自己给自己发指定信号
4.软件条件产生信号:
1>在管道中,假设父进程是读端,子进程是写端,正常情况下应该是子进程一直在写,父进程一直在读,但是如果父进程不但不读了,还把自己的读端关闭了,这时操作系统就会向该进程发送SIGPIPE信号。
2>alarm函数设定闹钟,定时器到期,内核secnds秒后向当前进程发送SIGALRM信号,默认终止当前进程。
unsigned int alarm(unsigned int seconds)
注意:有一个错误的观点是进程出bug了,操作系统发现了异常向该进程发送了信号,因此操作系统产生了信号。
三、信号的处理
处理信号的前提是要认识信号,进程收到信号时,一般不会立即去处理,而是把信号记录起来,在适当的时候处理。信号的三种处理方式:
1.忽略信号不去执行
2.执行该信号的默认处理动作,一般是结束进程,系统崩溃。
3.执行自定义动作,在内核态处理信号时切换到用户态执行自定义处理函数,称为捕捉信号。
三、阻塞信号
1.信号递达:实际处理信号的动作。
信号未决:信号已经产生,但没有处理。(pending)
阻塞信号:相当于放了一堵墙,即使产生阻塞信号也不会被递达。
2.信号表示:通过位图表示
每个信号有两个标志位:1>block:该信号是否被屏蔽。
2>pending:是否收到信号。
handler:函数指针数组表示处理动作。
信号未被屏蔽,没有收到信号,不会递达;收到信号,会递达,执行handler处理动作。
信号被屏蔽,没有收到信号,不会递达;收到信号,也不会递达,当屏蔽解除时,信号递达,执行handler处理动作。(若在解除屏蔽之前此信号产生多次,普通信号只递达一次,处理一次,实时信号会放进一个队列里,多次递达)
3.信号屏蔽字:
1>每个信号的两个标志位只有一个未决标志(0和1),只表示有效和无效,不记录信号产生多少次。因此这两个标志位(pending和block)用相同的数据类型sigset_t(信号集)存储,表示是否收到信号,信号是否被阻塞,可以把信号集认为是位图。
2>阻塞信号集:称为信号屏蔽字,它实际上是内核中我们看到的block表 , 类型是sigset_t。
四、信号的捕捉过程
1.信号处理的时机:从内核态切换为用户态时需要做信号处理.
2.如果信号的处理动作是忽略,就将其pending置零.
3.如果信号的处理动作是默认,一般情况下都是终止该进程,杀死信号并且退出.
4.下图为信号处理动作是自定义函数时信号的捕捉过程: