linux-信号
信号是发给进程的,进程接收到信号后会有内置的对应信号的处理行为。
信号是操作系统发给进程的,用户只是让操作系统发给进程。
进程必须要有识别信号,并且处理对应信号的能力。
即使没有收到信号,也需要直到该信号的处理方法。
在进程收到信号时,也许并不适合立即处理该信号,会把这个信号保存起来,合适的时候再进行处理。
并不适合立即处理该信号也就是进程正处于内核态,内核态拥有访问操作系统内部的权限,在进程地址空间当中3gb到4gb的这一个gb空间是属于内核空间,只有内核态才能访问,而内核态和用户态是由cpu当中的一个寄存器来决定的。
在进行系统调用这些操作就会进入内核态。
也就是说,在信号产生时到信号被处理之前,会有时间窗口,进程具有分辨哪些信号被处理了的能力。
自定义信号处理
用户也可以将信号处理的动作自定义,让这个信号的处理按照自己定义的来执行。
前台进程和后台进程
前台进程就是接收键盘数据的进程,每一个中断都会分配一个bash,每一个终端前台进程只有一个。
执行信号的处理动作被称为信号递达。
信号从产生到递达之间的状态,称为信号未决。
进程可以选择阻塞某个信号,被阻塞的信号会被保持在未决状态,不会被处理,直到进程解除了对该信号的阻塞,才执行递达的动作。
阻塞是信号不会递达,忽略是信号处理的一个选项。
信号捕捉
在信号被处理的时候,该信号会被屏蔽,这是为了同一信号在处理期间不会再被调用。在该信号处理完毕后会自动解除屏蔽。
signal
这个系统调用可以将原本的信号捕捉处理覆盖,变成用户设置的自定义处理方式。
但并不是所有的信号都可以被捕捉的
第一个参数signum是一个整数,信号实际上就是整数定义的宏。
左边的顺序编号实际上就是这些宏定义信号实际的整数。
第二个参数是一个参数为int返回值为void的函数指针。
也可以输入宏SIG_IGN忽略这个信号 SIF_DFL默认信号处理方式
sigaction和signal一样是信号捕捉的系统调用
第一个参数和signal一样是要捕捉信号对应的整数。
第二个参数是一个数据结构
这个数据结构一般只需要关注两个变量
一个是以int为参数,返回值为void的函数指针,和signal一样,是信号的处理方法。
还有一个是sigset_t位图类型的结构体变量,这个参数的作用是在处理该信号时,会将该结构体变量当中置1的信号一起屏蔽。
第三个参数是输出型参数,会保存之前的sigaction结构。
信号产生方式
键盘产生
指令产生
系统调用
让指定进程执行对应信号处理方式
第一个参数是进程pid,第二个参数是信号对应整数
让调用的进程执行对应信号处理方式
sig是信号对应的整数
让调用进程被中断 。
闹钟
这是一个系统调用,参数是整数,作用是以秒为单位,输入参数秒后对进程发送一个14号信号。
返回参数是调用这个系统调用时上一个alarm调用还有几秒发送信号。
进程等待的core dump
在父进程等待子进程,接收到返回数据时,除了错误码和终止信号,还有core dump,这是一个在传入参数的第8个比特位,代表的意思是收到的终止信号是错误信号还是常规的信号,错误信号就把这个比特位置为1,不是就置为0.
但是core dump信号还需要打开。
如果是错误信号,就会在进程运行的当前目录建立一个文件,core.进程pid,使用 core-file调用这个文件,能直接定位到出错行。
信号保存
信号实际上是通过进程指向的指针来完成的,block是一个位图结构,0表示没有屏蔽该信号,1表示屏蔽了该信号。
屏蔽信号也就是这个信号不会被处理。
pending也是一个位图结构,0是信号已被处理,1是信号未处理。
handler是一个函数指针数组,指向着不同的处理方法。
操作系统提供了一系列的系统调用来供用户使用。
#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);
sigset是一个位图结构
sigemptyse 可以将指针指向的sigset类型的pending全部清零
函数sigfillset可以将指针指向的pending全部置一。
sigaddset可以将指定的位图结构当中指定信号位置为1.
sigdelset可以将指定位图结构当中指定信号位置为0.
sigismember可以对指定位图结构当中指定信号位进行判断,有信号返回1,没信号返回0
第一个参数是位图结构,第二个参数是指定信号
sigpending可以将pending表当中的位图结构拷贝一份出来
set是一个输出型参数,将用户自定义的位图结构传进去,会将pending位图结构拷贝一份出来。
sigprocmask的参数有三个。
第一个参数是一个整数,定义了三个宏,第一个宏是将传入的位图结构进行或运算。第二个是将传入的位图结构取反后与原本的位图进行与运算。第三个宏是直接将传入的位图结构替换原本的位图结构。
第二个参数是一个位图结构,将自己定义好的位图结构传入进行第一个参数对应的运算
第三个参数是一个输出型参数,会保存进行运算之前的位图结构
从这些系统调用可以看出,操作系统并不希望用户直接拿到可以修改这些位图结构的地址,而是通过系统调用将位图结构拷贝一份出来,可以让用户知道位图结构的参数。要修改位图的话也是将位于栈区的用户自己创建的位图结构传过去,根据这个为统一结构进行修改。
而不能及时处理信号的状态就是内核态,在调用系统调用等一系列的操作时,会从用户态转进内核态,在内核态转换成用户态之前会检查一遍信号,如果有需要处理的信号那就直接处理掉。
sigchild
在子进程退出时,也会对父进程发送一个信号,这个信号是17.默认对这个信号的处理方式是忽略。
可以通过捕捉这个信号,在收到这个信号时,使用进程等待非阻塞轮询来回收子进程。