对IO复用以及五种IO模型的理解

阻塞IO

当系统调用没有拿到想要的数据时,它就会一直在等待,不会做其他事情,直到拿到了想要的数据或者资源,它才会返回调用成功的结果。

非阻塞IO

当系统调用产生,但是对于想要的数据来说它还没有被内核处理好,那么它不会一直等待,它立即返回,然后每过一段时间来查询数据是否被准备好。最后数据被读取后便不再轮询检查,直接返回调用结果

信号驱动IO

系统调用只管产生,然后便不管了,当有数据可以返回时,SIGIO信号会通知应用程序数据已经准备好了,快来读取吧!

IO多路转接

通过这个名字我们就可以 知道,对于整个系统调用来说,主要在于文件描述符的多个等待。

当只要有一个文件描述符准备好了就立马返回,而不用去等待每一个。

在高级IO部分,这种方式应用较多。

实现方式有三种:

  • select函数:

主要用于监听用户感兴趣的文件描述符上的异常,数据可读可写等事件。

函数原型:

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

参数nfds是需要监视的最⼤大的⽂文件描述符值+1;
rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写⽂件描述符的集合及异常⽂件描述符的集合,用户可通过这三个参数传入自己感兴趣的文件描述符。
参数timeout用来设置select()的等待时间,它是一个timeval结构体类型的指针。如果timeout为NULL,则说明select将一直呈现阻塞状态,直到有一个文件描述符就绪则立即返回。

select 返回值返回的是就绪文件描述符的总数,当设置的时间超时还没有数值返回时就会返回0,当select失败时返回-1。

需要注意:每次调用select前都需要重新设置文件描述符的集合

特点:支持的文件描述符的个数取决于sizeof(fdset), 将fd加入到select的监控集后,还需要定义一个数组来保存原生的fd,当有描述符发生变化,并且select返回后,就拿返回的结果和这个数组比较。当一次select调用完成之后,监控集上的所有未发生变化的值都需要清空。下一次调用时,从之间的数组里再读取值。

缺点:每次调用需要手动设置接口,不方便。调用接口将fd集合从用户态拷贝到内核态,需要开销。

  • poll调用

函数原型:

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

// pollfd结构
struct pollfd {
int fd; /* 文件描述符*/
short events; /* 注册的事件*/
short revents; /* 实际发生的事件,由内核进行修改,用来通知应用程序fd实际上发生了哪些事件 */
};

fds是一个poll函数监听的结构列表. 每⼀个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合.
nfds表⽰示fds数组的⻓度.
timeout表⽰示poll函数的超时时间, 单位是毫秒(ms),timeout=-1,poll调用阻塞,直到某个事件发生,timeout=0,立即返回

缺点:

  • epoll系统调用

epoll与select和poll区别在于,epoll不是一个函数,而是一套函数的调用。

epoll创建句柄:
int epoll_create(int size)
创建一个epoll的句柄.,size参数是被忽略的.
用完之后, 必须调用close()关闭.

epoll的事件注册函数.:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
它不同于select()是在监听事件时告诉内核要监听什么类型的事件, 而是在这里先注册要监听的事件类型.。

第一个参数是epoll_create()的返回值(epoll的句柄).
第二个参数表⽰示动作,⽤三个宏来表示.EPOLL_CTL_ADD,EPOLL_CTL_MOD,EPOLL_CTL_DEL
第三个参数是需要监听的fd.
第四个参数是告诉内核需要监听什么事.

epoll等待函数:

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

函数功能:在一段超时时间内等待一组文件描述符上的事件。

函数参数解释:

第一个参数是create的句柄

参数events是分配好的epoll_event结构体数组.
epoll将会把发生的事件赋值到events数组中 (events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存).
maxevents告之内核这个events有多⼤,这个 maxevents的值不能⼤于创建epoll_create()时的size.
参数timeout是超时时间 (毫秒,0会立即返回,-1是永久阻塞).
如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时, 返回小于0表示函数失败.

特点:避免用户拷贝产生的开销

异步IO

当内核将数据拷贝完成以后,就会告诉通知应用程序,这时直接可以返回结果。

猜你喜欢

转载自blog.csdn.net/qq_36474990/article/details/81112609