epoll_pwait系统调用及示例

epoll_pwait - 带信号掩码的epoll等待

函数介绍

epoll_pwait是epoll_wait的扩展版本,支持在等待期间设置临时的信号掩码,提供了更精确的信号控制能力。

函数原型

1
2
3
4
5
6
7
#include <sys/epoll.h>
#include <signal.h>

int epoll_pwait(int epfd, struct epoll_event *events,
int maxevents, int timeout,
const sigset_t *sigmask);

功能

等待epoll事件,同时可以指定在等待期间临时应用的信号掩码。

参数

  • int epfd: epoll实例的文件描述符

  • struct epoll_event *events: 用于存储就绪事件的数组

  • int maxevents: events数组的最大元素数

  • int timeout: 超时时间(毫秒)

  • const sigset_t *sigmask: 临时信号掩码(NULL表示不改变)

返回值

  • 成功时返回就绪事件的数量

  • 失败时返回-1,并设置errno

特殊限制

  • 需要Linux 2.6.19以上内核支持

  • 原子性地设置信号掩码和等待事件

相似函数

  • epoll_wait(): 基础版本

  • pselect(): 带信号掩码的select版本

  • ppoll(): 带信号掩码的poll版本

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>

int main() {
int epfd, sockfd, nfds;
struct epoll_event ev, events&#91;10];
sigset_t mask, orig_mask;

printf("=== Epoll_pwait 函数示例 ===\n");

// 创建epoll实例
epfd = epoll_create1(EPOLL_CLOEXEC);
if (epfd == -1) {
perror("epoll_create1 失败");
exit(EXIT_FAILURE);
}
printf("创建epoll实例: %d\n", epfd);

// 创建测试用的socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("创建socket失败");
close(epfd);
exit(EXIT_FAILURE);
}
printf("创建测试socket: %d\n", sockfd);

// 设置socket为非阻塞模式
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

// 添加socket到epoll监视集合
ev.events = EPOLLIN | EPOLLOUT;
ev.data.fd = sockfd;

if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {
perror("epoll_ctl 添加失败");
close(sockfd);
close(epfd);
exit(EXIT_FAILURE);
}
printf("添加socket到epoll监视集合\n");

// 示例1: 基本使用(不改变信号掩码)
printf("\n示例1: 基本使用(不改变信号掩码)\n");

nfds = epoll_pwait(epfd, events, 10, 1000, NULL);
if (nfds == -1) {
if (errno == EINTR) {
printf("等待被信号中断\n");
} else {
perror("epoll_pwait 失败");
}
} else {
printf("等待完成,就绪事件数: %d\n", nfds);
}

// 示例2: 设置临时信号掩码
printf("\n示例2: 设置临时信号掩码\n");

// 初始化信号掩码
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigaddset(&mask, SIGUSR2);

printf("设置临时阻塞SIGUSR1和SIGUSR2信号\n");

nfds = epoll_pwait(epfd, events, 10, 2000, &mask);
if (nfds == -1) {
if (errno == EINTR) {
printf("等待被未阻塞的信号中断\n");
} else {
perror("epoll_pwait 失败");
}
} else {
printf("等待完成,就绪事件数: %d\n", nfds);
}

// 示例3: 与普通epoll_wait对比
printf("\n示例3: 与epoll_wait对比\n");

printf("epoll_wait vs epoll_pwait的区别:\n");
printf("epoll_wait:\n");
printf(" - 使用当前进程的信号掩码\n");
printf(" - 无法在调用中临时改变信号掩码\n\n");

printf("epoll_pwait:\n");
printf(" - 可以指定临时信号掩码\n");
printf(" - 原子性地设置掩码和等待\n");
printf(" - 避免竞态条件\n\n");

// 示例4: 原子性操作演示
printf("示例4: 原子性操作演示\n");

printf("说明epoll_pwait的原子性优势:\n");
printf("传统方式(非原子性):\n");
printf(" 1. sigprocmask(SIG_BLOCK, &mask, &orig_mask);\n");
printf(" 2. epoll_wait(epfd, events, 10, -1);\n");
printf(" 3. sigprocmask(SIG_SETMASK, &orig_mask, NULL);\n");
printf(" 问题: 在步骤1和2之间可能收到信号\n\n");

