Linux如何处理信号

从Crtl+C开始:
之前一直有一个问题:在shell下运行一个程序,每次想中途停止这个程序时,下意识的就会按下Ctrl+C就可以终止当前阻塞在终端的进程,Ctrl+C这个组合键按下到底都发生了什么?

其实这个操作就是向前台进程发送SIGINT信号。
以下是linux支持的信号列表:

使用kill -n pid或在代码中使用int kill(pid_t pid, int sig);可以向一个进程发送信号。

如果代码中没有显式的用signal去注册信号对应的句柄,那就会采用默认的处理方式,例如接收到SIGINT默认会将进程停止。

新的问题来了,当使用了kill、signal这些API之后,内核发生了什么?这些信号怎么能够传到指定的进程?又是怎么被处理的?

先看下task_struct结构中与信号处理相关的成员:

signal:这是一个signal_struct结构,里面有一个比较关键的成员wait_chldexit,指向一个等待队列,当进程调用wait或者waitpid的时候,该进程就会在这个队列上等,知道内核发现对应的子进程退出则将其唤醒

sighand:这是个大小为64的sigaction的指针数组对应上文64种信号的handler,sigaction中有一个sa_handler成员默认是0,代表采用系统默认操作处理该信号。

blocked:一个位图,代表进程目前屏蔽哪些信号

pending:一个sigpending结构,底下串着sigqueue,这些sigqueue就是待处理的信号了,它包含user_struct用于记录信号发起者的信息、siginfo用于记录关于信号的全部信息。

将这些结构组织一下,大概是这样的:

这些信息都是有默认值的,在fork的copy_process过程中,会调用copy_sighand和copy_signal来拷贝父进程的信号处理方法,如果父进程对某个信号设置了句柄,那么子进程会继承过来。如果一直父进程一直往上都没有为任何信号设置句柄,那就继承init_task设置好的值,sighand->action[n].sa_handler应该为0(SIG_DFL)。init_task初始化时信号相关成员的初始化如下:

拿子进程退出举例,在编程中一般会调用wait来处理子进程退出,一旦子进程调用了exit,父进程就会从wait返回。

了解上面task结构体中的这些和信号相关的成员,大概也可以猜到内核是怎么实现信号处理的了,实际上子进程在退出时向父进程发送SIGCHLD信号,并去唤醒父进程处理。

所以就可以猜测sys_exit系统调用肯定会构造一个sigqueue的结构,然后挂到父进程的pending里面并尝试去父进程的wait_chldexit中唤醒父进程处理。实际就是这样的,exit系统调用的通知父进程的流程大致如下:

值得一提的是SIG_IGN和SA_NOCLDWAIT可能会导致不向进程发送信号,但是子进程退出时一定会唤醒父进程。

kill系统调流程也差不多,最终也是调用__send_signal把sigqueue挂到进程的pending上。

至于信号的处理的时机,即从内核态返回用户态(从系统调用或者中断返回)的时候都在work_pending检查处理。

以arm64为例:

TIF_SIGPENDING是在__send_signal中调用complete_signal设置的。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值