> 文章列表 > Linux进程信号

Linux进程信号

Linux进程信号

本篇博客我们开始讲述Linux中新的模块:进程信号

在这一模块中,我们需要讲述的内容有:进程信号的概念,信号的生命周期,函数冲入和关键字。

目录

1.信号概念

2.生命周期

2.1产生

2.1.1kill接口

2.1.2raise接口

2.1.3abort接口

2.1.4alarm

2.2注册

2.3注销

2.4处理

3.信号阻塞

3.1sigprocmask接口

1.信号概念

在Linux中我们需要了解的信号概念:软件中断 -- 通知一个进程发生了某个事件,打断当前进程的操作,去完成新发生的事件。(信号一定是多种多样,且能被识别的)

在之前的进程概念中我们提到过进程具有一种“可中断休眠态,”打断该状,态的休眠,便是由信号来完成的。进程休眠被打断后,意味着某些事件等待执行,这也是我们打断进程休眠的目的。

输入kill -l 指令,我们便可查看Linux中所有信号的内容,我们可以发现31~34中间不存在32和33信号,所以信号共计有62种信号。

对于前31位信号都是有独特的名称的,对于后31位信号没有独特的名称且和数字的加减组合在一起。 这意味着对于前31位信号肯定各自都代表着一种事件,而对于后31位信号不一定一一对应某种事件。

造成这样的原因是来自于Linux发展过程中积累造成的,对于前31位信号中,我们可以发现其中10和12位信号名称:"SIGUSR1"和“SIFUSR2”,这便是设计者为使用者设计出可供自己定义的信号。但是随着发展,这两种信号不足以完成一些较大的内容,所以后来的设计者便在原有31位信号的基础上,添加定义了后31位信号。

前后31位信号的特性:

  • 1~31信号:非可靠信号,非实时信号;
  • 34~64信号:可靠性好,实时信号。

其中可靠性好和非可靠信号的区别就在于:信号所代表的内容能否一定被执行;实时信号和非实时信号的区别在于:二者共同发送给进程,实时信号处理的优先级要高于非实时信号。

对于每种型号的具体内容介绍,笔者引用apue(圣经)中的介绍:

2.生命周期

信号的生命周期有四个阶段:产生 -> 注册 -> 注销 -> 处理。

2.1产生

信号的产生:有两种产生方式,硬件产生和软件产生。

  • 硬件产生:ctrl+c,ctrl+z ……
  • 软件产生:kill指令……

虽然二者的操作方式不同,但是二者的本质都是向操作系统发送一个信号,来对进程实现干预。

2.1.1kill接口:

int kill(pid_t pid, int signum);

kill接口的作用便是向指定的进程发送一个指定的信号。

通过对kill接口进行简单的编写,便可得到一些结果的显示: 

2.1.2raise接口

int raise(int signum);

raise接口的作用便是向进程自身发送一个信号。

2.1.3abort接口

void abort();

abort接口的作用便是向进程自身发送一个SIGABRT信号(异常退出信号)。

2.1.4alarm

int alarm(int sec);

alarm接口的作用便是sec秒之后向进程发送一个SIGALRM信号(超时)。

2.2注册

信号的注册:在进程中做标记,让进程能够知道自己收到了某个信号。

原理:在进程pcb中存在一个pending未决信号集合--位图(标记进程收到了某个信号),当我们向进程发送一个信号,便是给sigqueue链表中添加一个对应信息的节点,并且修改位图,将对应的信号值制1。

但是信号存在可靠信号和非可靠信号的区别,所以对于具体原理上,我们仍需要在原有基础上加以分类处理。

对于非可靠信号,当注册新信号时,若没有被注册,则注册并创建;若已经注册,则丢弃;

对于可靠信号,当注册新信号时,无论之前有无被注册,都会添加新的信号信息节点。

2.3注销

在信号被处理之前,我们便需要将信号注销,销毁信号存在的痕迹,这是为了防止该信号被重复处理。信号的注册同位图和信号链表有关,那么对于注销其实是与之相反的操作。

对于非可靠信号的注销:删除信号的信息节点,位图制0;

对于可靠信号的注销:删除信号的一个信息节点,当没有相同节点则位图制0。

2.4处理

在语言学习过程中,我们善于将每项功能封装成函数,对于信号的处理也是如此。信号的处理便是调用信号的事件处理函数。

信号的处理存在三种方式,分别为:

  • 默认处理:系统中已经预定义好的处理方式;
  • 忽略处理:空的处理方式;
  • 自定义处理:程序设计这自己定义的处理函数,替换掉原本的信号处理函数。

自定义处理的相关接口:

自定义的数据类型 :

typedef void (*sighandler_t) (int);

signal接口:

sighandler_t signal(int signum, sighandler_t handler);

signal接口会使用handler函数,替换掉signum信号当前的处理函数。即当进程收到signum信号,会使用handler函数进行处理。

其中handler存在两个关键字:SIG_DFL:信号默认处理方式和SIG_IGN:忽略处理。

我们编写一个简单的程序,来对signal接口进行使用:

 然后便可发现它的执行结果如下:

我们使用ctrl + c 的方式发送SIGINT信号无法将程序打断,因为我们在程序中将其替换成忽略处理。这便是signal接口的作用:替换信号执行函数。

3.信号阻塞

信号阻塞的概念:信号依然会被注册,但是信号暂时不会被处理。(直到被解除阻塞)

信号阻塞的原理:在进程pcb中存在一个block阻塞信号集合,当有信号被添加到block阻塞集合之中,就表示该进程暂时不处理该信号。

3.1sigprocmask接口

int sigprocmask(int how, sigset_t *set, sigset_t *old);

sigpromask接口用于操作阻塞,其中how:要对pdb中的信号阻塞集合进行的操作;old:将修改前的block集合中的信息添加到old(便于还原)。

set相关关键字:

  • SIG_BLOCK:block |= set,将set集合中的信号添加到block中;
  • SIG_UNBLOCK:block &= ~set,将set中的信号解除阻塞;
  • SIG_SETMASK:block = set。

返回值:成功返回0;失败返回-1。

猜谜语网