基于linux master v4.9版本

信号是异步的,

一、信号何时来

信号是异步的,对于一个进程随时都会接收到信号。

二、选择线程(task)来处理

  那么一个进程接收到信号时,需要选择一个task来处理。如何选择呢?

三、使线程达到能够处理信号的状态

  设置信号的pending flag。

  

  1)对于睡眠线程

    a)处于TASK_INTERRUPTIBLE,唤醒,系统调用返回

     while (dev->current_len == GLOBALFIFO_SIZE) {
if (filp->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE); mutex_unlock(&dev->mutex); schedule();
if (signal_pending(current)) {
ret = -ERESTARTSYS;
goto out2;
} mutex_lock(&dev->mutex);
}

    b)处于TASK_UNINTERRUPTIBLE,

        i)如果signal是SIGKILL(9),也会唤醒,返回系统调用

        ii)如果signal不是SIGKILL(9),不会唤醒,等task自己从睡眠中醒来,返回系统调用;如果task一直处于深度睡眠中,那么该信号就没办法得到处理

    c)处于TASK_RUNNING(queued/runnable就绪态和运行态,tsk->state = TASK_RUNNING),不需要做进一步处理

四、真正处理信号

  线程通过系统调用(sys_call)陷入内核态。具体来说,通过int80陷入内核,然后int80的处理函数通过系统调用号来调用sys_call,sys_call返回后会检查该task是否pending信号,有则处理。

  基于linux0.11版本的分析:https://blog.csdn.net/geekcome/article/details/6398414

基于新代码分析TODO

五、补充

从以上分析得出结论,信号只在系统调用结束,返回用户态前处理。那么一个不调用sys_call的进程就没有办法处理信号了?

实验:最简单的死循环

 int main(){
while(){}
}

strace跟踪:为了说明进程的启动,和 一些属性,本文没有删除启动过程产生的系统调用打印;进程启动后,kill -2 pid杀死进程。

 zsh@zsh-vm:~/program/C++$ strace ./signal_while.o
execve("./signal_while.o", ["./signal_while.o"], [/* 74 vars */]) =
brk(NULL) = 0x8993000
access("/etc/ld.so.nohwcap", F_OK) = - ENOENT (No such file or directory)
mmap2(NULL, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -, ) = 0xb7f8c000
access("/etc/ld.so.preload", R_OK) = - ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
mmap2(NULL, , PROT_READ, MAP_PRIVATE, , ) = 0xb7f76000
close() =
access("/etc/ld.so.nohwcap", F_OK) = - ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) =
read(, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\320\207\1\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
mmap2(NULL, , PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, , ) = 0xb7dc0000
mmap2(0xb7f70000, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, , 0x1af000) = 0xb7f70000
mmap2(0xb7f73000, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -, ) = 0xb7f73000
close() =
mmap2(NULL, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -, ) = 0xb7dbf000
set_thread_area({entry_number:-, base_addr:0xb7dbf700, limit:, seg_32bit:, contents:, read_exec_only:, limit_in_pages:, seg_not_present:, useable:}) = (entry_number:)
mprotect(0xb7f70000, , PROT_READ) =
mprotect(0x8049000, , PROT_READ) =
mprotect(0xb7fb5000, , PROT_READ) =
munmap(0xb7f76000, ) =
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} --- 这是窗口大小改变引起
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=, si_uid=} ---
+++ killed by SIGINT +++

可以看出信号还是得到了处理。那么就不符合以上的分析结果。再进一步分析,信号的处理时机肯定是统一接口的,那么一个死循环看似永远跑在用户态的进程什么时候会有系统调用呢?答案在进程调度引起了系统调用。普通进程CFS调度类,有233次上下文切换,180次非自愿,53次自愿。 当然这期间还有时钟中断的处理,这属于硬中断,不确定会不会引起信号处理。

 zsh@zsh-vm:~/program/C++$ cat /proc//sched
