signal

Linux系统编程信号

信号的概念

clip_image001

       信号时提供处理异步事件机制的软件中断。这些事件可以来自系统外部(用户能够通过输入CTRL+cCtrl+\,或者是终端驱动程序分配给信号控制字符的其他任何键来请求内核产生信号;),或者来自程序或内核内部的活动(内核:当进程执行出错时,内核会给进程发送一个信号,例如非法段存取(内存访问违规)、浮点数溢出等;进程:一个进程可以通过系统调用kill给另一个进程发送信号,一个进程可以通过信号和另外一个进程进行通信。由进程的某个操作产生的信号称为同步信号(synchronous signals),例如除0;由象用户击键这样的进程外部事件产生的信号叫做异步信号。(asynchronous signals))。

       这里要注意的是,不仅仅事件的发生是异步的,程序对信号的处理也是异步的。

三个处理信号过程

clip_image002

l  接收默认处理:接收默认处理的进程通常会导致进程本身消亡。例如连接到终端的进程,用户按下CTRL+c,将导致内核向进程发送一个SIGINT的信号,进程如果不对该信号做特殊的处理,系统将采用默认的方式处理该信号,即终止进程的执行;

l  忽略信号:进程可以通过代码,显示地忽略某个信号的处理,例如:signal(SIGINT,SIGDEF);但是某些信号是不能被忽略的;

l  捕捉信号并处理:进程可以事先注册信号处理函数,当接收到信号时,由信号处理函数自动捕捉并且处理信号。

l  注意:有两个信号既不能被忽略也不能被捕捉,它们是SIGKILLSIGSTOP。即进程接收到这两个信号后,只能接受系统的默认处理,即终止线程。

Linux支持的信号列表

可以使用kill –l来查看linux支持的信号列表

clip_image003

信号处理函数的过程:

(1)注册信号处理函数

 信号的处理是由内核来代理的,首先程序通过sigalsigaction函数为每个信号注册处理函数,而内核中维护一张信号向量表,对应信号处理机制。这样,在信号在进程中注销完毕之后,会调用相应的处理函数进行处理。

(2)信号的检测与响应时机

在系统调用或中断返回用户态的前夕,内核会检查未决信号集,进行相应的信号处理。

(3)处理过程:

程序运行在用户态时->进程由于系统调用或中断进入内核->转向用户态执行信号处理函数->信号处理函数完毕后进入内核->返回用户态继续执行程序

 首先程序执行在用户态,在进程陷入内核并从内核返回的前夕,会去检查有没有信号没有被处理,如果有且没有被阻塞就会调用相应的信号处理程序去处理。首先,内核在用户栈上创建一个层,该层中将返回地址设置成信号处理函数的地址,这样,从内核返回用户态时,就会执行这个信号处理函数。当信号处理函数执行完,会再次进入内核,主要是检测有没有信号没有处理,以及恢复原先程序中断执行点,恢复内核栈等工作,这样,当从内核返回后便返回到原先程序执行的地方了。

fork信号

clip_image005

当使用fork()函数时,子进程会继承父进程完全相同的信号语义,这也是有道理的,因为父子进程共享一个地址空间,所以父进程的信号处理程序也存在于子进程中。

 

脚本控制

脚本控制

13.1 处理信号

13.1.1 Linux信号回顾

       可以使用kill –l查看linux的信号集。

13.1.2 生成信号

       平时我们使用的CTRL+C就为发送了SIGINT信号;而CTRL+Z为生成了SIGTSTP信号,停止进程而不终止进程,停止进城后程序仍然爱留在内存中,能够从停止的地方继续运行。

13.1.3 捕获信号

       可以使用trap commands signals来捕获信号并拦截,如果脚本收到在trap命令中列出的信号,它将保护该信号不被shell处理。

       例如

       trap “echo haha” SIGINT SIGTERM,在该shell遇到两个信号时只是打印出haha而不会终止程序。

13.1.4 捕获脚本退出

       与上述相同,比如trap “echo haha” EXIT就会捕获退出信号,就算是CTRL+C也会捕获该信号。

13.1.5 移除捕获

       可以使用trap – signal来溢出捕获,但是如果在移除捕获之前收到了信号,脚本仍将根据trap命令处理该信号。

13.2 以后台模式运行脚本

13.2.1 以后台模式运行

       命令后添加&即可后台运行。

