从Linux 0.11内核看Linux信号处理机制
2012-12-26 09:40
323 查看
摘要:信号处理机制是Unix操作系统的一大特点。本文以Linux0.11信号处理相关源码为例,针对几个细节对信号处理的整个流程进行描述,力求简单明了,参考赵炯博士的《Linux内核完全剖析》和潘晓雷的《Linux0.11源码分析》。
外部信号:终端Ctrl-C产生SGINT信号,定时器到期产生SIGALRM…
显式请求:kill函数允许进程发送任何信号给其他进程或进程组。
kill -l
可以通过类似下面的命令显式的给一个进程发送一个信号:
kill -2 pid
捕获信号:注册信号处理函数,它对产生的特定信号做处理。
让信号默认动作起作用:unix内核定义的默认动作,有5种情况:
a) abort:终止进程并产生core文件。
b) stop:终止进程但不生成core文件。
c) 忽略:忽略信号。
d) suspend:挂起进程。
e) continue:若进程是挂起的,则resume进程,否则忽略此信号。
当前进程在内核中进入睡眠以后刚被唤醒的时候(必定是在系统调用中),或者由于不可忽略信号的存在而提前返回到用户空间。
进程结构体重关于信号的定义
do_signal()函数 参数为内核态堆栈中的内容
之所以把EIP的值设置成信号处理函数的地址,是因为一旦进程返回用户态,就要去执行信号处理程序,所以EIP要指向信号处理程序而不是原来应该执行的地址。
但是在Linux内核代码中,并没有 给出此函数的具体定义,查看相关资料,在Linux的Libc函数库中定义有函数如下
编译程序在编译连接用户自定义的信号处理函数时,会将sa_restorer()函数插入到用户程序中。这样,用户在处理完自定义的信号处理函数后,就会继续执行用户代码了。、
注:文中图片来自《Linux内核完全剖析》。
一、信号(signal)机制概述
所谓信号,是一种软中断机制,是实现进程间异步通讯的手段。信号的发送和处理是异步的,进程并不知道什么时候会收到信号。信号同中断很像,不同的信号对应不同的信号类型,并且对应不同的信号类型有相应的信号处理函数和信号屏蔽位。1.信号的来源
程序错误:除零,非法内存访问…外部信号:终端Ctrl-C产生SGINT信号,定时器到期产生SIGALRM…
显式请求:kill函数允许进程发送任何信号给其他进程或进程组。
2.Linux下查看信号
在Linux下,可以通过以下命令查看系统所有的信号:kill -l
可以通过类似下面的命令显式的给一个进程发送一个信号:
kill -2 pid
3.信号的处理
忽略信号:大部分信号可被忽略,除SIGSTOP和SIGKILL信号外(这是超级用户杀掉或停掉任意进程的手段)。捕获信号:注册信号处理函数,它对产生的特定信号做处理。
让信号默认动作起作用:unix内核定义的默认动作,有5种情况:
a) abort:终止进程并产生core文件。
b) stop:终止进程但不生成core文件。
c) 忽略:忽略信号。
d) suspend:挂起进程。
e) continue:若进程是挂起的,则resume进程,否则忽略此信号。
二、深入信号机制内部
1.信号检测和响应的时机
当前进程由于系统调用、中断或异常而进入内核态以后,从内核态返回到用户态之前。当前进程在内核中进入睡眠以后刚被唤醒的时候(必定是在系统调用中),或者由于不可忽略信号的存在而提前返回到用户空间。
2.Linux 0.11中用于信号处理的数据结构
用于保存信号信息的结构体struct sigaction { void (*sa_handler)(int);//信号处理句柄 sigset_t sa_mask;//信号的屏蔽码,可以阻塞指定的信号 int sa_flags;//信号选项标志 void (*sa_restorer)(void);//信号恢复函数指针(系统内部使用) };
进程结构体重关于信号的定义
long signal; struct sigaction sigaction[32]; long blocked; /* bitmap of masked signals */
3.信号的预处理操作
do_signal()函数 参数为内核态堆栈中的内容
void do_signal(long signr,long eax, long ebx, long ecx, long edx, long fs, long es, long ds, long eip, long cs, long eflags, unsigned long * esp, long ss) { unsigned long sa_handler; long old_eip=eip; struct sigaction * sa = current->sigaction + signr - 1; int longs; unsigned long * tmp_esp; sa_handler = (unsigned long) sa->sa_handler; if (sa_handler==1) return; if (!sa_handler) { if (signr==SIGCHLD) return; else do_exit(1<<(signr-1)); } if (sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; *(&eip) = sa_handler; longs = (sa->sa_flags & SA_NOMASK)?7:8; *(&esp) -= longs; verify_area(esp,longs*4); tmp_esp=esp; put_fs_long((long) sa->sa_restorer,tmp_esp++); put_fs_long(signr,tmp_esp++); if (!(sa->sa_flags & SA_NOMASK)) put_fs_long(current->blocked,tmp_esp++); put_fs_long(eax,tmp_esp++); put_fs_long(ecx,tmp_esp++); put_fs_long(edx,tmp_esp++); put_fs_long(eflags,tmp_esp++); put_fs_long(old_eip,tmp_esp++); current->blocked |= sa->sa_mask; }对内核态堆栈的修改
4.操作系统进入信号处理
通过上述内容,可以看到操作系统在检测到有信号传入时,首先把内核堆栈中存放返回执行点的eip(指令寄存器)保存为old_eip,然后将eip替换为信号处理函数的地址,然后将内核中保存的“原ESP”(即用户态栈地址)减去一定的值,目的是扩大用户态的栈,然后将内核栈上的内容保存到用户栈上。之所以把EIP的值设置成信号处理函数的地址,是因为一旦进程返回用户态,就要去执行信号处理程序,所以EIP要指向信号处理程序而不是原来应该执行的地址。
5.操作系统退出信号处理
在前面介绍sigaction数据结构的时候出现了信号活动恢复函数指针sa_restroer,该指针主要用于用户态堆栈的清理,把系统调用后的返回值eax和寄存器ecx,edx以及标志寄存器eflags弹出,完全恢复系统调用后各寄存器和CPU的状态,最后通过ret指令弹出原用户程序的eip(即堆栈中的old_eip),返回执行用户程序。但是在Linux内核代码中,并没有 给出此函数的具体定义,查看相关资料,在Linux的Libc函数库中定义有函数如下
编译程序在编译连接用户自定义的信号处理函数时,会将sa_restorer()函数插入到用户程序中。这样,用户在处理完自定义的信号处理函数后,就会继续执行用户代码了。、
注:文中图片来自《Linux内核完全剖析》。
相关文章推荐
- linux-0.11内核 信号处理小结
- linux-0.11内核 信号处理小结
- Linux信号处理机制分析 并 模拟VC实现多定时器机制
- 【linux高级程序设计】(第十章)Linux异步信号处理机制
- 【linux高级程序设计】(第十章)Linux异步信号处理机制 3
- Linux信号signal处理机制
- linux c之信号signal处理机制
- Linux0.11内核--系统中断处理程序int 0x80实现原理
- linux 信号signal处理机制(一)
- linux信号signal处理机制(一)
- Linux0.11内核--系统调用机制分析
- Linux0.11内核--缓冲区机制大致分析
- Linux 信号signal处理机制
- linux0.11内核中断处理
- Linux信号机制分析和信号处理函数
- linux信号signal处理机制(二)
- Linux 信号signal处理机制
- linux信号机制 - 用户堆栈和内核堆栈的变化【转】
- Linux 信号signal处理机制【转】
- Linux 信号signal处理机制