Linux I/O 多路复用机制对比分析:poll/ppoll/epoll/select

1. 概述

I/O 多路复用是现代高性能网络编程的核心技术,它允许单个线程同时监视多个文件描述符的状态变化,从而实现高效的并发处理。Linux 提供了多种 I/O 多路复用机制,每种都有其特点和适用场景。
本文将深入分析四种主要的 I/O 多路复用机制:selectpollppollepoll,并通过实际示例展示它们的使用方法和性能差异。

2. 四种机制对比分析

2.1 基本特性对比

特性selectpollppollepoll
引入时间早期UnixSVR3 (1986)Linux 2.6.16Linux 2.5.44
文件描述符限制FD_SETSIZE (通常1024)无理论限制无理论限制无理论限制
数据结构fd_set位图pollfd数组pollfd数组epoll_event数组
文件描述符拷贝每次调用都拷贝每次调用都拷贝每次调用都拷贝注册一次,多次使用
事件复杂度O(n)O(n)O(n)O(1)
跨平台性良好良好Linux特有Linux特有
信号处理基本支持基本支持增强支持基本支持

2.2 详细特性分析

select
  • 优点: 跨平台性最好,几乎所有Unix-like系统都支持
  • 缺点: 文件描述符数量受限,每次调用都需要拷贝fd_set
poll
  • 优点: 无文件描述符数量限制,API设计更清晰
  • 缺点: 每次调用都需要遍历所有文件描述符
ppoll
  • 优点: 提供了更好的信号处理机制,避免竞态条件
  • 缺点: Linux特有,需要较新内核支持
epoll
  • 优点: 性能最优,事件驱动,支持边缘触发和水平触发
  • 缺点: Linux特有,学习成本较高

3. 实际示例代码

3.1 select 示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>#define MAX_CLIENTS 1024
#define BUFFER_SIZE 1024int main_select() {fd_set read_fds, master_fds;int max_fd = 0;int client_sockets[MAX_CLIENTS] = {0};char buffer[BUFFER_SIZE];printf("=== select 示例 ===\n");// 初始化文件描述符集合FD_ZERO(&master_fds);FD_SET(STDIN_FILENO, &master_fds);max_fd = STDIN_FILENO;while (1) {read_fds = master_fds;struct timeval timeout = {1, 0};  // 1秒超时int activity = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);if (activity < 0) {if (errno == EINTR) continue;perror("select error");break;}if (activity == 0) {printf("select timeout\n");continue;}// 检查标准输入if (FD_ISSET(STDIN_FILENO, &read_fds)) {ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);if (bytes_read > 0) {buffer[bytes_read] = '\0';printf("stdin: %s", buffer);if (strncmp(buffer, "quit", 4) == 0) {break;}}}}return 0;
}

3.2 poll 示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <errno.h>
#include <string.h>#define MAX_FDS 10
#define TIMEOUT_MS 5000  // 5秒超时int main_poll() {struct pollfd fds[MAX_FDS];int nfds = 1;char buffer[1024];printf("=== poll 示例 ===\n");// 初始化 pollfd 结构fds[0].fd = STDIN_FILENO;fds[0].events = POLLIN;fds[0].revents = 0;printf("监视标准输入,输入 'quit' 退出\n");while (1) {int ready = poll(fds, nfds, TIMEOUT_MS);if (ready == -1) {if (errno == EINTR) continue;perror("poll error");break;}if (ready == 0) {printf("poll timeout\n");continue;}// 处理就绪的文件描述符for (int i = 0; i < nfds; i++) {if (fds[i].revents & POLLIN) {if (fds[i].fd == STDIN_FILENO) {ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);if (bytes_read > 0) {buffer[bytes_read] = '\0';printf("received: %s", buffer);if (strncmp(buffer, "quit", 4) == 0) {return 0;}}}}if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {printf("fd %d error\n", fds[i].fd);return 1;}}}return 0;
}

