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 230 231 232 233 234 235
| #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/eventfd.h> #include <sys/syscall.h> #include <errno.h> #include <string.h> #include <fcntl.h> #include <pthread.h>
// 系统调用包装(如果glibc不支持) static int eventfd_wrapper(unsigned int initval, int flags) { return syscall(__NR_eventfd2, initval, flags); }
// 线程函数 void* thread_function(void* arg) { int efd = *(int*)arg; uint64_t value = 1; printf(" 子线程: 准备发送事件通知\n"); // 发送事件通知 if (write(efd, &value, sizeof(value)) != sizeof(value)) { perror(" 子线程: 写入eventfd失败"); } else { printf(" 子线程: 成功发送事件通知\n"); } return NULL; }
int main() { int efd; uint64_t value; pthread_t thread; printf("=== Eventfd 函数示例 ===\n"); // 示例1: 基本使用 printf("\n示例1: 基本使用\n"); efd = eventfd_wrapper(0, 0); if (efd == -1) { perror("eventfd 创建失败"); exit(EXIT_FAILURE); } printf("成功创建eventfd,文件描述符: %d\n", efd); // 检查文件描述符属性 int flags = fcntl(efd, F_GETFD); if (flags != -1) { printf("eventfd文件描述符验证成功\n"); } // 关闭eventfd close(efd); printf("关闭eventfd\n"); // 示例2: 基本的事件通知 printf("\n示例2: 基本的事件通知\n"); efd = eventfd_wrapper(0, 0); if (efd == -1) { perror("eventfd 创建失败"); exit(EXIT_FAILURE); } printf("创建eventfd: %d\n", efd); // 启动线程发送事件 if (pthread_create(&thread, NULL, thread_function, &efd) != 0) { perror("创建线程失败"); close(efd); exit(EXIT_FAILURE); } printf("主线程: 等待事件通知...\n"); // 等待事件通知 ssize_t bytes_read = read(efd, &value, sizeof(value)); if (bytes_read == sizeof(value)) { printf("主线程: 收到事件通知,计数器值: %lu\n", value); } else { perror("主线程: 读取eventfd失败"); } // 等待线程结束 pthread_join(thread, NULL); close(efd); // 示例3: 使用标志位 printf("\n示例3: 使用标志位\n"); // 使用EFD_CLOEXEC标志 efd = eventfd_wrapper(0, EFD_CLOEXEC); if (efd != -1) { printf("创建带EFD_CLOEXEC标志的eventfd: %d\n", efd); // 验证标志是否设置 flags = fcntl(efd, F_GETFD); if (flags != -1 && (flags & FD_CLOEXEC)) { printf("EFD_CLOEXEC标志已正确设置\n"); } close(efd); } // 使用EFD_NONBLOCK标志 efd = eventfd_wrapper(0, EFD_NONBLOCK); if (efd != -1) { printf("创建带EFD_NONBLOCK标志的eventfd: %d\n", efd); // 尝试非阻塞读取(应该失败) bytes_read = read(efd, &value, sizeof(value)); if (bytes_read == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { printf("非阻塞读取正确返回EAGAIN: %s\n", strerror(errno)); } } close(efd); } // 示例4: 信号量模式 printf("\n示例4: 信号量模式\n"); efd = eventfd_wrapper(3, EFD_SEMAPHORE); if (efd != -1) { printf("创建信号量模式eventfd,初始值: 3\n"); // 多次读取,每次递减1 for (int i = 0; i < 5; i++) { bytes_read = read(efd, &value, sizeof(value)); if (bytes_read == sizeof(value)) { printf("第%d次读取,获得值: %lu\n", i+1, value); } else { if (errno == EAGAIN) { printf("第%d次读取,无可用事件: %s\n", i+1, strerror(errno)); break; } } } close(efd); } // 示例5: 错误处理演示 printf("\n示例5: 错误处理演示\n"); // 使用无效的初始值(虽然eventfd允许大值,但有上限) efd = eventfd_wrapper(0xffffffff, 0); if (efd != -1) { printf("使用大初始值创建eventfd成功: %d\n", efd); close(efd); } // 尝试写入无效值 efd = eventfd_wrapper(0, 0); if (efd != -1) { uint64_t invalid_value = 0xffffffffffffffffULL; // 最大值 ssize_t result = write(efd, &invalid_value, sizeof(invalid_value)); if (result == -1) { printf("写入最大值失败: %s\n", strerror(errno)); } else { printf("写入最大值成功\n"); } close(efd); } // 示例6: 计数器溢出处理 printf("\n示例6: 计数器溢出处理\n"); efd = eventfd_wrapper(0, 0); if (efd != -1) { // 写入接近最大值的数据 uint64_t large_value = 0xfffffffffffffffeULL; // 接近最大值 if (write(efd, &large_value, sizeof(large_value)) == sizeof(large_value)) { printf("写入大值成功\n"); // 再次写入会导致溢出 uint64_t add_value = 2; ssize_t result = write(efd, &add_value, sizeof(add_value)); if (result == -1) { if (errno == EAGAIN) { printf("计数器溢出,写入失败: %s\n", strerror(errno)); } } } close(efd); } // 示例7: 实际应用场景 printf("\n示例7: 实际应用场景\n"); printf("eventfd的典型应用场景:\n"); printf("1. 线程池任务通知\n"); printf("2. 异步I/O完成通知\n"); printf("3. 事件驱动编程\n"); printf("4. 进程间简单通信\n"); printf("5. 与epoll配合使用\n\n"); // 演示与epoll配合使用 printf("与epoll配合使用的示例:\n"); printf("int epfd = epoll_create1(EPOLL_CLOEXEC);\n"); printf("int efd = eventfd(0, EFD_CLOEXEC);\n"); printf("struct epoll_event ev;\n"); printf("ev.events = EPOLLIN;\n"); printf("ev.data.fd = efd;\n"); printf("epoll_ctl(epfd, EPOLL_CTL_ADD, efd, &ev);\n"); printf("// 在其他线程中: write(efd, &value, sizeof(value));\n"); printf("// 在事件循环中: epoll_wait(epfd, events, maxevents, timeout);\n\n"); // 示例8: 性能优势 printf("示例8: 性能优势\n"); printf("eventfd相比传统机制的优势:\n"); printf("1. 更少的系统调用\n"); printf("2. 更小的内存占用\n"); printf("3. 更快的通知速度\n"); printf("4. 更简单的API\n"); printf("5. 更好的可扩展性\n\n"); printf("与pipe的对比:\n"); printf("pipe: 需要两个文件描述符,缓冲区较大\n"); printf("eventfd: 只需要一个文件描述符,固定8字节计数器\n\n"); printf("总结:\n"); printf("eventfd是Linux提供的轻量级事件通知机制\n"); printf("适用于简单的同步和通知场景\n"); printf("支持多种模式和标志位\n"); printf("与epoll等机制配合使用效果更佳\n"); printf("是现代Linux编程的重要工具\n"); return 0; }
|