Linux I/O 多路復用機制對比分析:poll/ppoll/epoll/select

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;在推理速度上表現出色…