3.3 ppoll 示例

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <string.h>volatile sig_atomic_t signal_received = 0;void signal_handler(int sig) {signal_received = sig;printf("\nSignal %d received\n", sig);
}int main_ppoll() {struct pollfd fds[2];struct timespec timeout;sigset_t sigmask;char buffer[1024];printf("=== ppoll 示例 ===\n");// 设置信号处理struct sigaction sa;sa.sa_handler = signal_handler;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;sigaction(SIGINT, &sa, NULL);sigaction(SIGTERM, &sa, NULL);// 初始化 pollfdfds[0].fd = STDIN_FILENO;fds[0].events = POLLIN;fds[0].revents = 0;// 设置超时时间timeout.tv_sec = 3;timeout.tv_nsec = 0;// 设置信号屏蔽集sigemptyset(&sigmask);printf("Monitoring stdin with ppoll...\n");printf("Press Ctrl+C to send signal\n");printf("Type 'quit' to exit\n");while (!signal_received) {int ready = ppoll(fds, 1, &timeout, &sigmask);if (ready == -1) {if (errno == EINTR) {printf("ppoll interrupted by signal\n");if (signal_received) {printf("Signal handling complete\n");}continue;} else {perror("ppoll error");break;}}if (ready == 0) {printf("ppoll timeout\n");continue;}if (fds[0].revents & POLLIN) {ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);if (bytes_read > 0) {buffer[bytes_read] = '\0';printf("Input: %s", buffer);if (strncmp(buffer, "quit", 4) == 0) {break;}}}fds[0].revents = 0;  // 重置事件}printf("Program exiting normally\n");return 0;
}

3.4 epoll 示例

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>#define MAX_EVENTS 10
#define BUFFER_SIZE 1024int make_socket_nonblocking(int fd) {int flags = fcntl(fd, F_GETFL, 0);if (flags == -1) {return -1;}return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}int main_epoll() {int epoll_fd;struct epoll_event ev, events[MAX_EVENTS];char buffer[BUFFER_SIZE];printf("=== epoll 示例 ===\n");// 创建 epoll 实例epoll_fd = epoll_create1(0);if (epoll_fd == -1) {perror("epoll_create1");return 1;}// 设置标准输入为非阻塞if (make_socket_nonblocking(STDIN_FILENO) == -1) {perror("fcntl");close(epoll_fd);return 1;}// 添加标准输入到 epollev.events = EPOLLIN | EPOLLET;  // 边缘触发模式ev.data.fd = STDIN_FILENO;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1) {perror("epoll_ctl: stdin");close(epoll_fd);return 1;}printf("epoll monitoring stdin (edge-triggered mode)\n");printf("Type 'quit' to exit\n");while (1) {int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, 3000);  // 3秒超时if (nfds == -1) {if (errno == EINTR) continue;perror("epoll_wait");break;}if (nfds == 0) {printf("epoll timeout\n");continue;}for (int n = 0; n < nfds; n++) {if (events[n].events & EPOLLIN) {if (events[n].data.fd == STDIN_FILENO) {ssize_t bytes_read;while ((bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1)) > 0) {buffer[bytes_read] = '\0';printf("epoll input: %s", buffer);if (strncmp(buffer, "quit", 4) == 0) {close(epoll_fd);return 0;}}if (bytes_read == -1) {if (errno != EAGAIN && errno != EWOULDBLOCK) {perror("read");}}}}if (events[n].events & (EPOLLERR | EPOLLHUP)) {printf("epoll error on fd %d\n", events[n].data.fd);close(epoll_fd);return 1;}}}close(epoll_fd);return 0;
}

4. 性能测试对比

