前言
在介绍Unix IO模型之前,我们先来说说什么是IO。根据维基百科的定义,IO 指的是输入输出,通常指数据在内部存储器和外部存储器或其他周边设备之间的输入和输出。简而言之,从硬盘中读写数据或者从网络上收发数据,都属于IO行为。
以数据输入为例,一个输入操作,通常可以分为2个阶段:
等待操作系统内核把数据准备好
将数据从操作系统内核复制到用户进程空间
这里有一个问题就是,假如操作系统内核还没把数据准备好,这个时候用户进程要怎么处理?是一直等待直到数据准备好,还是隔一段时间来询问一次,又或者是操作系统把数据准备好后去通知用户进程呢?虽然一个IO操作只有2个阶段,但是根据IO是否阻塞、是否同步,却可以把IO再细分成不同的模型。实际上,在Unix网络编程中一共有5种不同的IO模型,下文将进行详细介绍。
Unix IO模型
阻塞式IO模型
阻塞式IO是所有IO模型中最简单的一种,在这种模型下,所有IO操作都是阻塞的。以套接字接口为例,在进程空间调用recvfrom,其系统调用直到数据包到达且被复制到应用进程的缓冲区或者发生错误时才返回,在此期间会一直等待,因此被称为阻塞式IO模型。
阻塞式IO就像是在排队买火车票一样,如果前面排队的人很多,你是没办法抽身走开的,只能干等着,如果走开一会儿就得重新排队了,因此阻塞式IO效率是十分低下的。
非阻塞式IO模型
非阻塞式IO模型跟阻塞式IO模型的主要区别是如果操作系统内核没有把数据准备好,recvfrom会直接返回一个错误,而不是一直阻塞。
非阻塞式IO就像是去小饭馆吃饭一样,你去的时候如果人很多,你可以先取号,取完号之后可以先在周围逛逛,等时间差不多了再到饭馆看看是否轮到自己就餐了。与阻塞式IO相比,非阻塞IO效率有所提升,因为不用一直待在原地“排队”,不过还是有一个缺点就是,要不断地去询问操作系统内核是否已经把数据准备好了。
IO复用模型
Unix中提供了select/poll来使用IO复用,这样一来用户进程就可以阻塞在select/poll上,而不是阻塞在具体的IO操作上。当操作系统内核将数据准备好之后,select/poll会返回可读条件,然后用户进程再调用recvfrom来将数据复制到用户进程空间。使用多路复用的好处是,我们可以等待多个文件描述符就绪,即select/poll会帮我们侦测多个文件描述符是否就绪,从而使得用户进程不必阻塞于具体的IO操作。
IO复用模型就好像是一群好友到游乐园排队玩儿过山车一样,一群人里面推选出一人在原地排队,其他人先去玩儿其他项目,然后其他人在玩儿的过程中每隔一段时间就打电话问那个排队的人排上号了没,如果排上号了,就过来玩儿过山车,否则就继续玩儿其他项目。可以看到IO复用模型的好处是,用户进程不用阻塞于具体的IO操作,而且多路复用器可以监听多个IO文件描述符。
信号驱动式IO模型
信号驱动式IO是指让操作系统内核在文件描述符就绪时发送信号给用户进程,这样一来用户进程只要调用sigaction后就能立即返回,不会被阻塞。当操作系统的文件描述符就绪时,会发送信号给用户进程,用户进程再调用recvfrom开始IO操作。
信号驱动式IO还是像上述一群好友到游乐园排队玩儿过山车的例子一样,这个时候还是派出一名代表在原地排队,其他人去玩儿其他项目,只是这个时候排队的人如果排上号了会主动在微信群里呼叫(发送信号)其他好友过来玩儿,其他人不用再隔一段时间就打电话给那个排队的人询问排队情况了,只要看微信群消息即可。这就是信号驱动式IO模型跟IO复用模型的区别,前者操作系统会主动发消息给用户进程,后者用户进程还是会阻塞在多路复用器上。
异步IO模型
异步IO模型指的是告诉操作系统内核启动某个操作,并让操作系统内核在完成整个操作后通知用户进程。其与信号驱动式IO的区别是信号驱动式IO模型是操作系统内核通知我们何时开始一个IO操作,而异步IO模型是操作系统内核通知我们该IO操作何时完成。
异步IO模型就像是委托别人去银行办理业务一样,只要将要办的业务以及相关的证件交给委托人,委托人就会帮你去办理,期间你是不需要跑腿的,还是可以做自己的事情,当委托人办完业务后再通知你办理的结果就行了。所以异步IO模型是效率最高的IO模型了,因为你自己不需要“跑腿”,只要敬候佳音就行了。
关于Unix IO模型的介绍就到这里了,如果觉得这篇文章对你有帮助,可以扫描下方二维码关注我的个人公众号。