简单理解IO模型

网上那些同步、异步、阻塞、非阻塞概念容易弄得很混乱。

以我的理解,只要知道原理即可,没必要为了一些概念弄得很混乱。

首先,要明白IO模型的用处是:用来从IO读取、或者写入数据。

以IO读取为例,有两个阶段:

1、阶段一,等待内核准备好数据;

2、阶段二,将数据从内核区拷贝到用户区。

以下对5种IO模型分别作简单介绍。

1、最简单的方法,告诉内核需要哪些数据,然后等待,直到有了数据之后,从内核区拷贝到用户区,也就是所谓的阻塞IO。

实现方法:recvfrom

2、告诉内核需要哪些数据,不等待,过一段时间去检查下,是否有数据,有则拷贝,没有则继续等待检查,这就是非阻塞IO。

实现方法:recvfrom+nonblock

3、同时监视多个IO端口,只要有数据就处理。这就是IO多路复用。

实现方法:select 、poll或者epoll

4、告诉内核需要哪些数据,既不等待,也不轮询,等到有数据后,内核主动通知,应用程序自己去拷贝,这就是信号驱动IO。

5、和信号驱动很像,只不过内核有数据后,并将数据从内核区拷贝到用户区,再通知应用,也就是异步IO。

方法:aio_read


目前,用的最多是IO多路复用。

对IO多路复用做个简单介绍。

我们都知道IO多路复用,有三种类型,分别是select, poll, epoll

1、select

int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *excepset, const struct timeval *timeout);

返回值:就绪描述符的数目,超时返回0,出错返回-1

缺点:

对待测试的描述符个数有限制,最大1024

每次返回后,对readset、writeset、excepset采用轮询的方式判断是否有就绪数据,效率低

每次调用select,都会将readset、writeset、excepset从用户区拷贝到内核区,同样,返回时,又会将三个数据结构从内核区拷贝到用户区,大量的空间复制,效率低


2、poll

int poll(struct pollfd *fds, unsigned int nfds, int timeout);

poll是对select的改进。

改进点:没有最大个数的限制。

select的其他缺点依然存在。

3、epoll 

int epoll_create(int size);

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

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

epoll对select存在的几个缺点分别有改进。

轮询的方式改为:wait事件返回后, 返回值是需要处理的事件数目,并且将就绪事件存放在events中,避免对非就绪fd的轮询。

大量内存复制改为:由原来一个接口改为三个接口,并只在接口epoll_ctl才将内容拷贝到内核,避免每次查询都拷贝。并且,不管是epoll_ctl的用户区拷贝到内核区,还是epoll_wait的内核区拷贝到用户区,都采用共享内存技术。

epoll还有一个优点,支持边缘触发。

所谓边缘触发,就是当描述符从未就绪变为就绪状态时,内核通过epoll告知应用,应用如果不处理,则下次内核不会告知这个变化。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容