4.1 基准测试代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <poll.h>
#include <sys/select.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <string.h>#define TEST_ITERATIONS 10000
#define TEST_FDS 100// 性能测试结构体
struct performance_test {const char *name;long long (*test_func)(int fd_count);
};// select 性能测试
long long test_select_performance(int fd_count) {fd_set read_fds;struct timeval timeout = {0, 1000};  // 1ms 超时struct timeval start, end;gettimeofday(&start, NULL);for (int i = 0; i < TEST_ITERATIONS; i++) {FD_ZERO(&read_fds);FD_SET(STDIN_FILENO, &read_fds);select(STDIN_FILENO + 1, &read_fds, NULL, NULL, &timeout);}gettimeofday(&end, NULL);return (end.tv_sec - start.tv_sec) * 1000000LL + (end.tv_usec - start.tv_usec);
}// poll 性能测试
long long test_poll_performance(int fd_count) {struct pollfd pfd;struct timeval start, end;pfd.fd = STDIN_FILENO;pfd.events = POLLIN;pfd.revents = 0;gettimeofday(&start, NULL);for (int i = 0; i < TEST_ITERATIONS; i++) {poll(&pfd, 1, 1);  // 1ms 超时pfd.revents = 0;}gettimeofday(&end, NULL);return (end.tv_sec - start.tv_sec) * 1000000LL + (end.tv_usec - start.tv_usec);
}// 显示性能测试结果
void show_performance_results() {struct performance_test tests[] = {{"select", test_select_performance},{"poll", test_poll_performance},{NULL, NULL}};printf("=== 性能测试结果 (10000 次调用) ===\n");printf("%-10s %-15s %-15s\n", "机制", "耗时(微秒)", "平均耗时(纳秒)");printf("%-10s %-15s %-15s\n", "----", "----------", "--------------");for (int i = 0; tests[i].name; i++) {long long total_time = tests[i].test_func(TEST_FDS);double avg_time = (double)total_time * 1000.0 / TEST_ITERATIONS;printf("%-10s %-15lld %-15.2f\n", tests[i].name, total_time, avg_time);}printf("\n性能特点:\n");printf("1. select: 有文件描述符数量限制,每次调用需要拷贝fd_set\n");printf("2. poll:   无文件描述符数量限制,但仍需遍历所有描述符\n");printf("3. epoll:  事件驱动,只处理活跃的描述符,性能最优\n");printf("4. ppoll:  提供更好的信号处理机制,避免竞态条件\n");
}int main_performance_comparison() {printf("=== I/O 多路复用性能对比测试 ===\n\n");show_performance_results();printf("\n=== 实际应用建议 ===\n");printf("选择建议:\n");printf("1. 跨平台应用: 使用 select\n");printf("2. 中等并发(<1000): 使用 poll\n");printf("3. 高并发(>1000): 使用 epoll\n");printf("4. 需要信号处理: 使用 ppoll\n");printf("5. Linux 专用: 使用 epoll\n");return 0;
}

5. 实际应用场景分析

5.1 网络服务器场景

// 简单的 HTTP 服务器示例,展示不同机制的使用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <poll.h>
#include <sys/epoll.h>#define PORT 8080
#define MAX_CLIENTS 1000
#define BUFFER_SIZE 4096// 创建监听套接字
int create_server_socket(int port) {int server_fd;struct sockaddr_in address;int opt = 1;if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");return -1;}if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {perror("setsockopt");close(server_fd);return -1;}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(port);if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");close(server_fd);return -1;}if (listen(server_fd, 3) < 0) {perror("listen");close(server_fd);return -1;}return server_fd;
}// 处理 HTTP 请求
void handle_http_request(int client_fd) {char buffer[BUFFER_SIZE];ssize_t bytes_read = read(client_fd, buffer, sizeof(buffer) - 1);if (bytes_read > 0) {buffer[bytes_read] = '\0';// 简单的 HTTP 响应const char *response = "HTTP/1.1 200 OK\r\n""Content-Type: text/html\r\n""Connection: close\r\n""\r\n""<html><body><h1>Hello from I/O Multiplexing Server!</h1></body></html>\r\n";write(client_fd, response, strlen(response));}close(client_fd);
}// 使用 poll 的 HTTP 服务器
int http_server_poll(int port) {int server_fd, client_fd;struct sockaddr_in address;int addrlen = sizeof(address);struct pollfd *fds;int max_fds = MAX_CLIENTS + 1;int nfds = 1;printf("Starting HTTP server with poll on port %d\n", port);server_fd = create_server_socket(port);if (server_fd == -1) return -1;fds = calloc(max_fds, sizeof(struct pollfd));if (!fds) {perror("calloc");close(server_fd);return -1;}// 添加监听套接字fds[0].fd = server_fd;fds[0].events = POLLIN;fds[0].revents = 0;while (1) {int ready = poll(fds, nfds, 1000);  // 1秒超时if (ready == -1) {if (errno == EINTR) continue;perror("poll");break;}if (ready == 0) continue;  // 超时// 检查监听套接字if (fds[0].revents & POLLIN) {client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);if (client_fd >= 0) {if (nfds < max_fds) {fds[nfds].fd = client_fd;fds[nfds].events = POLLIN;fds[nfds].revents = 0;nfds++;printf("New connection from %s:%d\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));} else {printf("Too many connections, rejecting\n");close(client_fd);}}}// 检查客户端连接for (int i = 1; i < nfds; i++) {if (fds[i].revents & POLLIN) {handle_http_request(fds[i].fd);// 移除已处理的连接for (int j = i; j < nfds - 1; j++) {fds[j] = fds[j + 1];}nfds--;i--;  // 重新检查当前位置}}// 重置 reventsfor (int i = 0; i < nfds; i++) {fds[i].revents = 0;}}free(fds);close(server_fd);return 0;
}// 使用 epoll 的 HTTP 服务器
int http_server_epoll(int port) {int server_fd, client_fd, epoll_fd;struct sockaddr_in address;int addrlen = sizeof(address);struct epoll_event ev, events[MAX_CLIENTS];int nfds;printf("Starting HTTP server with epoll on port %d\n", port);server_fd = create_server_socket(port);if (server_fd == -1) return -1;epoll_fd = epoll_create1(0);if (epoll_fd == -1) {perror("epoll_create1");close(server_fd);return -1;}// 添加监听套接字到 epollev.events = EPOLLIN;ev.data.fd = server_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == -1) {perror("epoll_ctl: listen");close(server_fd);close(epoll_fd);return -1;}while (1) {nfds = epoll_wait(epoll_fd, events, MAX_CLIENTS, 1000);  // 1秒超时if (nfds == -1) {if (errno == EINTR) continue;perror("epoll_wait");break;}if (nfds == 0) continue;  // 超时for (int i = 0; i < nfds; i++) {if (events[i].data.fd == server_fd) {// 新连接client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);if (client_fd >= 0) {ev.events = EPOLLIN | EPOLLET;ev.data.fd = client_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) == -1) {perror("epoll_ctl: client");close(client_fd);} else {printf("New connection from %s:%d\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));}}} else {// 客户端数据handle_http_request(events[i].data.fd);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);}}}close(epoll_fd);close(server_fd);return 0;
}

6. 最佳实践和使用建议

6.1 选择指南

// 根据应用场景选择合适的 I/O 多路复用机制/*
选择决策树:1. 是否需要跨平台支持?- 是 -> 选择 select- 否 -> 继续下一步2. 操作系统是什么?- Windows -> 选择 select 或 IOCP- Linux -> 继续下一步3. 并发连接数是多少?- < 100 -> select 或 poll 都可以- 100-1000 -> poll- > 1000 -> epoll4. 是否需要特殊的信号处理?- 是 -> ppoll- 否 -> 继续下一步5. 性能要求如何?- 高性能 -> epoll- 一般 -> poll
*/// 配置结构体
struct io_multiplexing_config {enum {IO_SELECT,IO_POLL,IO_PPOLL,IO_EPOLL} method;int max_connections;int timeout_ms;int edge_triggered;  // 仅对 epoll 有效int signal_safe;    // 是否需要信号安全
};// 根据配置推荐机制
const char* recommend_io_method(const struct io_multiplexing_config *config) {if (config->method != 0) {// 明确指定了方法switch (config->method) {case IO_SELECT: return "select";case IO_POLL: return "poll";case IO_PPOLL: return "ppoll";case IO_EPOLL: return "epoll";}}// 根据配置自动推荐if (config->signal_safe) {return "ppoll";}if (config->max_connections > 1000) {return "epoll";}if (config->max_connections > 100) {return "poll";}return "select";
}// 显示推荐结果
void show_recommendation(const struct io_multiplexing_config *config) {printf("=== I/O 多路复用机制推荐 ===\n");printf("配置参数:\n");printf("  最大连接数: %d\n", config->max_connections);printf("  超时时间: %d ms\n", config->timeout_ms);printf("  边缘触发: %s\n", config->edge_triggered ? "是" : "否");printf("  信号安全: %s\n", config->signal_safe ? "是" : "否");printf("\n");printf("推荐机制: %s\n", recommend_io_method(config));printf("\n");
}

6.2 错误处理最佳实践

// 安全的 I/O 多路复用封装
typedef struct {int fd;void *data;int (*read_handler)(int fd, void *data);int (*write_handler)(int fd, void *data);int (*error_handler)(int fd, void *data);
} io_handler_t;// 通用的错误处理函数
int handle_io_errors(int fd, int revents, const char *context) {if (revents & (POLLERR | POLLHUP | POLLNVAL)) {fprintf(stderr, "Error on fd %d in %s: ", fd, context);if (revents & POLLERR) {fprintf(stderr, "POLLERR ");}if (revents & POLLHUP) {fprintf(stderr, "POLLHUP ");}if (revents & POLLNVAL) {fprintf(stderr, "POLLNVAL ");}fprintf(stderr, "\n");return -1;}return 0;
}// 安全的 poll 封装
int safe_poll(struct pollfd *fds, nfds_t nfds, int timeout_ms) {if (!fds || nfds == 0) {errno = EINVAL;return -1;}int result;do {result = poll(fds, nfds, timeout_ms);} while (result == -1 && errno == EINTR);return result;
}// 安全的 ppoll 封装
int safe_ppoll(struct pollfd *fds, nfds_t nfds,const struct timespec *timeout_ts,const sigset_t *sigmask) {if (!fds || nfds == 0) {errno = EINVAL;return -1;}int result;do {result = ppoll(fds, nfds, timeout_ts, sigmask);} while (result == -1 && errno == EINTR);return result;
}// 安全的 epoll 封装
int safe_epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout) {if (!events || maxevents <= 0) {errno = EINVAL;return -1;}int result;do {result = epoll_wait(epfd, events, maxevents, timeout);} while (result == -1 && errno == EINTR);return result;
}

7. 完整的综合示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <sys/select.h>
#include <sys/epoll.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <string.h>// 综合测试结构体
struct comprehensive_test {const char *name;void (*test_func)(void);int supported;
};// 综合性能测试
void comprehensive_performance_test() {printf("=== 综合性能测试 ===\n");// 测试不同文件描述符数量下的性能int test_sizes[] = {10, 50, 100, 500, 1000};int num_tests = sizeof(test_sizes) / sizeof(test_sizes[0]);printf("%-10s %-10s %-15s %-15s %-15s\n", "机制", "FD数量", "select(μs)", "poll(μs)", "epoll(μs)");printf("%-10s %-10s %-15s %-15s %-15s\n", "----", "------", "----------", "---------", "----------");for (int i = 0; i < num_tests; i++) {int fd_count = test_sizes[i];// 这里简化处理,实际应该进行真实的时间测量printf("%-10s %-10d %-15d %-15d %-15d\n","测试", fd_count,fd_count * 2,    // select 模拟时间fd_count * 1.5,  // poll 模拟时间fd_count * 0.5); // epoll 模拟时间}
}// 实际使用场景演示
void practical_usage_scenarios() {printf("\n=== 实际使用场景 ===\n");printf("1. Web 服务器:\n");printf("   - 高并发: 推荐使用 epoll\n");printf("   - 中等并发: 可以使用 poll\n");printf("   - 简单场景: select 也足够\n");printf("\n2. 数据库连接池:\n");printf("   - 连接数较少: poll 或 select\n");printf("   - 连接数较多: epoll\n");printf("   - 需要信号处理: ppoll\n");printf("\n3. 实时通信应用:\n");printf("   - 聊天服务器: epoll (支持边缘触发)\n");printf("   - 游戏服务器: epoll (高性能)\n");printf("   - 需要精确信号处理: ppoll\n");printf("\n4. 系统监控工具:\n");printf("   - 文件监控: poll (简单可靠)\n");printf("   - 网络监控: epoll (高性能)\n");printf("   - 需要信号处理: ppoll\n");
}// 移植性考虑
void portability_considerations() {printf("\n=== 移植性考虑 ===\n");printf("跨平台支持:\n");printf("1. select: 几乎所有 Unix-like 系统都支持\n");printf("2. poll: POSIX 标准,广泛支持\n");printf("3. ppoll: Linux 特有 (2.6.16+)\n");printf("4. epoll: Linux 特有 (2.5.44+)\n");printf("\n条件编译示例:\n");printf("#ifdef __linux__\n");printf("    // 使用 epoll\n");printf("#elif defined(__FreeBSD__) || defined(__APPLE__)\n");printf("    // 使用 kqueue\n");printf("#else\n");printf("    // 使用 poll 或 select\n");printf("#endif\n");
}int main() {printf("=== Linux I/O 多路复用机制综合分析 ===\n\n");// 性能测试comprehensive_performance_test();// 实际使用场景practical_usage_scenarios();// 移植性考虑portability_considerations();printf("\n=== 总结 ===\n");printf("1. select: 简单可靠,跨平台性好,但有 FD 数量限制\n");printf("2. poll:   无 FD 限制,API 清晰,适合中等并发\n");printf("3. ppoll:  增强的信号处理,避免竞态条件,Linux 特有\n");printf("4. epoll:  性能最优,事件驱动,Linux 特有\n");printf("\n");printf("选择建议:\n");printf("- 新项目且只运行在 Linux: 首选 epoll\n");printf("- 需要跨平台支持: 使用 poll 或 select\n");printf("- 需要特殊信号处理: 考虑 ppoll\n");printf("- 简单应用场景: select 也足够\n");return 0;
}

8. 编译和运行说明

# 编译所有示例
gcc -o select_example example1.c
gcc -o poll_example example2.c
gcc -o ppoll_example example3.c -D_GNU_SOURCE
gcc -o epoll_example example4.c
gcc -o performance_test performance_test.c
gcc -o comprehensive_analysis comprehensive.c# 运行示例
./select_example
./poll_example
./ppoll_example
./epoll_example
./performance_test
./comprehensive_analysis# 测试不同场景
echo "Testing select with 100 FDs..."
./select_example 100echo "Testing epoll with high concurrency..."
./epoll_example 1000echo "Running comprehensive analysis..."
./comprehensive_analysis

9. 系统要求检查

# 检查内核版本
uname -r# 检查 glibc 版本
ldd --version# 检查 epoll 支持
grep -w epoll /usr/include/linux/eventpoll.h# 检查 ppoll 支持
grep -w ppoll /usr/include/asm/unistd_64.h# 查看系统调用限制
ulimit -n  # 文件描述符限制
cat /proc/sys/fs/file-max  # 系统最大文件描述符

10. 最佳实践总结

// 1. 错误处理模板
int robust_io_multiplexing() {struct pollfd *fds = NULL;int max_fds = 1024;int nfds = 0;// 分配内存fds = malloc(max_fds * sizeof(struct pollfd));if (!fds) {return -1;}// 初始化memset(fds, 0, max_fds * sizeof(struct pollfd));// 主循环while (1) {int ready;// 使用安全的 poll 调用do {ready = poll(fds, nfds, 1000);  // 1秒超时} while (ready == -1 && errno == EINTR);if (ready == -1) {if (errno != EINTR) {perror("poll error");break;}continue;}if (ready == 0) {// 超时处理continue;}// 处理事件for (int i = 0; i < nfds; i++) {if (fds[i].revents != 0) {// 检查错误if (handle_io_errors(fds[i].fd, fds[i].revents, "main loop") == -1) {// 处理错误连接continue;}// 处理正常事件if (fds[i].revents & POLLIN) {// 处理可读事件}if (fds[i].revents & POLLOUT) {// 处理可写事件}}}}// 清理资源if (fds) {free(fds);}return 0;
}// 2. 资源管理模板
typedef struct {int epoll_fd;int *client_fds;int client_count;int max_clients;
} server_context_t;int init_server_context(server_context_t *ctx, int max_clients) {ctx->epoll_fd = -1;ctx->client_fds = NULL;ctx->client_count = 0;ctx->max_clients = max_clients;// 创建 epollctx->epoll_fd = epoll_create1(0);if (ctx->epoll_fd == -1) {return -1;}// 分配客户端数组ctx->client_fds = malloc(max_clients * sizeof(int));if (!ctx->client_fds) {close(ctx->epoll_fd);ctx->epoll_fd = -1;return -1;}return 0;
}void cleanup_server_context(server_context_t *ctx) {if (ctx->epoll_fd != -1) {close(ctx->epoll_fd);ctx->epoll_fd = -1;}if (ctx->client_fds) {free(ctx->client_fds);ctx->client_fds = NULL;}ctx->client_count = 0;
}

通过以上详细的对比分析和示例代码,我们可以清楚地看到各种 I/O 多路复用机制的特点和适用场景。选择合适的机制对于构建高性能的网络应用程序至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/91792.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/91792.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/91792.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高防服务器租用:保障数据安全

您的网络速度是否卡顿&#xff0c;业务是否经常受到网络攻击的威胁呢&#xff1f;别担心&#xff0c;高防服务器租用能够帮助你解决这些困扰&#xff01;高防服务器租用拥有着卓越的防御能力&#xff0c;可以帮助企业抵御各种网络攻击&#xff0c;能够轻松化解各种超大流量的网…

基于python多光谱遥感数据处理、图像分类、定量评估及机器学习方法应用

基于卫星或无人机平台的多光谱数据在地质、土壤调查和农业等应用领域发挥了重要作用&#xff0c;在地质应用方面&#xff0c;综合Aster的短波红外波段、landsat热红外波段等多光谱数据&#xff0c;可以通过不同的多光谱数据组合&#xff0c;协同用于矿物信息有效提取。第一&…

CSS content-visibility:提升页面渲染性能的 “智能渲染开关”

在前端开发中&#xff0c;你是否遇到过这样的问题&#xff1a;页面包含大量 DOM 元素&#xff08;如长列表、复杂表格&#xff09;时&#xff0c;滚动变得卡顿&#xff0c;交互响应迟缓&#xff1f;这往往是因为浏览器需要不断渲染屏幕外的元素&#xff0c;浪费了大量计算资源。…

Javascript面试题及详细答案150道之(016-030)

《前后端面试题》专栏集合了前后端各个知识模块的面试题&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

仿真电路:(十七下)DC-DC升压压电路原理简单仿真

1.前言 升压的环境用的没降压的多&#xff0c;但是升压会用在LED的很多电路上&#xff0c;所以理解一下原理 2.DC-DC升压原理简单仿真 升压原理 下面还是对升压进行简单的仿真 拓扑结构以及原理和降压还是很相似的&#xff0c;只是位置不太一样&#xff0c;过程推导就不推导…

ros2--source

setup脚本类型 install下面会有几个setup.xxx的shell脚本。 setup.bash setup.ps1 setup.sh setup.zsh 什么区别呢 文件名 Shell 类型 适用场景 setup.bash Bash (Linux/macOS) 标准 Linux/macOS 终端(默认使用) setup.sh 通用 Shell 兼容性更广,但功能可能受限 setu…

40.MySQL事务

1.事务的作用事务用于保证数据的一致性&#xff0c;它由一组相关的 dml (update delete insert) 语句组成&#xff0c;该组的 dml (update delete insert) 语句要么全部成功&#xff0c;要么全部失败。如&#xff1a;转账就要用事务来处理&#xff0c;用以保证数据的一致性。假…

java导入pdf(携带动态表格,图片,纯java不需要模板)

java导出pdf文件一、介绍二、准备三、实现效果四、代码一、介绍 上一篇文章&#xff08;java使用freemarker操作word&#xff08;携带动态表格&#xff0c;图片&#xff09;&#xff09;https://blog.csdn.net/weixin_45853881/article/details/129298494 紧跟上文&#xff0c…

【dropdown组件填坑指南】鼠标从触发元素到下拉框中间间隙时,下拉框消失,怎么解决?

开发dropdown组件填坑之hideDelay 引言 在开发下拉菜单&#xff08;dropdown&#xff09;或弹出框&#xff08;popover&#xff09;组件时&#xff0c;一个常见的用户体验问题就是鼠标移出触发区域后&#xff0c;弹出内容立即消失&#xff0c;这会导致用户无法移动到弹出内容上…

Linux I/O 函数完整清单

Linux I/O 函数完整清单 1. 基础 I/O 函数 1.1 基本读写 #include <unistd.h>ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count);1.2 位置指定读写 #include <unistd.h>ssize_t pread(int fd, void *buf, siz…

面经——电子电路技术知识详解

电子电路技术知识详解 目录 德摩根定律周期性矩形波产生方法自激振荡器原理与设计晶体管温度效应分析反向饱和电流影响因素放大电路负反馈类型判断正弦波90相移电路直接耦合放大器的缺点二阶有源低通滤波器分析开关电源与线性电源对比 德摩根定律 德摩根定律&#xff08;De …

docker 安装 gitlab

null文章浏览阅读445次。问题&#xff1a;运行 docker run hello-world 报错。原因&#xff1a;原镜像源网络不稳定。https://blog.csdn.net/sszdzq/article/details/145733419 镜像获取 在线下载 docker pull gitlab/gitlab-ce:17.11.1-ce.0 离线获取 创建运行 sudo docke…

PHP中的日期/时间处理之Carbon组件

日常开发中&#xff0c;我们会经常用到日期和时间的操作&#xff0c;但官方的一般操作比较复杂&#xff0c;需要大量的时间进行格式化问题和大量计算等等。Carbon组件 可以帮助我们在 PHP 开发中处理日期/时间变得更加简单、更语义化&#xff0c;从而使得我们的代码更容易阅读和…

学习嵌入式第十八天

文章目录1.数据结构1.概念2.衡量代码质量和效率1.时间复杂度2.空间复杂度3.数据结构分类1.逻辑结构2.存储结构3.常见的数据结构2.链表1.与顺序表的区别2.链表分类1.单向链表1.定义链表节点类型2.空链表的创建3.链表的头插法4.链表的遍历5.链表元素删除3.makefile习题1.数据结构…

基于SpringBoot+Vue实现校园商铺系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参…

从资源闲置到弹性高吞吐,JuiceFS 如何构建 70GB/s 吞吐的缓存池?

AI 模型的训练与推理对存储系统提出了极为严苛的要求&#xff0c;特别是在高吞吐、高并发以及对海量小文件的高效处理方面&#xff0c;已成为三大主要挑战。尽管基于 Lustre 或 GPFS 的并行文件系统具备出色的性能&#xff0c;但其成本高昂、吞吐能力与容量强耦合&#xff0c;可…

提升JVM性能之CMS垃圾回收器的优化分析与案例剖析

这里写目录标题一、CMS基本介绍二、CMS核心优化策略1. 避免并发模式失败&#xff08;Concurrent Mode Failure&#xff09;2. 减少内存碎片3. 调优并发阶段耗时4. 新生代优化配合三、典型案例解析案例1&#xff1a;电商服务频繁Full GC案例2&#xff1a;金融交易系统碎片导致长…

Token系列 - 再谈稳定币

相关政策 2024年12月&#xff0c;欧洲《加密资产市场监管法案》正式成为法律2025年3月&#xff0c;日本细化了加密资产及稳定币的监管调整2025年5月&#xff0c;英国发布了关于稳定币发行、加密资产托管及加密资产公司财务稳健性的监管提案&#xff1b;2025年5月20日&#xff…

【20min 急速入门】使用Demucs进行音轨分离

创建环境 conda create --name mujica python3.10下载加速依赖 先用nvidia-smi检查机器使用的独显版本, 然后从pytorch官网下载对应的GPU版torch, torchaudio 比如我的是12.2, 就下载11.8版本的 pip3 install torch torchvision torchaudio --index-url https://download.p…

字节Seed发布扩散语言模型,推理速度达2146 tokens/s,比同规模自回归快5.4倍

用扩散模型写代码&#xff0c;不仅像开了倍速&#xff0c;改起来还特别灵活&#xff01;字节Seed最新发布扩散语言模型Seed Diffusion Preview&#xff0c;这款模型主要聚焦于代码生成领域&#xff0c;它的特别之处在于采用了离散状态扩散技术&#xff0c;在推理速度上表现出色…