文章目录
- 概述
- 1. epoll_create - 创建一个epoll实例
- 2. epoll_ctl - 控制epoll实例的事件
- 结构体介绍
- events取值:
- data: 联合体(共用体):
- 3. epoll_wait - 等待事件发生
- 伪代码
- 总结
概述
在网络编程中,高效地处理大量并发连接是一个关键问题。而Linux提供的epoll机制成为了解决这个问题的重要工具。epoll是一种I/O事件通知机制,通过三个主要函数,即epoll_create、epoll_ctl和epoll_wait,实现了高性能的事件驱动并发编程。让我们深入了解这三个函数以及它们的使用方法。
1. epoll_create - 创建一个epoll实例
int epoll_create(int size);
size:创建的红黑树的监听节点数量。(仅供内核参考。)
返回值:指向新创建的红黑树的根节点的 fd。
-1:代表错误
2. epoll_ctl - 控制epoll实例的事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd:epoll_create 函数的返回值。 epfd
op:对该监听红黑数所做的操作。
EPOLL_CTL_ADD 添加fd到 监听红黑树
EPOLL_CTL_MOD 修改fd在 监听红黑树上的监听事件。
EPOLL_CTL_DEL 将一个fd 从监听红黑树上摘下(取消监听)
fd:待监听的fd
event: 本质 struct epoll_event 结构体指针
返回值:成功 0; 失败: -1 errno
结构体介绍
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
events取值:
EPOLLIN : 表示对应的文件描述符可以读(包括对端SOCKET正常关闭)
EPOLLOUT: 表示对应的文件描述符可以写
EPOLLERR: 表示对应的文件描述符发生错误
等等(不重要)
data: 联合体(共用体):
int fd; 对应监听事件的 fd
void *ptr; 回调函数
uint32_t u32;
uint64_t u64;
3. epoll_wait - 等待事件发生
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epoll_wait函数用于等待事件发生,当事件发生时将返回就绪的文件描述符以及对应的事件类型。参数包括:
epfd:epoll_create 函数的返回值。 epfd
events:传出参数,【数组】, 满足监听条件的 那些 fd 结构体。
maxevents:上面数组元素的总个数。 eg:struct epoll_event evnets[1024]->1024
timeout:
-1: 阻塞
0: 不阻塞
大于0: 超时时间 (毫秒)
返回值:
大于0: 满足监听的 总个数。 可以用作循环上限。
0: 没有fd满足监听事件
-1:失败。 errno
伪代码
关于代码实现可以参考:链接
epoll实现多路IO转接思路:
lfd = socket(); 监听连接事件lfd
bind();
listen();
int epfd = epoll_create(1024); epfd, 监听红黑树的树根。
struct epoll_event tep, ep[1024]; tep, 用来设置单个fd属性, ep 是 epoll_wait() 传出的满足监听事件的数组。
tep.events = EPOLLIN; 初始化 lfd的监听属性。
tep.data.fd = lfd
epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tep); 将 lfd 添加到监听红黑树上。
while (1) {
ret = epoll_wait(epfd, ep,1024, -1); 实施监听
for (i = 0; i < ret; i++) {
if (ep[i].data.fd == lfd) { // lfd 满足读事件,有新的客户端发起连接请求
cfd = Accept();
tep.events = EPOLLIN; 初始化 cfd的监听属性。
tep.data.fd = cfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tep);
} else { cfd 们 满足读事件, 有客户端写数据来。
n = read(ep[i].data.fd, buf, sizeof(buf));
if ( n == 0) {
close(ep[i].data.fd);
epoll_ctl(epfd, EPOLL_CTL_DEL, ep[i].data.fd , NULL); // 将关闭的cfd,从监听树上摘下。
} else if (n > 0) {
小--大
write(ep[i].data.fd, buf, n);
}
}
}
总结
通过使用epoll_create、epoll_ctl和epoll_wait这三个主要函数,我们可以充分利用Linux的epoll机制来实现高效的事件驱动并发编程。首先,通过创建epoll实例,我们能够监控多个文件描述符。然后,使用epoll_ctl函数来添加、修改和删除需要监控的事件。最后,通过调用epoll_wait函数来等待事件的发生并处理就绪的文件描述符。
这些函数的合理使用可以帮助我们有效地管理大量并发连接,提高系统的性能和响应能力。当然,在实际应用中,我们还需要结合其他的网络编程知识和技巧,以实现更加稳定和高效的网络应用程序