Linux 信号
信号
信号是linux中进程间通信的一种方式,全称为软中断信号。它只是通知进程发生了某种事情,本身不传递任何其他信息。
-
可靠信号与不可靠信号
信号分为可靠信号与不可靠信号,可靠信号又称为实时信号,非可靠信号又称为非实时信号,信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号。
不可靠信号的特点在于:- 每次信号处理完之后,就会恢复成默认处理,如果希望后面可以再次捕捉信号,需要重新注册。
- 存在信号丢失的问题,进程收到的信号不作排队处理,相同的信号多次到来会合并为一个。
造成这种情况的原因主要在于Unix对信号处理比较简单,linux继承了Unix的信号处理,不过在后面进行了优化,出现了可靠信号。
-
信号产生
- 硬件方式:
- 用户输入:比如用户输入ctrl+c,就发送了一个SIGINT信号给当前的进程
- 硬件异常:CPU检测到内存非法访问等异常,通知内核生成相应信号,并发送给发生事件的进程。
- 软件方式:
- 命令行输入kill,发送对应类型的信号给指定的进程
- 进程调用系统调用,发送信号给其他进程
- 硬件方式:
-
信号处理
在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。因此,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个,也就是不可靠信号的信号丢失。
内 核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。
内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。
需要注意的一些地方:
1. 在一些系统中,当一个进程处理完中断信号返回用户态之前,内核清除用户区中设 定的对该信号的处理例程的地址,即下一次进程对该信号的处理方法又改为默认值,除非在下一次信号到来之前再次使用signal系统调用。这可能会使得进程 在调用signal之前又得到该信号而导致退出。在BSD中,内核不再清除该地址。但不清除该地址可能使得进程因为过多过快的得到某个信号而导致堆栈溢 出。为了避免出现上述情况。在BSD系统中,内核模拟了对硬件中断的处理方法,即在处理某个中断时,阻止接收新的该类中断。
2. 如果要捕捉的信号发生于进程正在一个系统调用中时,并且该进程睡眠在可中断的优先级上,这时该信号引起进程作一次longjmp,跳出睡眠 状态,返回用户态并执行信号处理例程。当从信号处理例程返回时,进程就象从系统调用返回一样,但返回了一个错误代码,指出该次系统调用曾经被中断。这要注 意的是,BSD系统中内核可以自动地重新开始系统调用。
3. 若进程睡眠在可中断的优先级上,则当它收到一个要忽略的信号时,该进程被唤醒,但不做longjmp,一般是继续睡眠。但用户感觉不到进程曾经被唤醒,而是象没有发生过该信号一样。
4. 内核对子进程终止(SIGCLD)信号的处理方法与其他信号有所区别。当进程检查出收到了一个子进程终止的信号时,缺省情况下,该进程 就象没有收到该信号似的,如果父进程执行了系统调用wait,进程将从系统调用wait中醒来并返回wait调用,执行一系列wait调用的后续操作(找 出僵死的子进程,释放子进程的进程表项),然后从wait中返回。SIGCLD信号的作用是唤醒一个睡眠在可被中断优先级上的进程。如果该进程捕捉了这个 信号,就象普通信号处理一样转到处理例程。如果进程忽略该信号,那么系统调用wait的动作就有所不同,因为SIGCLD的作用仅仅是唤醒一个睡眠在可被 中断优先级上的进程,那么执行wait调用的父进程被唤醒继续执行wait调用的后续操作,然后等待其他的子进程。
程序后台运行
当终端断开的时候,会收到 HUP信号从而关闭其所有子进程,程序无法后台运行,也就是因为受到HUP信号的影响。因此,就有两种途径让程序可以后台运行:让进程忽略 HUP 信号;让进程运行成为不属于此终端的子进程。
- nohup
nohup 的使用是比较常见的,也是十分方便的,只需在要处理的命令前加上 nohup 即可,标准输出和标准错误缺省会被重定向到 nohup.out 文件中。一般我们可在结尾加上"&"来将命令同时放入后台运行,也可用">filename 2>&1"来更改缺省的重定向文件名。 - setsid
setsid 的使用也是非常方便的,也只需在要处理的命令前加上 setsid。这样新进程的父id就是init进程,而不是当前终端的进程 ID了。 - ()&
将命令放在()中,就可以让命令在子shell中运行而非当前shell。如果在()中再加上&,那么运行的命令的父进程就是init进程了。 - disown
如果我们在运行的时候忘记了使用上面的方法,但是又不想重启程序,那么这时候,就可以使用diown了。
用disown -h jobspec来使某个作业忽略HUP信号。
用disown -ah 来使所有的作业都忽略HUP信号。
用disown -rh 来使正在运行的作业忽略HUP信号。
需要注意的是,当使用过 disown 之后,会将把目标作业从作业列表中移除,我们将不能再使用jobs来查看它,但是依然能够用ps -ef查找到它。 - screen
screen可以让用户在多个会话之间切换,也可以共享会话。
使用screen创建的会话,父进程都是init进程。当前session断开就不会影响到对应的会话,而且其他session和可以登录到这个会话操作。