signal_while.o (, #threads: )
-------------------------------------------------------------------
se.exec_start : 83967758.954863
se.vruntime : 251746.287113
se.sum_exec_runtime : 137316.047183
se.nr_migrations :
8 nr_switches : 233
9 nr_voluntary_switches : 53
10 nr_involuntary_switches : 180

se.load.weight :
se.avg.load_sum :
se.avg.util_sum :
se.avg.load_avg :
se.avg.util_avg :
se.avg.last_update_time :
17 policy : 0
18 prio : 120

clock-delta :

五、拓展:1、通过signal no来获取信号处理函数。

      task的信号处理函数保存在 task_struct里。

     /* Signal handlers: */
struct signal_struct *signal;
struct sighand_struct *sighand;

     signal指向进程信号描述符,不是很明白,与进程组有关,sighand是64个信号处理函数的指针。

 struct sighand_struct {
atomic_t count;
struct k_sigaction action[_NSIG];
spinlock_t siglock;
wait_queue_head_t signalfd_wqh;
};

    当然信号处理函数可以通过signal和sigaction函数设置用户自定义处理函数,不设置即为系统默认处理。linux里面,signal用sigaction实现。

   2、驱动层ERESTARTSYS和EINTR的区别

    参考:https://stackoverflow.com/questions/9576604/what-does-erestartsys-used-while-writing-linux-driver

    ERESTARTSYS有两种结果:1)该信号设置了SA_RESTART,会重启系统调用,当然,用户态不感知,但是对于一些系统调用没办法或者很难直接重启系统调用,比如定时器,详见man说明。2)该信号没有设置SA_RESTART,那么会返回到用户态,返回值为-1,并设置errno=EINTR(return −1 and set errno to indicate the error)。

    EINTR只是返回到用户态,返回值为-1,并设置errno=EINTR(return −1 and set errno to indicate the error),上面所述第二种情况。

syscall_get_nr判断是不是从系统调用返回,另外的情况是(硬件)中断和异常(比如page_falut)。

        只有系统调用睡眠后,被信号唤醒,才会返回ERESTARTSYS或EINTR(具体返回那个视功能决定,比如conncet就不能返回ERESTARTSYS)。如果是不会发生睡眠的系统调用,则不会返回这两个值。那么在信号处理完成后,不会走handle_signal的ERESTARTSYS分支,也就不会返回EINTR错误。所以非阻塞的socket系统调用,比如read、write不会返回EINTR错误。部分代码分析见:https://blog.csdn.net/yusiguyuan/article/details/45669657

    3、系统调用的改变

         4、kill命令

DESCRIPTION
The default signal for kill is TERM. Use -l or -L to list available signals. Particularly useful signals include HUP, INT, KILL, STOP, CONT, and . Alternate signals may be specified in
three ways: -, -SIGKILL or -KILL. Negative PID values may be used to choose whole process groups; see the PGID column in ps command output. A PID of - is special; it indicates all pro‐
cesses except the kill process itself and init. pid> send_signal对应进程
pid = - send_signal除自己和init进程外的所有进程,
pid = send_signal本进程组
其他负值 send_signal pid所在进程组
当然是有权限的限制。这里还牵涉到namespace,就不说了。

5、signo < 32 是不能排队的,所以是不可靠信号 0-31共32个是不可靠信号,也叫非实时信号。

  #define SIGRTMIN 32
static inline int legacy_queue(struct sigpending *signals, int sig)
{
return (sig < SIGRTMIN) && sigismember(&signals->signal, sig);
} result = TRACE_SIGNAL_ALREADY_PENDING;
if (legacy_queue(pending, sig))
goto ret;

不可靠的来源:

    • 进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。
    • 信号可能丢失,因为不支持排队,pending住信号后,收到同一个信号,那么该信号只能丢弃
      因此,早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。目前linux通过sigaction函数已经解决了 signo>SIGRTMIN的两个不可靠来源,< SIGRTMIN为了兼容以前程序没有解决。

6、信号0

http://www.linuxjournal.com/content/monitoring-processes-kill-0

“signal 0″ is kind of like a moral equivalent of “ping”.

Using “kill -0 NNN” in a shell script is a good way to tell if PID “NNN” is alive or not:

signal 0 is just used to check process is exists or not.

题外话:

1、TASK_INTERRUPTIBLE:浅度睡眠

TASK_UNINTERRUPTIBLE:深度睡眠