13.2.3 退出终端

       需要注意的是,运行后台进程的终端如果关闭的话,所有的后台进程也将退出。

13.3 在不使用控制台的情况下运行脚本

$nohup ./testshell &

nohup命令运行另一个命令阻塞发送到进程的任何SIGHUP信号,这可以防止在退出终端会话时退出进程。

13.4 作业控制

       重启、停止、终止和恢复作业的操作称为作业控制job control

13.4.1 查看作业

       命令jobs输出的信息中带有加号+的作业被视为默认作业,带有减号的作业是在处理完当前默认作业之后将称为默认作业的作业。在某一个时间点,无论shell中运行了多少作业,只能有一个带有加号+的作业,也只能有一个带有减号的作业。

13.4.2 重新启动停止的作业

       以后台模式启动bg n,以前台模式启动fg n,其中n为作业的编号。

13.5 变得更好

       默认情况下,从shell启动的所有进程在linux系统上的调度优先级scheduling priority都相同,为0。调度优先级的范围为从-20(最高优先级)到20(最低优先级)。

13.5.1 nice命令

       nice命令可以在启动命令时设置它的调度优先级。不过只能让命令在更低的优先级下运行,而不能调高优先级。这是一个安全特性,防止用户以高优先级启动所有命令。

       格式为 : nice –n N command

13.5.2 renice命令

       格式为renice N –p PID ,其中N为想调至的优先级参数。

       命令renice可以指定运行进程的PID以更改优先级。与nice命令一样,renice命令也有几个限制:

 

l  只能对拥有的进程使用renice命令;

l  只能使用renice命令将进程调制更低的优先级;

l  跟用户可以使用renice命令敬爱那个任何进程调至任何优先级。

13.6 准确无误地运行

13.6.1 使用at命令调度作业

       at命令运行指定linux系统运行脚本的时间。at命令将作业提交到一个队列,并指示shell在何时运行该作业。另一个命令atd以后台模式运行,并检查作业队列以运行作业。

       atd命令检查系统上的特殊目录,通常是/var/spool/at,以便运行使用at命令提交的作业。

       at命令的格式很简单:

       at [-f filename] time :其中filename为希望执行的脚本文件,time为希望运行的时间。其中time的方式可以发挥你的创造性了。

       使用at命令时,作业将提交到作业队列job queue中,有26中不同的作业队列可用于不同的优先级水平。使用小写字母az引用作业队列。

       默认情况下,所有的at作业都提交到作业队列a,即最高的优先级队列。如果希望以较低的优先级运行作业,可以使用-q参数指定字母。

       默认情况下,at作业完成后,不会再显示器显示任何内容,但是系统将生成一个电子邮件信息。

       使用atq命令可以查看系统中排队的作业。

       可以使用atrm N命令移除排队的作业,其中N为作业编号。

13.6.2 使用batch命令

       命令batchat稍有不同,batch命令的作用不是安排脚本在预设的时间运行,而是安排脚本在系统使用率低时运行

       如果linux系统正处于高负载水平,batch命令将延迟提交作业的运行,直到系统负载减低为止。这对于服务器是个良好的特性,因为服务器在白天和夜晚的负载水平可能大不相同。batch会检查linux系统当前的平均负载水平,如果平均负载低于0.8,它将运行在作业队列中的作业。

       batch命令的格式与at类似:

       batch [-f filename] [time]

13.6.3 调度定期脚本

       Linux系统中的cron程序可以调度需要定期运行的饿作业,cron程序在后台运行,它从特殊表格(cron表格)中查找需要调度运行的作业。

       cron表格的格式如下:

min hour dayofmonth month dayofweek command

       可以使用crontab –l查看现有的cron表格。

       对于cron存在的问题是,如果系统处于关闭状态,作业将无法执行。而anacron程序使用时间戳确定调度的作业是够在正确的时间间隔运行,如果确实错过了该作业的调度运行时间,它将尽快自动运行改作业。这意味着,如果Linux系统关闭了好几天,当它再次开启时,任何计划在系统关闭器件运行的作业都将自动运行。

       anacron表格的格式稍微不同于cron表格的格式:

period delay identifier command

13.7 从头开始

13.7.1 在启动时启动脚本

       可以写入rc脚本中,随系统一起启动。

13.7.2 随新shell一起启动

       写入.bashrc或者.bash_profile中,随着打开shell时启动。