进程信号

信号是什么:信号就是一个软件中断,通知进程发生了某个事件,打段进程当前的操作,去处理这个事件。
信号是多种多样的,并且一个信号对应一个事件,这样才能做到收到一个信号后,直到到底是一个什么事件,应该如何处理。(但是要保证必须识别到这个信号)

信号种类:62种,1-31是非可靠信号;34-64是可靠信号。
信号周期:产生,进程种的注册,进程种的注销,捕捉处理。

信号的产生

  • 硬件:ctrl + c , ctrl + | , ctrl + z
  • 软件:
    kill命令:kill -signum pid
int kill(pid_t pid, int signum);
int raise(int signum);
void abort()
int alarm(int seconds);
//core dumped:核心转储---程序异常退出时,保存程序的运行信息,便于事后调试---默认关闭
//ulimit -c:设置核心转储文件的最大大小 ulimit -c 1024 则最大大小为1M

信号在进程中的注册
在pcb中有一个未决信号集合,pending集合,信号的注册就是指在这个pending集合中标记对应信号数值的二进制位为1。
1-31非可靠信号的注册:若信号还未注册,则注册一下,添加一个sigqueue节点,若已注册,则什么都不做。
34-64可靠信号的注册:每次注册信号,不管是否已经注册,每次都会添加一个sigqueue节点(信号信息)

信号的注销
删除要处理的信号sigqueue节点;
若信号是非可靠信号,则直接将位图置0(非可靠信号在没有处理之前只会注册一次)
若信号是可靠信号,则删除后,需要判断是否还有相同节点,没有的话才会重置位图为0.

信号的捕捉处理

  • 默认处理 SIG_DFL
  • 忽略处理 SIG_IGN
  • 自定义处理 void sigcb(int signum)
//修改信号的回调函数
sighandler_t signal(int signum, sighandler_t handler);
//修改信号的整个处理动作
sigaction(int signum, struct sigaction *new, struct sigaction *old);

自定义信号的捕捉流程
信号的处理是在程序运行从内核态切换回用户态之前,默认 / 忽略直接在内核中完成处理,而用户自定义信号处理方式,则需要返回用户态执行回调函数,完成后返回内核态,最终没有信号处理了,再返回程序主控流程。

  1. 当程序在用户态主控流程运行的时候,因为系统调用/中断/异常 切换到内核态运行
  2. 完成功能后,在返回用户态主控流程之前,调用do_signal函数去处理未决信号。若信号的处理方式是忽略/默认,则直接在内核态完成;若信号的处理方式是用户自定义,会切换到用户态执行用户自定义的处理函数,执行完毕后。
  3. 调用sigreturn返回内核态运行,当没有信号待处理的时候,则返回用户态主控流程

信号的阻塞
阻塞一个信号的递达,信号依然可以注册,只是暂时不处理。
在pcb中有一个blocked位图(阻塞信号集合),凡是添加到这个集合中的信号,都表示需要阻塞,暂时不处理。

int sigprocmask(int how, sigset_t *set, sigset_t *old);
//how:当前要对block集合进行的操作

SIG_BLOCK 将set集合中的信号添加到block进程阻塞信号集合中 block = block|set
表示阻塞set集合中的信号以及原有的阻塞信号,并且将原有阻塞的信号返回到old集合中(便于还原)

SIG_UNBLOCK 将set集合中的信号从block集合中移除,将set集合中的信号解除阻塞

SIG_SETMASK 直接将block集合中的信号修改为set集合中的信号, block = set

函数的可重入与不可重入
函数的重入:在多个执行流中,进入同一个函数进行运行处理;
函数的不可重入:在函数的重入之后,有可能会造成数据二义或者引起程序逻辑混乱,在这个函数是一个不可重入函数。
函数的可重入:在函数重入之后,不会造成数据二义或者引起程序逻辑混乱,则这个函数是一个可重入函数。

函数的可重入与不可重入的判定点:一个函数中是否对全局数据进行了非原子性的操作。
原子性:操作要不然一次性完成,要不然就不做(操作不可被打断)
malloc / free 都是不可重入函数,因此在多个执行流中进行操作的时候需要小心。

volatile关键字:修饰一个变量,使这个变量保持内存可见性—每次对变量进行访问都需要重新从内存加载变量的数据,防止编译器过度优化。

SIGCHILD信号
子进程退出后,操作系统发送SIGCHILD信号给父进程,但是因为SIGCHILE信号的默认处理方式就是忽略,因此在之前的程序中并没有感受到操作系统的通知,因此只能固定的使用进程等待来避免产生僵尸进程,但是在这个过程中父进程是一直阻塞的,只能一直等待子进程退出。

如何让程序感知到操作系统的通知?
若在程序初始化阶段,将SIGCHILD信号的处理方式自定义,并且在自定义函数重调用waitpid,这样的话就当子进程退出的时候,则自动回调处理了,父进程就不需要一直等待了。

多个子进程同时退出,都会向父进程发送SIGCHILD信号,但是SIGCHILD信号是非可靠信号,有可能会丢失事件。
例如:三个子进程同时退出,但是信号只注册了一次,意味着只会执行一次回调函数,调用一次waitpid,只能处理一个僵尸进程。
非可靠信号的丢失是无法避免的,因此只能在一次信号回调中处理完所有的僵尸进程。

while((waitpid(-1, NULL, WNOHANG)) > 0);
waitpid
//返回值 >0 - 退出子进程的pid
        ==0 - 没有子进程退出
        <0 - 出错

循环是为了若有子进程退出则一直处理,直到没有子进程退出,则退出循环信号回调完毕,
WNOHANG : 将waitpid设置为非阻塞,没有子进程退出的时候返回0,退出循环,不要导致程序流程卡在信号回调函数中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值