2、SIGKILL和SIGSTOP信号是不能捕捉和忽略的,即用户态不能对这两个信号设置处理函数和mask,只能执行系统默认行为。

 int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
{
if (!valid_signal(sig) || sig < || (act && sig_kernel_only(sig)))sig_kernel_only --检查对kill 和 stop 行为的非法设置
return -EINVAL; k = &p->sighand->action[sig-]; spin_lock_irq(&p->sighand->siglock);
if (oact)
*oact = *k; sigaction_compat_abi(act, oact); if (act) {
sigdelsetmask(&act->sa.sa_mask,
sigmask(SIGKILL) | sigmask(SIGSTOP)); --去除对kill 和 stop信号的屏蔽操作

后面发现的资料:http://kernel.meizu.com/linux-signal.html

linux信号的处理--部分源码分析的更多相关文章

  1. Linux下USB suspend/resume源码分析【转】

    转自:http://blog.csdn.net/aaronychen/article/details/3928479 Linux下USB suspend/resume源码分析 Author:aaron ...

  2. Linux内核2.6.14源码分析-双向循环链表代码分析(巨详细)

    Linux内核源码分析-链表代码分析 分析人:余旭 分析时间:2005年11月17日星期四 11:40:10 AM 雨 温度:10-11度 编号:1-4 类别:准备工作 Email:yuxu97101 ...

  3. Flask框架 (四)—— 请求上下文源码分析、g对象、第三方插件(flask_session、flask_script、wtforms)、信号

    Flask框架 (四)—— 请求上下文源码分析.g对象.第三方插件(flask_session.flask_script.wtforms).信号 目录 请求上下文源码分析.g对象.第三方插件(flas ...

  4. linux 2.6 互斥锁的实现-源码分析

    http://blog.csdn.net/tq02h2a/article/details/4317211 看了看linux 2.6 kernel的源码,下面结合代码来分析一下在X86体系结构下,互斥锁 ...

  5. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)

    http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...

  6. linux内存源码分析 - 零散知识点

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 直接内存回收中的等待队列 内存回收详解见linux内存源码分析 - 内存回收(整体流程),在直接内存回收过程中, ...

  7. linux内存源码分析 - 内存回收(整体流程)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 概述 当linux系统内存压力就大时,就会对系统的每个压力大的zone进程内存回收,内存回收主要是针对匿名页和文 ...

  8. linux中断源码分析 - 中断发生(三)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 回顾 上篇文章linux中断源码分析 - 初始化(二)已经描述了中断描述符表和中断描述符数组的初始化,由于在初始 ...

  9. linux调度器源码分析 - 运行(四)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 之前的文章已经将调度器的数据结构.初始化.加入进程都进行了分析,这篇文章将主要说明调度器是如何在程序稳定运 ...

随机推荐

  1. IntelliJ+AntBuild+Tomcat实现Maven站点的热部署

    这段时间要研究WebGL技术,做一下三维建模项目,涉及到较多的前端编码.eclipse编译器那令人着急的编码提示功能,以及丑恶的界面对项目的开展造成了一定的阻碍.为解决这个问题,转向IntelliJ ...

  2. Altium designer的PCB设计规则

    PCB布线规则,布板需要注意的点很多,但是基本上注意到了下面的这此规则,LAYOUT PCB应该会比较好,不管是高速还是低频电路,都基本如此. 1. 一般规则 1.1 PCB板上预划分数字.模拟.DA ...

  3. 【C#】 WebApi 路由机制剖析

    C#进阶系列——WebApi 路由机制剖析:你准备好了吗? 转自:https://blog.csdn.net/wulex/article/details/71601478 2017年05月11日 10 ...

  4. 在ubuntu 14.04 编译android 2.3.1 错误解决办法

    首先必须降低gcc版本: sudo apt-get install gcc-4.4sudo apt-get install g++-4.4sudo rm -rf /usr/bin/gcc /usr/b ...

  5. selenium自动化测试、Python单元测试unittest框架以及测试报告和日志输出

    部分内容来自:https://www.cnblogs.com/klb561/p/8858122.html 一.基础介绍 核心概念:test case, testsuite, TestLoder,Tex ...

  6. beijing(数学题)

    beijing(数学题) 甲和乙随机进行2n+1场n胜球赛,赌球必须对每场球赛单独押注.由于小明是甲队的铁杆球迷,现在小明希望如果甲最终获胜,那么他获得\(2^{2n-1}\)元,否则乙队获胜,他失去 ...

  7. 洛谷P3709 大爷的字符串题(莫队)

    题目背景 在那遥远的西南有一所学校 /*被和谐部分*/ 然后去参加该省省选虐场 然后某蒟蒻不会做,所以也出了一个字符串题: 题目描述 给你一个字符串a,每次询问一段区间的贡献 贡献定义: 每次从这个区 ...

  8. Shell-4-让文本飞

    1.正则表达式 ^ 行起始标志 $ 行尾标记 . 匹配任意一个字符 [ ] 匹配包含在[字符]之中的任意一个字符,coo[kl]匹配cook或cool [^] 匹配除[^字符]的任意一个字符 [-] ...

  9. Unity---Directory目录的删除、存在、创建操作

    使用Directory类要引入命名空间 using  System.IO; Directory类下的三个静态方法. public static void CreateDirectory(string  ...

  10. luogu1999 高维正方体

    神仙题 分析法是个好方法 反正xjb分析就分析出来了 首先,i维立方体的点数(0维元素数)为\(2^i\) 首先0维肯定是1(不就是一个点吗) 你想想你是怎么用点拼成线段的 你把两个点往地上一扔 然后 ...