基于linux master v4.9版本
信号是异步的,
一、信号何时来
信号是异步的,对于一个进程随时都会接收到信号。
二、选择哪个线程(task)来处理
那么一个进程接收到信号时,需要选择一个task来处理。
如何选择呢?
三、使线程达到能够处理信号的状态
设置信号的pending flag。
1)对于睡眠线程
a)处于TASK_INTERRUPTIBLE,唤醒,系统调用返回
1 while (dev->current_len == GLOBALFIFO_SIZE) { 2 if (filp->f_flags & O_NONBLOCK) { 3 ret = -EAGAIN; 4 goto out; 5 } 6 __set_current_state(TASK_INTERRUPTIBLE); 7 8 mutex_unlock(&dev->mutex); 9 10 schedule(); 11 if (signal_pending(current)) { 12 ret = -ERESTARTSYS; 13 goto out2; 14 } 15 16 mutex_lock(&dev->mutex); 17 }
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信号,有则处理。
五、拓展:1、通过signal no来获取信号处理函数。
task的信号处理函数保存在 task_struct里。
1 /* Signal handlers: */ 2 struct signal_struct *signal; 3 struct sighand_struct *sighand;
signal保存64个信号的标志位,sighand是信号处理函数的指针。
1 struct sighand_struct { 2 atomic_t count; 3 struct k_sigaction action[_NSIG]; 4 spinlock_t siglock; 5 wait_queue_head_t signalfd_wqh; 6 };
当然信号处理函数可以通过signal和sigaction函数设置用户自定义处理函数。
2、驱动层ERESTARTSYS和EINTR的区别
参考:https://stackoverflow.com/questions/9576604/what-does-erestartsys-used-while-writing-linux-driver
ERESTARTSYS有两种结果:1)该信号设置了SA_RESTART,会重启系统调用,当然,用户态不感知,但是对于一些系统调用没办法或者很难直接重启系统调用,比如定时器。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),上面所述第二种情况。
3、系统调用的改变
题外话:
1、TASK_INTERRUPTIBLE:浅度睡眠
TASK_UNINTERRUPTIBLE:深度睡眠