printf("epoll_pwait方式(原子性):\n");
printf(" epoll_pwait(epfd, events, 10, -1, &mask);\n");
printf(" 优势: 设置掩码和等待是原子操作\n\n");

// 示例5: 错误处理演示
printf("示例5: 错误处理演示\n");

// 使用无效的信号掩码
nfds = epoll_pwait(epfd, events, 10, 1000, (sigset_t*)-1);
if (nfds == -1) {
if (errno == EINVAL) {
printf("无效信号掩码错误处理正确: %s\n", strerror(errno));
}
}

// 其他错误与epoll_wait相同
nfds = epoll_pwait(-1, events, 10, 1000, &mask);
if (nfds == -1) {
if (errno == EBADF) {
printf("无效epoll文件描述符错误处理正确\n");
}
}

// 示例6: 实际应用场景
printf("\n示例6: 实际应用场景\n");

printf("多线程服务器中的信号处理:\n");
printf("场景: 主线程处理网络事件,信号处理线程处理信号\n");
printf("要求: 主线程在epoll_wait期间不被某些信号中断\n\n");

printf("实现方案:\n");
printf("sigset_t mask;\n");
printf("sigemptyset(&mask);\n");
printf("sigaddset(&mask, SIGUSR1); // 阻塞用户信号\n");
printf("sigaddset(&mask, SIGALRM); // 阻塞定时器信号\n");
printf("epoll_pwait(epfd, events, MAX_EVENTS, -1, &mask);\n\n");

// 示例7: 信号掩码操作演示
printf("示例7: 信号掩码操作演示\n");

// 创建复杂的信号掩码
sigset_t complex_mask;
sigemptyset(&complex_mask);

// 添加多个信号到掩码
int signals&#91;] = {SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGALRM};
const char *signal_names&#91;] = {"SIGINT", "SIGTERM", "SIGUSR1", "SIGUSR2", "SIGALRM"};

printf("创建包含以下信号的掩码:\n");
for (int i = 0; i < 5; i++) {
sigaddset(&complex_mask, signals&#91;i]);
printf(" %s\n", signal_names&#91;i]);
}

printf("使用复杂信号掩码进行epoll_pwait\n");
nfds = epoll_pwait(epfd, events, 10, 1000, &complex_mask);
if (nfds != -1) {
printf("epoll_pwait成功完成\n");
}

// 示例8: 线程安全考虑
printf("\n示例8: 线程安全考虑\n");

printf("在多线程环境中的使用:\n");
printf("1. 每个线程可以有自己的epoll实例\n");
printf("2. 每个线程可以设置不同的信号掩码\n");
printf("3. 避免线程间的信号处理冲突\n");
printf("4. 提高信号处理的精确性\n\n");

printf("线程特定的信号掩码设置:\n");
printf("线程1: 阻塞SIGUSR1, SIGUSR2\n");
printf("线程2: 阻塞SIGALRM, SIGVTALRM\n");
printf("线程3: 不阻塞任何信号\n\n");

// 示例9: 性能和安全性
printf("示例9: 性能和安全性\n");

printf("epoll_pwait的优势:\n");
printf("1. 原子性操作,避免竞态条件\n");
printf("2. 更精确的信号控制\n");
printf("3. 提高程序的可靠性\n");
printf("4. 简化信号处理逻辑\n\n");

printf("使用建议:\n");
printf("1. 在需要精确信号控制时使用epoll_pwait\n");
printf("2. 合理设计信号掩码\n");
printf("3. 注意信号处理的线程安全性\n");
printf("4. 在多线程环境中谨慎使用\n\n");

// 清理资源
epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
close(sockfd);
close(epfd);

printf("总结:\n");
printf("epoll_pwait是epoll_wait的增强版本\n");
printf("支持临时信号掩码设置\n");
printf("提供原子性的信号控制\n");
printf("适用于需要精确信号处理的场景\n");
printf("在现代Linux系统中推荐使用\n");

return 0;
}

https://www.calcguide.tech/2025/08/09/epoll-pwait系统调用及示例

data-ad-format="auto" data-full-width-responsive="true">