信号处理与等待:深入解析sigaction、pause、sigsuspend和sigwait
立即解锁
发布时间: 2025-08-22 01:29:09 阅读量: 1 订阅数: 7 


UNIX系统编程:通信、并发与线程精解
### 信号处理与等待:深入解析sigaction、pause、sigsuspend和sigwait
#### 1. 信号捕获与忽略:sigaction函数
sigaction函数允许调用者检查或指定与特定信号关联的操作。其原型如下:
```c
#include <signal.h>
int sigaction(int sig, const struct sigaction *restrict act,
struct sigaction *restrict oact);
```
- `sig`:指定信号编号。
- `act`:指向`struct sigaction`结构的指针,指定要采取的操作。
- `oact`:指向`struct sigaction`结构的指针,接收与信号关联的先前操作。
如果`act`为`NULL`,调用`sigaction`不会更改与信号关联的操作;如果`oact`为`NULL`,调用不会返回与信号关联的先前操作。
`sigaction`成功时返回0,失败时返回 -1 并设置`errno`。以下是可能的错误码:
| errno | 原因 |
| --- | --- |
| EINVAL | sig是无效的信号编号,或尝试捕获无法捕获的信号,或尝试忽略无法忽略的信号 |
| ENOTSUP | sa_flags的SA_SIGINFO位被设置,且实现不支持POSIX:RTS或POSIX:XSI |
`struct sigaction`结构至少包含以下成员:
```c
struct sigaction {
void (*sa_handler)(int); /* SIG_DFL, SIG_IGN or pointer to function */
sigset_t sa_mask; /* additional signals to be blocked during execution of handler */
int sa_flags; /* special flags and options */
void(*sa_sigaction) (int, siginfo_t *, void *); /* realtime handler */
};
```
- `sa_handler`和`sa_sigaction`的存储可能重叠,应用程序应仅使用其中一个成员来指定操作。
- 如果`sa_flags`字段的`SA_SIGINFO`标志被清除,`sa_handler`指定为指定信号要采取的操作。
- 如果`sa_flags`字段的`SA_SIGINFO`标志被设置,并且实现支持POSIX:RTS或POSIX:XSI扩展,`sa_sigaction`字段指定一个信号捕获函数。
以下是一些使用`sigaction`的示例:
- **设置SIGINT信号处理程序**:
```c
struct sigaction newact;
newact.sa_handler = mysighand;
/* set the new handler */
newact.sa_flags = 0;
/* no special options */
if ((sigemptyset(&newact.sa_mask) == -1) ||
(sigaction(SIGINT, &newact, NULL) == -1))
perror("Failed to install SIGINT signal handler");
```
- **忽略SIGINT信号(如果当前为默认操作)**:
```c
struct sigaction act;
if (sigaction(SIGINT, NULL, &act) == -1)
/* Find current SIGINT handler */
perror("Failed to get old handler for SIGINT");
else if (act.sa_handler == SIG_DFL) {
/* if SIGINT handler is default */
act.sa_handler = SIG_IGN;
/* set new SIGINT handler to ignore */
if (sigaction(SIGINT, &act, NULL) == -1)
perror("Failed to ignore SIGINT");
}
```
- **捕获Ctrl - C生成的SIGINT信号**:
```c
void catchctrlc(int signo) {
char handmsg[] = "I found Ctrl-C\n";
int msglen = sizeof(handmsg);
write(STDERR_FILENO, handmsg, msglen);
}
struct sigaction act;
act.sa_handler = catchctrlc;
act.sa_flags = 0;
if ((sigemptyset(&act.sa_mask) == -1) ||
(sigaction(SIGINT, &act, NULL) == -1))
perror("Failed to set SIGINT to handle Ctrl-C");
```
在信号处理程序中,应使用异步信号安全的函数,如`write`,因为POSIX保证`write`是异步信号安全的,而`fprintf`和`strlen`等函数没有类似保证。
#### 2. 信号等待函数
信号提供了一种无需忙等待即可等待事件的方法。POSIX的`pause`、`sigsuspend`和`sigwait`函数提供了三种机制,用于在信号发生之前挂起进程。
##### 2.1 pause函数
`pause`函数挂起调用线程,直到传递一个信号,该信号的操作要么是执行用户定义的处理程序,要么是终止进程。如果操作是终止,`pause`不会返回;如果信号被进程捕获,`pause`在信号处理程序返回后返回。
```c
#include <unistd.h>
int pause(void);
```
`pause`总是返回 -1。如果被信号中断,`pause`将`errno`设置为`EINTR`。
要使用`pause`等待特定信号,必须确定哪个信号导致`pause`返回。由于此信息不可直接获取,信号处理程序必须设置一个标志,供程序在`pause`返回后检查。
以下是使用`pause`等待信号的代码示例及问题分析:
```c
static volatile sig_atomic_t sigreceived = 0;
while(sigreceived == 0)
pause();
```
如果在测试`sigreceived`和调用`pause`之间传递了信号,先前传递的信号不会影响`pause`,`pause`不会返回,直到另一个信号或同一信号的另一次出现被传递给进程。
```c
static volatile sig_atomic_t sigreceived = 0;
int signum;
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, signum);
sigprocmask(SIG_BLOCK, &sigset, NULL);
while(sigreceived == 0)
pause();
```
此代码段在信号被阻塞时执行`pause`,导致程序永远不会收到信号,`pause`也不会返回。如果在执行`pause`之前解除信号阻塞,可能会在解除阻塞和执行`pause`之间收到信号。
##### 2.2 sigsuspend函数
`pause`之前信号传递的问题是原始UNIX信号的主要问题之一,而`sigsuspend`函数提供了解决此问题的方法。它将信号掩码设置为`sigmask`指向的掩码,并挂起进程,直到进程捕获到信号。当捕获信号的处理程序返回时,`sigsuspend`返回。
```c
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
```
`sigsuspend`总是返回 -1 并设置`errno`。如果被信号中断,`sigsuspend`将`errno`设置为`EINTR`。
以下是使用`sigsuspend`等待信号的代码示例及问题分析:
```c
sigfillset(&sigmost);
sigdelset(&sigmost, signum);
sigsuspend(&sigmost);
```
此代码段存在与使用`pause`相同的问题。如果在代码段开始之前传递了信号,进程仍会挂起自身,如果没有生成另一个`signum`信号,将导致死锁。
正确等待单个信号的代码示例:
```c
static volatile sig_atomic_t sigreceived = 0;
sigset_t maskall, maskmost, maskold;
int signum = SIGUSR1;
sigfillset(&maskall);
sigfillset(&maskmost);
sigdelset(&maskmost, signum);
sigprocmask(SIG_SETMASK, &maskall, &maskold);
if (sigreceived == 0)
sigsuspend(&maskmost);
sigprocmask(S
```
0
0
复制全文
相关推荐





