epoll_wait系统调用及示例

epoll_wait - 等待epoll事件

函数介绍

epoll_wait系统调用用于等待epoll实例中的文件描述符就绪事件。它是epoll机制的核心函数,用于高效地等待多个文件描述符的I/O事件。

函数原型

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

int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);

功能

等待epoll实例中的事件就绪,返回就绪的事件数组。

参数

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

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

  • int maxevents: events数组的最大元素数(必须大于0)

int timeout: 超时时间(毫秒)

  • -1: 永久等待

  • 0: 立即返回(轮询)

  • 0: 等待指定毫秒数

返回值

  • 成功时返回就绪事件的数量(0表示超时)

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

特殊限制

  • maxevents必须大于0

  • 需要有效的epoll文件描述符

  • 可能被信号中断

相似函数

  • epoll_pwait(): 支持信号掩码的版本

  • poll(): 传统轮询等待

  • select(): 传统多路复用等待

示例代码

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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#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>

// 信号处理标志
volatile sig_atomic_t signal_received = 0;

void signal_handler(int sig) {
signal_received = 1;
printf("接收到信号 %d\n", sig);
}

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

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

// 设置信号处理
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);

// 创建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");

printf("等待1秒...\n");
nfds = epoll_wait(epfd, events, 10, 1000); // 1秒超时

if (nfds == -1) {
if (errno == EINTR) {
printf("等待被信号中断\n");
} else {
perror("epoll_wait 失败");
}
} else if (nfds == 0) {
printf("等待超时(1秒内无事件)\n");
} else {
printf("就绪事件数量: %d\n", nfds);
for (int i = 0; i < nfds; i++) {
printf(" 事件 %d: fd=%d, events=0x%x\n",
i, events&#91;i].data.fd, events&#91;i].events);
}
}

// 示例2: 立即返回的轮询
printf("\n示例2: 立即返回的轮询\n");

nfds = epoll_wait(epfd, events, 10, 0); // 立即返回

if (nfds == -1) {
perror("epoll_wait 轮询失败");
} else {
printf("轮询结果: %d 个事件就绪\n", nfds);
}

// 示例3: 永久等待(演示信号中断)
printf("\n示例3: 永久等待(演示信号中断)\n");
printf("请在5秒内按Ctrl+C发送SIGINT信号\n");

alarm(5); // 5秒后发送SIGALRM
nfds = epoll_wait(epfd, events, 10, -1); // 永久等待

if (nfds == -1) {
if (errno == EINTR) {
printf("永久等待被信号中断\n");
} else {
perror("epoll_wait 失败");
}
} else {
printf("永久等待返回: %d 个事件\n", nfds);
}
alarm(0); // 取消alarm

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

// 使用无效的epoll文件描述符
nfds = epoll_wait(-1, events, 10, 1000);
if (nfds == -1) {
if (errno == EBADF) {
printf("无效epoll文件描述符错误处理正确: %s\n", strerror(errno));
}
}

// 使用无效的maxevents
nfds = epoll_wait(epfd, events, 0, 1000);
if (nfds == -1) {
if (errno == EINVAL) {
printf("无效maxevents错误处理正确: %s\n", strerror(errno));
}
}

// 使用NULL事件数组
nfds = epoll_wait(epfd, NULL, 10, 1000);
if (nfds == -1) {
if (errno == EFAULT) {
printf("NULL事件数组错误处理正确: %s\n", strerror(errno));
}
}

// 示例5: 事件处理演示
printf("\n示例5: 事件处理演示\n");

// 模拟不同类型的事件
printf("常见事件类型处理:\n");

for (int i = 0; i < 10; i++) {
events&#91;i].events = 0;
events&#91;i].data.fd = i;
}

// 设置一些模拟事件
events&#91;0].events = EPOLLIN;
events&#91;1].events = EPOLLOUT;
events&#91;2].events = EPOLLIN | EPOLLOUT;
events&#91;3].events = EPOLLERR;
events&#91;4].events = EPOLLHUP;

printf("模拟事件处理:\n");
for (int i = 0; i < 5; i++) {
printf(" fd %d: ", events&#91;i].data.fd);
if (events&#91;i].events & EPOLLIN) printf("可读 ");
if (events&#91;i].events & EPOLLOUT) printf("可写 ");
if (events&#91;i].events & EPOLLERR) printf("错误 ");
if (events&#91;i].events & EPOLLHUP) printf("挂起 ");
printf("\n");
}

// 示例6: 性能考虑
printf("\n示例6: 性能考虑\n");

printf("epoll_wait性能优化建议:\n");
printf("1. 合理设置maxevents参数\n");
printf(" - 不要过大浪费内存\n");
printf(" - 不要过小频繁调用\n");
printf("2. 使用适当的超时时间\n");
printf(" - 根据应用需求选择\n");
printf("3. 批量处理就绪事件\n");
printf(" - 减少系统调用次数\n");
printf("4. 避免在事件处理中阻塞\n");
printf(" - 保持事件循环响应性\n\n");

// 示例7: 实际服务器循环演示
printf("示例7: 实际服务器循环演示\n");

printf("典型的服务器事件循环:\n");
printf("while (running) {\n");
printf(" int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);\n");
printf(" if (nfds == -1) {\n");
printf(" if (errno == EINTR) continue; // 被信号中断\n");
printf(" else break; // 真正的错误\n");
printf(" }\n");
printf(" \n");
printf(" for (int i = 0; i < nfds; i++) {\n");
printf(" if (events&#91;i].data.fd == listen_sock) {\n");
printf(" // 处理新连接\n");
printf(" } else {\n");
printf(" // 处理已连接socket的数据\n");
printf(" }\n");
printf(" }\n");
printf("}\n\n");

// 示例8: 超时时间说明
printf("示例8: 超时时间说明\n");
printf("timeout参数说明:\n");
printf(" -1: 永久等待,直到有事件或被信号中断\n");
printf(" 0: 立即返回,不阻塞(轮询模式)\n");
printf(" >0: 等待指定毫秒数\n\n");

printf("超时时间选择建议:\n");
printf("实时应用: 较小的超时值(1-100ms)\n");
printf("批处理应用: 较大的超时值(1000ms以上)\n");
printf("交互应用: 中等超时值(100-500ms)\n\n");

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

printf("总结:\n");
printf("epoll_wait是epoll机制的核心等待函数\n");
printf("支持灵活的超时控制\n");
printf("正确处理信号中断很重要\n");
printf("合理的maxevents和timeout设置影响性能\n");
printf("是构建高性能网络服务器的基础\n");

return 0;
}
data-ad-format="auto" data-full-width-responsive="true">