信号

1.基本概念

信号是事件发生时对进程的通知机制,也可以把它称为软件中断。信号与硬件中断的相似之处在于能够打断程序当前执行的正常流程, 其实是在软件层次上对中断机制的一种模拟。 大多数情况下,是无法预测信号达到的准确时间,所以,信号提供了一种处理异步事件的方法

1.1信号的用途

一个有“一定权限”的进程(比如内核)可以给另一个进程发送信号,所以信号是一种进程间通信的机制。当某个事件发生时,一个进程通知另一个进程。

1.2信号的使用案例

  • Linux在终端中输入 Ctrl + Z可以使内核发送**暂停信号 **(SIGCONT )以暂停前台的进程
  • Linux在终端中输入 Ctrl + C可以使内核发送中断信号(SIGINT )以结束前台的进程
  • 用户可以通过 kill() 系统调用将任意信号发送给其它进程。当然对此是有所限制的,接收信号的进程和发送信号的进程的所有者必须相同,亦或者发送信号的进程的所有者是 root 用户
  • 硬件发生异常,即硬件检测到错误条件并通知内核,随即再由内核发送相应的信号给相关进程

前台进程:当用终端运行程序时,该进程会占用终端,即为一个前台进程。如果一个进程在后台运行,没占用终端,那他就不算前台进程。

1.3对信号的处理

  • 忽略信号

在信号到达后,不去处理,就像没有一样。但SIGCONTSIGINT这2个信号不能忽略,因为它们向内核和root提供了使进程终止或停止的可靠方法

  • 捕获信号

可以使用signal()系统调用为信号注册一个处理函数,在进程捕获到该信号后会调用这个函数(与Qt的信号槽类似)

  • 执行系统的默认操作

操作系统为每个信号都实现了一个默认的处理函数,对于大多数信号,系统的默认处理函数就是终止

1.4信号在Linux中的实现

信号在Linux中实际上就是个int型变量,下面列出部分信号的序号

1
2
3
4
5
6
7
8
9
10
11
12
/* Signals. */
#define SIGHUP 1 /* Hangup (POSIX). */
#define SIGINT 2 /* Interrupt (ANSI). */
#define SIGQUIT 3 /* Quit (POSIX). */
#define SIGILL 4 /* Illegal instruction (ANSI). */
#define SIGTRAP 5 /* Trace trap (POSIX). */
#define SIGABRT 6 /* Abort (ANSI). */
#define SIGIOT 6 /* IOT trap (4.2 BSD). */
#define SIGBUS 7 /* BUS error (4.2 BSD). */
#define SIGFPE 8 /* Floating-point exception (ANSI). */
#define SIGKILL 9 /* Kill, unblockable (POSIX). */
...

2.信号的分类

(1)信号根据其可靠性可以分为:可靠信号和不可靠信号。

  • 不可靠信号:最早被提出来的信号,无法排队处理,可能处理错误或丢失
  • 可靠信号:支持排队,不会丢失的信号

(2)信号还可以分为:实时信号和非实时信号

  • 实时信号都是可靠的
  • 非实时信号都是不可靠的

3.信号的处理函数

Linux中,可以通过siganl()或者sigaction()为某个信号指定其对应的处理函数,就相当于Qt里的槽函数。

4.发送信号

进程中将信号发送给另一个进程是需要权限的,并不是可以随便给任何一个进程发送信号,超级用户root 进程可以将信号发送给任何进程,但对于非超级用户(普通用户)进程来说,其基本规则是发送者进程的实际用户 ID 或有效用户 ID 必须等于接收者进程的实际用户 ID 或有效用户 ID。

4.1kill()系统调用

kill()系统调用的原型如下:

1
2
3
4
#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

它可以将任意信号发给某个进程

4.2raise()系统调用

raise()系统调用的原型如下:

1
2
#include <signal.h>
int raise(int sig);

raise()用于向自己发送信号,等价于kill(getpid(),sig_num);

5.信号集

信号集是一个能够表示多个信号的数据结构,其定义如下:

1
2
3
4
5
# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{
unsigned long int __val[_SIGSET_NWORDS];
} sigset_t;

使用这个结构体可以表示一组信号,将多个信号添加到该数据结构中, 当然 Linux 系统了用于操作sigset_t 信号集的 API,譬如 sigemptyset()sigfillset()sigaddset()sigdelset()sigismember()

6.信号掩码

信号掩码是一个信号集,用于阻塞某些信号传递给该进程,就像Qt中的事件过滤器一样