信号

信号种类

信号是一种软件层面上对中断的模拟,而这种软件模拟的信号或者说中断的产生,有三大类:

1. 外部信号。

用户在终端 按下某些键时,终端驱动程序会发送信号给前台进程,例如Ctrl-C 产生 SIGINT 信 号, Ctrl-\ 产生 SIGQUIT 信号, Ctrl-Z 产生 SIGTSTP 信号。

2. 硬件异常产生的信号。

这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。

例如当前进程执行了 除以0 的指令, CPU 的运算单元会产生异常,内核将这个异常解释为 SIGFPE 信号发送给进 程。
再比如当前进程访问了非法内存地址,, MMU 会产生异常,内核将这个异常解释为 SIGSEGV 信 号发送给进程。

3. 显示的请求。

alarm(times);

定义一个闹钟,约定times秒后,内核向该进程发送一个SIGALRM信号;
kill 发信号

信号处理

异常的处理在CPU的中断表中有固定的位置

  1. 如果用户没有挂除0处理函数,执行路径:
kernel/signal.c do_notify_resume() --> do_signal() --> get_signal_to_deliver()

在get_signal_to_deliver()中, 用户空间任务直接被do_group_exit() 杀掉.

  1. 如果用户挂了除0的处理函数,执行路径:
kernel/signal.c do_notify_resume() --> do_signal() --> handle_signal()

handle_signal()比较复杂, 要构建用户栈, 修改eip等寄存器, 这样从内核返回后(entry.s中的restore_all)

  1. 执行特殊的系统调用sigreturn从用户态回到内核态
信号与中断
信号与中断的相似点:

(1 )采用了相同的异步通信方式;

(2 )当检测出有信号或中断请求时,都暂停正在执行的程序而转去执行相应的处理程序;

(3 )都在处理完毕后返回到原来的断点;

(4 )对信号或中断都可进行屏蔽。

信号与中断的区别:

(1 )中断有优先级,而信号没有优先级,所有的信号都是平等的;

(2 )信号处理程序是在用户态下运行的,而中断处理程序是在核心态下运行;

(3 )中断响应是及时的,而信号响应通常都有较大的时间延迟。

信号的原理

这里写图片描述
这里写图片描述
在Linux里面每个进程都是按照进程描述符 task_struct 结构创建的,还有一个叫task vector的东西,从名字上就能看出来这是一个数组,这里面保存的是指向每一个进程的指针,即指向每一个task_struct的指针,因此一个Linux**系统最大的进程数**,取决于task vector这个数组的大小,一般默认是512个。

在每一个进程描述符task_struct里面,其中一项是Signal_Struct,在Signal_Struct这里面有一项list_head的描述符,在这里面有一个sigset_t表,定义了64种信号的所代表的含义。
铺垫了很多,其实主要是为了铺垫64种信号存放的位置。也就是说在每个进程之中,都有存着一个表,里面存着每种信号所代表的含义,而这也是信号机制的根本。

由于信号的触发和发送是随机的,也就是异步的。接收进程是无法预知什么时间,会收到哪个信号的。下面就开始讲下信号的详细发送机制,

举例说明,如果有A,B两个进程,A进程接收到触发条件,开始发送信号给B进程,信号并不是直接从进程A发送给进程B,而是要通过内核来进行转发。之所以要通过内核来转发,这样做的目的应该也是为了对进程的管理和安全因素考虑。因为在这些信号当中,SIGSTOP和SIGKILL这两个信号是可以将接收此信号的进程停掉的,而这类信号,肯定是需要有权限才可以发出的,不能够随便哪个程序都可以随便停掉别的进程。

(
q1)信号是直接从进程到进程的吗?
q2)是否有发信号的权限管理

A进程发送的信号消息,其实就是根据上面的那个信号表,根据需要对相应的表项进行设置。内核接受到这个信号消息后,会先检查A进程是否有权限对B进程的信号表对应的项进行设置,如果可以,就会对B进程的信号表进行设置,这里面信号处理有个特点,就是没有排队的机制,也就是说某个信号被设置之后,如果B进程还没有来及进行响应,那么如果后续第二个同样的信号消息过来,就会被阻塞掉,也就是丢弃。

(
q3)有排队机制吗?
)

内核对B进程信号设置完成后,就会发送中断请求给B进程,这样B进程就进入到内核态,这个时候进程B根据那个信号表,查找对应的此信号的处理函数,然后设置frame,设置好之后,跳回到用户态执行信号处理函数,处理完成后,再次返回到内核态,再次设置frame,然后再次返回用户态,从中断位置开始继续执行。

这个frame其实就是在用户态和内核态之间跳转的时候,对堆栈现场的压栈保存。
大致的原理就是这么一个过程,在真正使用的时候,就比较简单了,用kill函数发送信号,在接收进程里,通过signal或者signalaction函数调用sighandler,来启动对应的函数处理信号消息

信号的使用

用kill函数发送信号。
与kill函数不同的是raise()函数允许进程向自身发送信号

在接收进程里,通过signal或者signalaction函数调用sighandler,来启动对应的函数处理信号消息

三种处理:

忽略信号,

对信号不做任何处理,但是又两个信号是不能忽略的,即SIGKILL,SIGSTOP

捕捉信号,

定义信号处理函数,当信号发送时,执行相应的自定义处理函数

执行缺省操作,linux对每种信号都规定了默认操作
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值