由于在下能力相当有限,有不当之处,还望大家批评指正^_^
一、 信号概念
- 信号(signal)是软中断。
- 信号提供了一种处理异步事件的方式。
- 当各种各样的事件发生时,程序相应的会收到各种各样的信号。
- 对于大部分信号,程序可以自行决定如何处理他们。
执行某条指令,产生了异常
(例如, 访存指令访问无效内存时收到SIGSEGV信号,执行非法指令收到SIGILL信号)
执行某些不合适的操作
(例如,写破列的管道收到SIGPIPE信号,后台程序向控制台打印信息收到SIGTTOU信号)
发生了一些与当前程序有关的事情
(例如,子进程退出后父进程收到SIGCHLD信号,定时器时间到了收到SIGALRM信号)
程序之间相互发送信号
(例如, 程序之间通过信号进行协作)
通过man 7 signal命令可以查看Linux下的信号说明。
- 忽略
- 捕获并处理
- 让系统按默认方式处理
每个经程,通过pthread_sigmask设置自己要阻塞的信号。
问题是,这样的话,信号来了,具体由哪个线程谁处理呢???
/*
* Now find a thread we can wake up to take the signal off the queue.
*
* If the main thread wants the signal, it gets first crack.
* Probably the least surprising to the average bear.
*/
if (wants_signal(sig, p))
t = p;
else if (!group || thread_group_empty(p))
/*
* There is just one thread and it does not need to be woken.
* It will dequeue unblocked signals before it runs again.
*/
return;
else {
/*
* Otherwise try to find a suitable thread.
*/
t = signal->curr_target;
while (!wants_signal(sig, t)) {
t = next_thread(t);
if (t == signal->curr_target)
/*
* No thread needs to be woken.
* Any eligible threads will see
* the signal in the queue soon.
*/
return;
}
signal->curr_target = t;
}
同时,信号又能够得到妥善的处理。一种解决方法是:所有线程均阻塞所有process-directed信号,由一个线程轮询接收处理这些信号。
任务一般会因为中断、系统调用、指令异常这几个原因,进入内核态。但不管是哪一种,在返回用户态之前,内核都会做信号方面的检查与处理。这个流程是一样的。
以i386为例,其代码在如下文件中。
arch\x86\kernel\entry_32.S
a). 信号处理总入口
static void do_signal(struct pt_regs *regs)
他由do_notify_resume调用
b). get_signal_to_deliver中处理被忽略的信号,以及按默认方式处理的信号
c). handle_signal中处理用户设定了要捕获处理的信号
以ioctl为例。在x86_64下,其系统调用号为16。
见arch\x86\include\asm\unistd_64.h
ioctl的C库函数汇编代码如下:
000000000040af30 <__ioctl>:
40af30: b8 10 00 00 00 mov $0x10,%eax
40af35: 0f 05 syscall
40af37: 48 3d 01 f0 ff ff cmp $0xfffffffffffff001,%rax
40af3d: 0f 83 ad 23 00 00 jae 40d2f0 <__syscall_error>
40af43: c3 retq
40af44: 90 nop
40af45: 90 nop
handle_signal中的代码片断如下:
case -ERESTARTSYS:
if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->ax = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
regs->ax = regs->orig_ax;
regs->ip -= 2; //等于回到__ioctl中调用syscall之前的地方了
break;
当线程要从内核态回到用户态时,如果有信号要处理,那么就不是回到用户态原先因为进内核而被中断的地方执行了。而是回到用户设置的信号处理函数的地址处开始执行,就好像线程当初进入内核前正要执行信号处理函数一样。
以x86_32,传统信号处理方式为例。
代码在ia32_setup_frame中
内核将用户态程序现场修改为,回到用户态正好要执行的下一条指令,就是用户态信号处理函数的第一条指令。这样,回到用户态,就从用户态信号处理函数第一条指令开始执行了。
以x86_32,传统信号处理方式为例。
内核在ia32_setup_frame中为返回做好了准备
用户态信号处理函数的栈帧中,返回地址被内核赋了相应的值。即用户态信号处理函数在返回时,会返回到内核事先安排好的地方继续执行。其实就是调用__NR_ia32_sigreturn,对应sys_sigreturn。
主要是restore_sigcontext这个函数。
他从栈里面取出现场环境数据,进行恢复。
这些栈里面的数据,则是前面在ia32_setup_frame 处理中,通过调用函数ia32_setup_sigcontext进行保存的。
1367



被折叠的 条评论
为什么被折叠?



