epoll_wait – 等待epoll事件
函数介绍
epoll_wait
系统调用用于等待epoll实例中的文件描述符就绪事件。它是epoll机制的核心函数,用于高效地等待多个文件描述符的I/O事件。
函数原型
#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()
: 传统多路复用等待
示例代码
#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[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[i].data.fd, events[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[i].events = 0;
events[i].data.fd = i;
}
// 设置一些模拟事件
events[0].events = EPOLLIN;
events[1].events = EPOLLOUT;
events[2].events = EPOLLIN | EPOLLOUT;
events[3].events = EPOLLERR;
events[4].events = EPOLLHUP;
printf("模拟事件处理:\n");
for (int i = 0; i < 5; i++) {
printf(" fd %d: ", events[i].data.fd);
if (events[i].events & EPOLLIN) printf("可读 ");
if (events[i].events & EPOLLOUT) printf("可写 ");
if (events[i].events & EPOLLERR) printf("错误 ");
if (events[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[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;
}