### 使用方法
#### 创建 epoll 实例
在 Linux 中,使用 `epoll` 的第一步是创建一个 epoll 文件描述符。这可以通过调用 `epoll_create1()` 函数来完成。此函数返回一个文件描述符,用于后续的 epoll 操作。
```c
int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}
```
这段代码展示了如何创建一个 epoll 实例[^3]。
#### 添加文件描述符到 epoll 实例
一旦有了 epoll 文件描述符,下一步就是将需要监视的文件描述符添加进去。这通过 `epoll_ctl()` 函数实现,可以指定要监视的事件类型,如读就绪、写就绪等。
```c
struct epoll_event event;
event.events = EPOLLIN; // 监视读事件
event.data.fd = listen_sock; // 要监视的文件描述符
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
```
以上代码片段演示了如何将一个监听套接字添加到 epoll 实例中[^2]。
#### 等待事件发生
接下来,程序需要等待在注册的文件描述符上发生的事件。这通过调用 `epoll_wait()` 函数完成,该函数会阻塞直到有事件发生或者超时。
```c
#define MAX_EVENTS 10
struct epoll_event events[MAX_EVENTS];
int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}
```
此代码段展示了如何等待事件的发生。
#### 处理事件
当 `epoll_wait()` 返回时,它会返回一个包含所有已触发事件的数组。应用程序需要遍历这个数组并处理每个事件。
```c
for (int i = 0; i < nfds; ++i) {
if (events[i].data.fd == listen_sock) {
// 处理新的连接请求
} else {
// 处理数据读取或写入
}
}
```
上述代码提供了一个处理事件的基本框架[^2]。
#### 删除文件描述符
如果某个文件描述符不再需要被监视,应该从 epoll 实例中移除。同样使用 `epoll_ctl()` 函数,但这次传递 `EPOLL_CTL_DEL` 标志。
```c
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {
perror("epoll_ctl: DEL");
exit(EXIT_FAILURE);
}
```
此代码示例说明了如何从 epoll 实例中删除一个文件描述符[^2]。
### 示例代码
下面是一个简单的 TCP 服务器示例,展示了如何使用 epoll 来处理多个客户端连接。
```c
#include <sys/epoll.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define PORT 8080
#define MAX_EVENTS 10
int main(void) {
int listen_sock, conn_sock, epoll_fd;
struct sockaddr_in serv_addr, cli_addr;
socklen_t cli_len = sizeof(cli_addr);
struct epoll_event event, events[MAX_EVENTS];
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);
if (bind(listen_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
perror("bind");
close(listen_sock);
exit(EXIT_FAILURE);
}
if (listen(listen_sock, SOMAXCONN) == -1) {
perror("listen");
close(listen_sock);
exit(EXIT_FAILURE);
}
epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
close(listen_sock);
exit(EXIT_FAILURE);
}
event.events = EPOLLIN;
event.data.fd = listen_sock;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &event) == -1) {
perror("epoll_ctl: listen_sock");
close(listen_sock);
close(epoll_fd);
exit(EXIT_FAILURE);
}
while (1) {
int n, i;
n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (i = 0; i < n; i++) {
if (events[i].data.fd == listen_sock) {
conn_sock = accept(listen_sock, (struct sockaddr *)&cli_addr, &cli_len);
if (conn_sock == -1) {
perror("accept");
continue;
}
event.events = EPOLLIN | EPOLLET;
event.data.fd = conn_sock;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &event) == -1) {
perror("epoll_ctl: conn_sock");
close(conn_sock);
}
} else {
char buf[1024];
int count = read(events[i].data.fd, buf, sizeof(buf));
if (count == -1) {
perror("read");
close(events[i].data.fd);
continue;
} else if (count == 0) {
close(events[i].data.fd);
continue;
}
write(events[i].data.fd, buf, count);
}
}
}
close(listen_sock);
close(epoll_fd);
return 0;
}
```
此代码展示了一个基于 epoll 的简单 TCP 服务器,能够处理多个客户端连接[^1]。
###