epoll调用

本文介绍了Linux内核中Epoll机制的基本原理及使用方法,对比了Epoll与Select的区别,并详细解释了Epoll的三个核心函数:epoll_create、epoll_ctl和epoll_wait的功能与应用场景。
在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。
相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:
#define __FD_SETSIZE    1024
表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。

epoll的接口非常简单,一共就三个函数:
1. int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。


2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里


3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待事件的产生,类似于select()调用。参数events用来返回从内核得到事件的集合maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

使用epoll的注意事项 
1. ET模式比LT模式高效,但比较难控制。 
2. 如果某个句柄期待的事件不变,不需要EPOLL_CTL_MOD,但每次读写后将该句柄modify一次有助于提高稳定性,特别在ET模式。 

3. socket关闭后最好将该句柄从epoll中delete(EPOLL_CTL_DEL),虽然epoll自身有处理,但会使epoll的hash的节点数增多,影响搜索hash的速度。


原文地址: http://hi.baidu.com/kwokwing0011/blog/item/a68d5a62a6e0aad08cb10d72.html


### 实现用户空间下的类epoll功能 为了在Linux用户态下模拟`epoll`的行为,可以采用多线程或多进程模型配合轮询机制来监视多个文件描述符的状态变化。虽然这种方式无法完全达到内核级`epoll`的效率和特性,但对于学习目的来说是一个不错的实践方法。 #### 使用Python作为示例语言 下面展示了一个简单的基于Python的实现方案,该方案利用了`select.select()`函数来模仿`epoll_wait`的功能[^1]: ```python import select import socket def create_server_socket(port): server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_sock.bind(('localhost', port)) server_sock.listen(5) return server_sock def main(): # 创建监听套接字 listen_sock = create_server_socket(8080) # 初始化要监控的socket列表 sockets_to_monitor = [listen_sock] while True: readable_sockets, _, _ = select.select(sockets_to_monitor, [], []) for sock in readable_sockets: if sock is listen_sock: client_conn, addr = sock.accept() print(f'Connection from {addr}') sockets_to_monitor.append(client_conn) else: try: data = sock.recv(1024).decode('utf-8') if not data: raise Exception("Client disconnected") response = f"Received: {data}" sock.sendall(response.encode()) except Exception as e: print(e) socks.remove(sock) if __name__ == '__main__': main() ``` 此脚本创建了一个TCP服务器并接受来自客户端的消息请求。每当有一个新的连接到来或是已有的连接上有可读的数据时,都会触发相应的处理逻辑。这里通过`select.select()`实现了对多个文件描述符的同时监测,这类似于`epoll_wait`的工作方式[^2]。 需要注意的是,在实际应用中,对于更高层次的需求比如边缘触发模式、超时设置等功能,则需要进一步扩展上述基础框架才能更好地贴近真实的`epoll`行为[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值