epoll
是 Linux 下用于处理大量文件描述符(比如网络连接)的高效 I/O 事件通知机制。它是 select
和 poll
的替代品,特别适用于高并发场景。
epoll
提供了三种系统调用:
epoll_create
: 创建一个 epoll
实例。epoll_ctl
: 向 epoll
实例中添加、修改或删除要监控的文件描述符。epoll_wait
: 等待 epoll
实例中的文件描述符就绪,并返回就绪的文件描述符列表。epoll
使用红黑树管理文件描述符,添加、删除和查找的时间复杂度都是 O(log n)。同时,它使用事件驱动的方式,只有当文件描述符就绪时才会通知应用程序,避免了轮询的开销。epoll
没有最大并发连接的限制,只受限于系统内存大小。epoll
常用于构建高性能的网络服务器,如 Web 服务器、聊天服务器等,特别是在需要处理大量并发连接的场景下。
以下是一个简单的 epoll
服务器示例,使用边缘触发模式:
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_EVENTS 10
#define PORT 8080
int set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) return -1;
flags |= O_NONBLOCK;
return fcntl(fd, F_SETFL, flags);
}
int main() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == -1) perror("socket");
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
if (bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) perror("bind");
if (listen(listen_fd, SOMAXCONN) == -1) perror("listen");
int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) perror("epoll_create1");
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = listen_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) == -1) perror("epoll_ctl");
struct epoll_event events[MAX_EVENTS];
while (1) {
int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (n == -1) perror("epoll_wait");
for (int i = 0; i < n; i++) {
if (events[i].data.fd == listen_fd) {
while (1) {
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int conn_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_len);
if (conn_fd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
else perror("accept");
continue;
}
set_nonblocking(conn_fd);
event.events = EPOLLIN | EPOLLET;
event.data.fd = conn_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &event) == -1) perror("epoll_ctl");
}
} else {
char buf[1024];
int conn_fd = events[i].data.fd;
while (1) {
ssize_t count = read(conn_fd, buf, sizeof(buf));
if (count == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
else perror("read");
close(conn_fd);
break;
} else if (count == 0) {
close(conn_fd);
break;
}
write(conn_fd, buf, count); // Echo back
}
}
}
}
close(listen_fd);
close(epoll_fd);
return 0;
}
这个示例代码创建了一个简单的回声服务器,使用 epoll
来处理并发连接。注意,在实际应用中,你可能需要添加更多的错误处理和资源管理代码。
领取专属 10元无门槛券
手把手带您无忧上云