首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >进程信号

进程信号

作者头像
海盗船长
发布2020-08-27 17:30:03
1.2K0
发布2020-08-27 17:30:03
举报
文章被收录于专栏:基础知识文章基础知识文章

信号入门

技术中的信号

  1. 用户输入命令,在Shell下启动一个前台进程。
  2. 用户按下 Ctrl-C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程
  3. 前台进程因为收到信号,进而引起进程退出
注意
  1. Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
  2. Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C 这种控制键产生的信号。
  3. 前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到 SIGINT 信号而终止,所以信号相对于进程的控制流程来说是异步的。
信号概念

信号是进程之间事件异步通知的一种方式,属于软中断。

用kill -l命令可以察看系统定义的信号列表

在这里插入图片描述
在这里插入图片描述
  • 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2
  • 编号34以上的是实时信号。这些信号各自在什么条件下产生,默认的处理动作是什么,在signal(7)中都有详细说明: man 7 signal
在这里插入图片描述
在这里插入图片描述
信号处理常见方式概览

可选的处理动作有以下三种:

  1. 忽略此信号。
  2. 执行该信号的默认处理动作。
  3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉(Catch)一个信号。

产生信号

通过终端按键产生信号

SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,现在我们来验证一下。

调用系统函数向进程发信号

首先在后台执行死循环程序,然后用kill命令给它发SIGSEGV信号。

在这里插入图片描述
在这里插入图片描述
  • 4568是test进程的id。之所以要再次回车才显示 Segmentation fault ,是因为在4568进程终止掉 之前已经回到了Shell提示符等待用户输入下一条命令,Shell不希望Segmentation fault信息和用 户的输入交错在一起,所以等用户输入命令之后才显示。
  • 指定发送某种信号的kill命令可以有多种写法,上面的命令还可以写成 kill -SIGSEGV 4568 或 kill -11 4568 , 11是信号SIGSEGV的编号。以往遇 到的段错误都是由非法内存访问产生的,而这个程序本身没错,给它发SIGSEGV也能产生段错误。
kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。raise函数可以给当前进程发送指定的信号(自己给自己发信号)。
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
这两个函数都是成功返回0,错误返回-1。
abort函数使当前进程接收到信号而异常终止。
#include <stdlib.h>
void abort(void);
就像exit函数一样,abort函数总是会成功的,所以没有返回值。

由软件条件产生信号

SIGPIPE是一种由软件条件产生的信号,在“管道”中已经介绍过了。这次主要介绍alarm函数 和SIGALRM信号。

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理
动作是终止当前进程。

这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数。 例如:

在这里插入图片描述
在这里插入图片描述

这个程序的作用是1秒钟之内不停地数数,1秒钟到了就被SIGALRM信号终止。

硬件异常产生信号

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

阻塞信号

信号其他相关常见概念
  • 实际执行信号的处理动作称为信号递达
  • 信号从产生到递达之间的状态,称为信号未决
  • 进程可以选择阻塞某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作
  • 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
在内核中的表示

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

在这里插入图片描述
在这里插入图片描述
  • 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。
  • 如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。
捕捉信号
在这里插入图片描述
在这里插入图片描述
1. 内核如何实现信号的捕捉

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。 sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

2. 信号捕捉函数signal
man 2 signal

可重入函数

在这里插入图片描述
在这里插入图片描述
  • main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的 时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换 到sighandler函数,sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的 两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续 往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是,main函数和sighandler先后 向链表中插入两个节点,而最后只 有一个节点真正插入链表中了。
  • 像上例这样,insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入,insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为 不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant) 函数。想一下,为什么两个不同的控制流程调用同一个函数,访问它的同一个局部变量或参数就不会造成错乱?
如果一个函数符合以下条件之一则是不可重入的:

调用了malloc或free,因为malloc也是用全局链表来管理堆的。 调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-03-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 信号入门
    • 技术中的信号
      • 注意
      • 信号概念
    • 用kill -l命令可以察看系统定义的信号列表
      • 信号处理常见方式概览
  • 产生信号
    • 通过终端按键产生信号
      • 调用系统函数向进程发信号
        • 由软件条件产生信号
          • 硬件异常产生信号
            • 信号其他相关常见概念
            • 在内核中的表示
            • 捕捉信号
        • 阻塞信号
          • 可重入函数
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档