Linux常见IO模型-3

在前两篇博客中,我们讲述了常见的IO模型,并对多路转接模型中的select模型进行了细致介绍和具体的代码实现。那么接下来,我们在本篇博客中将对剩余的多路转接模型中的:poll和epoll进行介绍。

目录

1.poll

1.1操作流程

1.2代码实现

2.epoll

2.1操作流程

2.2代码实现

2.3触发方式

2.3.1水平触发

2.3.2边缘触发

1.poll

1.1操作流程 

一、我们需要定义一个事件结构体数组,来存取监控事件和它们的状态,具体的结构体内容如下:

struct pollfd {
    int fd;//需要进行监控的文件描述符
    short events;//想要监控的事件, POLLIN--可读 POLLOUT--可写
    short revents;//监控返回后,存储实际就绪的事件
};

二、我们对不同的描述符按需进行具体的监控事件,并在数组中进行设置,例如:

//定义具体结构体数据
struct pollfd fds[MAX];
//对标准输入进行可读事件监控
fds[0].fd = 0;
fds[0].events = POLLIN;
//对标准输出进行可写事件监控
fds[0].fd = 1;
fds[0].events = POLLOUT;

三、开始监控,原理和select相似,依旧是将数组中有效数据拷贝到内核当中,进行多次的轮询遍历,第一次遍历:判断有无就绪事件,没有则挂起到监控队列;第二次遍历:进程阻塞被唤醒之后,进行一次遍历,对每个元素的revents设置实际的就绪事件。

我们介绍具体的监控接口如下:

int poll(struct pollfd *fds, nfds_t maxevents, int timeout);
/*其中,
    fds是定义事件结构体数组首地址
    maxevents数组中有效元素个数
    timeout是监控阻塞的超时时间,以毫秒为单位
*/

具体的返回值为:>0表示实际就绪的事件个数;==0表示超时;<0表示出错。

四、 调用返回之后,遍历事件结构体数组,根据revents成员确定描述符是否就绪了某个事件,进而对描述符进行操作。

1.2代码实现

我们设计简单代码,对poll模型加以使用,即对标准输入进行读端监控,得到具体代码如下:

然后编译并执行可以得到结果如下:

超时警告和读端监控都可正常运行。

2.epoll

2.1操作流程

一、在内核中创建epoll句柄即eventpoll结构,具体接口如下:

int epoll_create(int size);
/*
    其中,size是所监控的描述符上线,在Linux2.6.8中被忽略,但必须大于0
*/

具体的返回值是:返回epoll的描述符,创建错误则返回-1。(我们可以用epoll监控epoll)

二、向内核的句柄中,添加/移除/修改所要监控的描述符即对应的事件结构,具体的操作接口如下: 

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev);
/*
    其中,epfd是epoll_create所返回的epoll描述符
    op是对epoll要进行的操作,EPOLL_CTL_ADD/EPOLL_CTL_DEL/EPOLL_CTL_MOD
    fd是要进行操作的描述符
    struct epoll_event *ev是对描述符进行操作的详细信息  
*/

我们对其中结构体struct epoll_event进行介绍,其中包括一个uint32_t类型的变量,代表想要监控的事件,以及监控后存放实际就绪的事件;还存在一个联合体,用来存储额外信息。

三、开始监控,epoll的监控是一个异步阻塞操作,发起监控调用是为了告诉操作系统,可以开始监控,并且具体的监控任务由操作系统完成,而系统内容为epoll的每个描述的就绪事件挂了一个回调函数。

该回调函数的功能就是:描述符一旦就绪了指定的事件,将事件信息拷贝一份到epoll_create接口所创建的双向链表rdlist中,那么rdlist的作用便是:存放就绪的描述符对应的事件结构。

一旦系统监控中存在描述符就绪,则唤醒进程的阻塞,进程被唤醒之后,将会查看rdlist双向链表中是否由数据,便可确定是否有描述符就绪。(进程只需要判断rdlist链表是否为NULL,便可知是否存在事件就绪,不需要进行遍历)

具体的操作接口如下:

int epoll_wait(int epfd, struct epoll_event *evs, int maxevents, int timeout);
/*
    其中,epfd时是epoll_create所返回的描述符
    evs是epoll_event结构体数组的空间首地址,接收就绪事件
    maxevents是数组的最大元素个数,也表示了当前想要获取的最大事件个数
    timeout是设置的监控时间--以毫秒为单位
*/

具体的返回值为:>0表示实际就绪的事件个数;==0表示超时;<0表示出错。

2.2代码实现

我们首先设计Epoll类,便于后续具体操作,具体内容如下:

其中使用到的tcp_socket.hpp头文件内容在Linux常见IO模型-2中进行过编写和展示,并且其中的客户端代码不变,对服务端代码进行修改如下:

最后编译并执行,得到结果如下:

2.3触发方式

2.3.1水平触发

水平触发是只要存在事件满足对应触发条件,则会触发对应的事件。

  • 可读:缓冲区中数据大小大于高水位标记(默认1字节),就会触发可读事件;
  • 可写:缓冲区中剩余空间大小大于高水位标记,则会触发可写事件。

2.3.2边缘触发

边缘触发是尽量让用户在一次事件触发中,将所有能够的数据全部都处理完毕,尽量减少事件触发次数,减少运行态切换次数。

  • 可读:每当套接字有新的数据到来的时候,则会触发一次事件;
  • 可写:缓冲区剩余空间从无到有的时候,才会触发一次事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值