五种基本IO模型介绍

一、阻塞式IO

大家最开始接触网络编程时,基本都是接触的阻塞式IO,在一个基本的TCP服务器程序中,我们使用recv()函数来接收对端发送的数据,而在对端未发送数据时,我们的服务器是阻塞在recv()函数处的,如:

recv(connFd, buf, MAX_RECV_DATA, 0);

以上就是一个阻塞式IO的基本例子,而使用recv()函数即为一次系统调用,此时会从应用切换到内核中运行,一段时间后再切换回来。我们以图表的形式来描述阻塞式IO模型:
在这里插入图片描述

二、非阻塞式IO

在介绍了阻塞IO后,我们不免会对非阻塞IO有对应的思考,阻塞IO是指系统调用时,我们的应用进程会等待内核返回的结果,这一等可能就是海枯石烂了,因此,非阻塞IO就应运而生啦!
非阻塞IO的做法是将我们使用的套接字设置为非阻塞,如:

fcntl(connFd, F_SETFL, O_NONBLOCK);

这样设置就相当于跟内核说:“哥们你好好干,我会按时来问你的”。于是乎,在非阻塞的情况下,我们需要在一个循环中不断地查询内核是否准备好数据,如果没准备好,内核会返回给我们一个EWOULDBLOCK的错误,相比于阻塞式IO呆呆地等在原地啥也不干,非阻塞显然可以在循环中处理一些其他事情,当然,频繁的调用recv()函数即频繁的进行系统调用,这会耗费我们大量的CPU资源的。我们同样给出相应图表:
在这里插入图片描述

三、IO复用

非阻塞IO虽然不用一直等待那啥也干不了,但是需要一直去轮询内核,浪费我们的CPU资源,此时我们或许会想,为啥不让内核主动告诉我们数据已经准备好了呢,这个时候我们再去取数据不就方便多了吗?
没错,这正是IO复用的初衷,我们熟悉的select/poll/epoll就是为了实现这种想法,同时能够支持多个套接字的网络事件,而select/poll/epoll的区别相信大家都有所了解,主要是支持的套接字数量和内核通知的区别,这里不展开进行讲述了哈。
下面以select为主,给出IO复用的图表:
在这里插入图片描述
可能有同学会产生疑问,这IO复用和阻塞IO一比好像没啥优化啊,都是一直阻塞在系统调用,实则不然,使用select/poll/epoll能够检测成百上千个甚至数十万的套接字,为实现咱们高并发打下了基础,这就是IO复用的优势哈!

四、信号驱动式IO

IO复用存在的问题式:select函数阻塞调用,等待数据处理完,我们在使用recv()函数发起系统调用去取数据。而信号驱动IO相比于IO复用,先注册了信号处理函数,当内核准备好数据时,给用户进程发送约定好的信号,然后用户进程再使用recv()函数取数据,这样就使得数据从等待到准备完成的期间用户进程不被阻塞。
我们给出对应图表:
在这里插入图片描述

五、异步IO模型

经过上面四种IO模型的介绍,它们或多或少都有不足,用户进程都需要阻塞在某个地方来等待相应结果,那么有没有一种模型不需要用户进程来等待内核返回的结果呢?当然是有的,异步IO模型就支持我们的想法,我们使用aio_read()进行一次系统调用之后,不需要等待内核准备数据,用户进程可以继续干其他事情,内核处理好之后再发送aio_read()中指定的信号给用户进程中设置的信号处理函数进行处理即可。
对应的IO模型如下:
在这里插入图片描述

咱们的IO模型介绍就告一段落啦,文中图表借鉴的是《UNIX环境编程第3版》。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿杰的小鱼塘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值