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[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; }
|