信号(signal)

   所谓信号,就简单场景来说,启动一个前台进程,用户按下Ctrl_C可将进程终止。


   在这呢,简单说说后台进程能否用Ctrl_C终止?

   一个命令后面加个&便可在后台执行。这样Shell不必等待进程结束就可以新的命令,启动新的进程。Shell可以同时执行一个前台进程和多个后台进程,只有前台进程才能接到Ctrl_C控制键产生的信号。

   若在前台执行死循环,可用Ctrl_C,kill -9 id 将进程终止

   若在后台执行死循环,Ctrl_C不会将进程杀死,kill -9 id可以将进程终止,kill -11 id也可以将进程终止,但是产生段错误。


一、产生信号

基本概念:

1. 如何产生信号呢?

   <1> 键盘。当某个进程正在运行时,按下Ctrl_C便可终止进程。

   <2> 命令。如kill -9 3212 将id为3212的进程终止。

   <3> 函数。如kill,alarm,abort函数。

   <4> 操作系统捕捉。软件,硬件异常

2. 如何处理信号?

   <1> 忽略信号

   <2> 执行该信号的默认动作,一般是将该进程终止

   <3> 捕捉。自定义函数。

3. 当进程在收到信号时,并不是立即处理,而是在合适的时候处理。应先将所接受的信号先保存在自己    的PCB中。


几个函数:

  1. kill函数:给指定的进程发送指定的信号

    函数原型:int kill(pid_t pid,int signo)

    返回值:成功为0,失败为-1

  2. rasie函数:给当前进程发送指定的信号

    函数原型:int raise(int signo)

    返回值:成功为0,失败为-1

  3. abort函数:当前进程接受到SIGABRT信号而异常终止

    函数原型:void abort(void)

  4. alarm函数:在seconds秒后给当前进程发SIGALRM信号,默认终止当前进程

    unsigned int alarm(unsigned int seconds)

    返回值:返回0或者设定闹钟剩下的时间

    当seconds为0时,表示取消该闹钟,并返回设定闹钟剩下的时间。


  5. signal函数:设置某一信号的对应动作

    函数原型:

    wKiom1eXY_SjncZ2AAAbEEeCJM0101.png  


检验当seconds为0时,取消闹钟,函数返回以前设定的闹钟剩下的时间

wKioL1eXY_Sh6lawAABWuH1fqNM016.png



wKioL1eXZbOiqp2dAADX1ftg28Y796.png


二、阻塞信号

基本概念:

  1. 实际执行信号的处理动作称为信号抵达忽略是递达的一种

  2. 信号从产生到递达之间的状态称为信号未决

  3. 进程可以选择阻塞某一信号。被阻塞的信号将保持在未决状态,直到进程解除对此信号的阻塞,才能执行递达的动作。



 信号在内核中的表示示意图:

wKioL1eXZ8KxTZDnAABqddkoiW0517.png

每个信号都有两个标志位分别为block(阻塞)和pending(未决),还有一个函数指针表示处理动作。

  1. SIGUP:信号未产生也为阻塞,当它递达时执行默认处理动作

  2. SIGINT:信号产生,但是阻塞。它的处理动作是忽略。阻塞不解除,都不会执行处理动作。

  3. SIGQUIT:没有信号产生,一旦产生就会被阻塞,处理动作是用户自定义函数sighandler。



每个信号只有一个bit的未决标志和阻塞标志,非0即1,不记录该信号产生的次数。因此,未决和阻塞标志可以用相同的数据类型sigset_t储存,sigset_t称为信号集。这个类型表示信号的有效和无效。在阻塞信号集(信号屏蔽字)中即block表,1表示信号阻塞,0表示不阻塞。在未决信号集即pending表,1表示信号产生未决状态,0表示没有产生信号。总而言之,信号集为能够表示信号类型的0,1序列


注:不可使用位操作操作信号集,有专有的函数操作


信号集操作函数:

wKioL1eXccyR5JwEAABIzEmRaak696.png

sigemptyset:初始化set所指向的信号集,使其中所有的信号对应的bit清零

sigfillset:初始化set所指向的信号集,使其中所有的信号对应的bit置1

sigaddset:在该信号集中添加某一信号

sigdelset:在该信号集中删除某一信号

sigismember:判断一个信号集的有效信号中是否包含某种信号,若包含返回1,不包含返回0,出错返               回-1


sigprocmask函数:读取或更改进程的信号屏蔽字(阻塞信号集)

wKioL1eXc2vxzsqgAAAdBn_xyPg925.png

返回值:成功为0,失败为-1.

set:非空,更改进程的信号屏蔽字,参数how指示如何更改

set,oset:非空,则先将原来的信号屏蔽字备份到oset,然后根据set,how更改信号屏蔽字


how参数的取值:(假设信号屏蔽字为mask)

   SIG_BLOCK:set包含了添加到当前信号屏蔽字的信号,相当于mask = mask | set

   SIG_UNBLOCK:set包含从当前信号屏蔽字中解除阻塞的信号,相当于mask = mask&~set

   SIG_SETMASK:设置当前信号屏蔽字为set所指向的值,相当于mask = set


sigpending函数:读取当前进程的未决信号集

    int sigpending(sigset_t *set);

    返回值:成功为0,出错为-1


三.捕捉信号

   

   信号捕捉过程<4次权限转换>:

 wKioL1eXjKyhteJmAAG7abIug3A491.png

   

前面曾提过当一个进程收到信号时,并不是立即处理,而是在合适的时候,所谓的合适的时候就是

从内核态返回用户态时切换处理信号当内核处理完异常或中断时,会先检查当前进程中是否有可以被递达的信号,有,如果信号的处理方式捕捉:从内核态调到用户态执行代码,之后返回内核态,从内核态返回用户态即上次被中断或者异常的地方。若信号的处理方式为默认,则终止进程,若为忽略,从Pending表中删除该信号,即将1变为0,直接跳到用户态。


sigaction函数:读取和修改与指定信号相关联的处理动作。

wKiom1eXjj6w-sciAAAlU9rxVAQ528.png

返回值:成功为0,出错为-1.

signo:指定信号的编号

act:非空。根据act修改该信号的处理动作

oact:非空。传出该信号原来的处理动作

act和oact指向如下结构体:

wKiom1eXjj-CWNDNAACus2Ag92A820.png

sa_hander:若赋值为SIG_IGN 表示忽略信号;SIG_DFL表示执行默认动作;赋值为一个函数指针表示自定义函数捕捉信号。这个函数不是被main函数调用,而是被系统函数所调用。

sa_mask:需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字

sa_flags:包含一些选项


pause函数:使调用进程挂起直到有信号递达

函数原型:int pause(void)      返回值:只有出错的返回值

  1. 信号的处理动作为终止进程,则进程终止,不执行pause

  2. 信号的处理动作为忽略,进程继续挂起,pause不返回

  3. 信号的处理动作为捕捉,则调用了信号处理函数后返回-1


实现sleep函数

wKioL1eXk-2CGGfYAABK35w5z_g169.png

测试结果:每个1秒打印一个hello bit

wKiom1eXk-3ioGIrAAAVtfol8AY221.png



测试31个普通信号中那些能被捕捉,那些不能被捕捉。


wKioL1eXif2R4TsvAABHUnZV2gI338.png

   

测试结果:

wKiom1eXif3hjI2QAAAwvYl_FKE915.pngwKiom1eXif6DZgNOAAA1SsUDd8s451.png


分析:首先对每个信号都设置对应的行为,当信号为9时,直接终止进程,若将信号9除外,当信号为19 时,进程终止。若将信号9,19除外,则每个信号都可以被捕捉。

   



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章