1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Linux网络编程 之 IO复用epoll(十)

Linux网络编程 之 IO复用epoll(十)

时间:2024-02-29 06:01:53

相关推荐

Linux网络编程 之 IO复用epoll(十)

1. epoll概述

相对于 select() 和 poll() 来说,epoll 更加灵活,没有描述符限制。epoll 使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的 copy 只需一次。

2. epoll的接口函数

为了在后面更加方便理解epoll的原理,我们先从接口函数开始下手:

epoll_create

创建epoll句柄

函数声明:int epoll_create(int size)

参数:size用来告诉内核这个监听的数目一共有多大。

返回值:返回创建了的epoll句柄。

当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

epoll_ctl

将被监听的描述符添加到epoll句柄或从epool句柄中删除或者对监听事件进行修改。

函数申明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event*event);

参数:

epfd: epoll_create()的返回值

op:表示要进行的操作,其值分别为:

EPOLL_CTL_ADD: 注册新的fd到epfd中;

EPOLL_CTL_MOD: 修改已经注册的fd的监听事件;

EPOLL_CTL_DEL: 从epfd中删除一个fd;

fd:需要操作/监听的文件句柄

event:是告诉内核需要监听什么事件,struct epoll_event如下:

typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t; struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ };

events可以是以下几个宏的集合:

- EPOLLIN:触发该事件,表示对应的文件描述符上有可读数据。(包括对端SOCKET正常关闭);

- EPOLLOUT:触发该事件,表示对应的文件描述符上可以写数据;

- EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);

- EPOLLERR:表示对应的文件描述符发生错误;

- EPOLLHUP: 表示对应的文件描述符被挂断;

- EPOLLET:将EPOLL设为边缘触发(EdgeTriggered)模式,这是相对于水平触发(Level Triggered)来说的。

- EPOLLONESHOT: 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

epoll_wait

等侍注册在epfd上的socket fd的事件的发生,如果发生则将发生的sokct fd和事件类型放入到events数组中。

函数原型:int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

参数:

epfd:由epoll_create 生成的epoll文件描述符

events:用于回传代处理事件的数组

maxevents:每次能处理的最大事件数

timeout:等待I/O事件发生的超时毫秒数,-1相当于阻塞,0相当于非阻塞。一般用-1即可。

3. epoll实现机制

当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。eventpoll结构体如下所示:

struct eventpoll{..../*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/struct rb_root rbr;/*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/struct list_head rdlist;....};

内部用了一个红黑树记录添加的socket,用了一个双向链表接收内核触发的事件。

然后我们通过epoll_ctl方法向epoll对象中添加进来的事件。

这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。

而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。

在epoll中,对于每一个事件,都会建立一个epitem结构体,如下所示:

struct epitem{struct rb_node rbn;//红黑树节点struct list_head rdllink;//双向链表节点struct epoll_filefd ffd; //事件句柄信息struct eventpoll *ep; //指向其所属的eventpoll对象struct epoll_event event; //期待发生的事件类型}

当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。

4. epoll的工作模式

ET(EdgeTriggered):高速工作模式,只支持no_block(非阻塞模式)。在此模式下,当描述符从未就绪变为就绪时,内核通过epoll告知。然后它会假设用户知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到某些操作导致那个文件描述符不再为就绪状态了。(触发模式只在数据就绪时通知一次,若数据没有读完,下一次不会通知,直到有新的就绪数据)

LT(LevelTriggered):缺省工作方式,支持blocksocket和no_blocksocket。在LT模式下内核会告知一个文件描述符是否就绪了,然后可以对这个就绪的fd进行IO操作。如果不作任何操作,内核还是会继续通知!若数据没有读完,内核也会继续通知,直至设备数据为空为止!

二者的差异在于level-trigger模式下只要某个socket处于readable/writable状态,无论什么时候进行epoll_wait都会返回该socket;而edge-trigger模式下只有某个socket从unreadable变为readable或从unwritable变为writable时,epoll_wait才会返回该socket。

从本质上讲:与LT相比,ET模型是通过减少系统调用来达到提高并行效率的。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。