# 信号，第 2 部分：待处理的信号和信号掩码

> 原文：<https://github.com/angrave/SystemProgramming/wiki/Signals%2C-Part-2%3A-Pending-Signals-and-Signal-Masks>

## 信号深度

## 如何了解有关信号的更多信息？

linux 手册页讨论了第 2 节中的信号系统调用。第 7 节中还有一篇较长的文章（尽管不在 OSX / BSD 中）：

```
man -s7 signal 
```

## 信号术语

*   生成 - 通过 kill 系统调用在内核中创建信号。
*   待定 - 尚未交付但很快将交付
*   已阻止 - 未传送，因为没有信号处理可以传送信号
*   交付 - 交付过程，正在采取所述行动
*   抓住 - 当进程停止信号来摧毁它并用它做其他事情时

## 什么是进程的信号处理？

对于每个过程，每个信号都有一个配置，这意味着当信号传递给过程时会发生什么动作。例如，默认处置 SIGINT 是终止它。信号处理可以通过调用 signal（）来改变（这是简单但不可移植的，因为它在不同的 POSIX 架构上的实现有细微的变化，也不推荐用于多线程程序）或`sigaction`（稍后讨论）。您可以将过程对所有可能信号的处置想象为函数指针条目表（每个可能的信号一个）。

信号的默认处置可以是忽略信号，停止进程，继续停止进程，终止进程，或终止进程并转储'核心'文件。请注意，核心文件是可以使用调试器检查的进程内存状态的表示。

## 多个信号可以排队吗？

否 - 但是可能有信号处于挂起状态。如果信号处于待处理状态，则表示尚未交付给该进程。信号待决的最常见原因是进程（或线程）当前阻止了该特定信号。

如果是特定信号，例如 SIGINT 正在等待，因此无法再次排队相同的信号。

可能在待处理状态下具有多个不同类型的信号。例如，SIGINT 和 SIGTERM 信号可能正在等待（即尚未传送到目标进程）

## 如何阻止信号？

通过设置过程信号掩码，或者在编写多线程程序时，可以阻止信号（意味着它们将保持在挂起状态），线程信号掩码。

## 线程/子项中的处置

## 创建新线程时会发生什么？

新线程继承了调用线程掩码的副本

```c
pthread_sigmask( ... ); // set my mask to block delivery of some signals
pthread_create( ... ); // new thread will start with a copy of the same mask
```

## 分叉时会发生什么？

子进程继承父进程信号处理的副本。换句话说，如果您在分叉之前安装了 SIGINT 处理程序，那么如果将 SIGINT 传递给子进程，子进程也将调用处理程序。

注意孩子的待处理信号是 _ 而不是 _ 在分叉期间继承的。

## exec 期间会发生什么？

信号掩码和信号配置都转移到执行程序。 [https://www.gnu.org/software/libc/manual/html_node/Executing-a-File.html#Executing-a-File](Source) 也会保留待处理信号。信号处理程序被重置，因为原始处理程序代码与旧进程一起消失。

## 叉期间会发生什么？

子进程继承父进程的信号处理副本和父进程信号掩码的副本。

例如，如果在父母中阻止`SIGINT`，它也将在孩子中被阻止。例如，如果父级为 SIG-INT 安装了处理程序（回调函数），则子级也将执行相同的行为。

然而，待定信号不是由孩子继承的。

## 如何在单线程程序中阻止信号？

使用`sigprocmask`！使用 sigprocmask，您可以设置新掩码，将要阻止的新信号添加到进程掩码，以及取消阻止当前阻塞的信号。您还可以通过传入 oldset 的非 null 值来确定现有掩码（并在以后使用它）。

```
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);` 
```

从 sigprocmask 的 Linux 手册页，

```
SIG_BLOCK: The set of blocked signals is the union of the current set and the set argument.
SIG_UNBLOCK: The signals in set are removed from the current set of blocked signals. It is permissible to attempt to unblock a signal which is not blocked.
SIG_SETMASK: The set of blocked signals is set to the argument set. 
```

sigset 类型表现为位图，除了使用函数而不是使用＆amp;显式设置和取消设置位。和|。

在修改一位之前忘记初始化信号集是一个常见的错误。例如，

```c
sigset_t set, oldset;
sigaddset(&set, SIGINT); // Ooops!
sigprocmask(SIG_SETMASK, &set, &oldset)
```

正确的代码将该集初始化为全部打开或全部关闭。例如，

```c
sigfillset(&set); // all signals
sigprocmask(SIG_SETMASK, &set, NULL); // Block all the signals!
// (Actually SIGKILL or SIGSTOP cannot be blocked...)

sigemptyset(&set); // no signals 
sigprocmask(SIG_SETMASK, &set, NULL); // set the mask to be empty again
```

## 如何在多线程程序中阻止信号？

多线程程序中的阻塞信号类似于单线程程序：

*   使用 pthread_sigmask 而不是 sigprocmask
*   阻止所有线程中的信号以防止其异步传递

确保信号在所有线程中被阻止的最简单方法是在创建新线程之前在主线程中设置信号掩码

```c
sigemptyset(&set);
sigaddset(&set, SIGQUIT);
sigaddset(&set, SIGINT);
pthread_sigmask(SIG_BLOCK, &set, NULL);

// this thread and the new thread will block SIGQUIT and SIGINT
pthread_create(&thread_id, NULL, myfunc, funcparam);
```

正如我们在 sigprocmask 中看到的那样，pthread_sigmask 包含一个'how'参数，用于定义如何使用信号集：

```c
pthread_sigmask(SIG_SETMASK, &set, NULL) - replace the thread's mask with given signal set
pthread_sigmask(SIG_BLOCK, &set, NULL) - add the signal set to the thread's mask
pthread_sigmask(SIG_UNBLOCK, &set, NULL) - remove the signal set from the thread's mask
```

## 如何在多线程程序中提供待处理信号？

信号被传送到任何未阻塞该信号的信号线程。

如果两个或多个线程可以接收信号，那么哪个线程将被中断是任意的！