一顿饭的事儿,搞懂了 Linux5 种 IO 模型
大家好,人生有三大难题,事业、爱情,和
——这顿吃什么!
人在家中躺,肚子饿得响,又到了不得不吃的时候,这顿饭该怎么吃?吃什么呢?
Linux 里有五种 IO 模型:阻塞 IO、非阻塞 IO、多路复用 IO、信号驱动式 IO 和异步 IO,我发现这五种 IO 模型,其实能和吃饭这件事关联起来。
阻塞 IO(Blocking I/O)
阻塞 IO 是最常见的 IO 模型。
当发起一个 IO 操作时,比如读取数据,系统会调用 read()函数。如果请求的数据没有准备好,此时进程会被挂起(blocked),进入等待状态。直到数据准备好,而且复制到应用进程的缓冲区,这时候才会返回。
从调用到返回,整个时间段都是阻塞的,所以被称为阻塞 IO。
就像是手机没电的时候,去饭馆吃饭,我点完菜,只能等着厨师做好,服务员端上来,我才能愉快干饭。这段时间,我就只能坐在座位上干等。
非阻塞 IO(Non-Blocking I/O)
阻塞 IO,还是比较浪费资源的,那么非阻塞 IO 就来了。
所谓非阻塞 IO,是在调用 IO 操作时,如果缓冲区没有数据的话,直接返回一个错误码。应用进程需要不断轮询,来检查数据是否准备好。数据准备好了,就返回数据。
就像是我奢侈一把,想吃个西餐,于是就去了肯德基,点完餐,我就可以坐着刷刷手机。当然,我还需要时不时地看看我的餐是不是已经备好,餐备好了,就去取一下。
多路复用 IO(I/O Multiplexing)
虽然非阻塞 IO 相比阻塞 IO,性能提升了很多,但是轮询过程中,还是有大量的系统调用,上下文切换的开销比较大。
那么,多路复用 IO 就来了。
多路指的是多个数据通道,复用指的是一个进程可以同时监控多个文件描述符(比如 socket),当某个文件描述符状态发生变化(比如变得可读或可写),多路复用的函数将返回变化的文件描述符。
这样,在数据传输过程中,同一个进程中不同的任务都能被处理。特点是在数据传输过程中,进程能够同时处理多个任务,提高了程序的效率。
select、poll、epoll 等都是 I/O 多路复用的具体实现。
以 select/poll 为例,进程通过将一个或多个 fd 传递给 select 或 poll 系统调用,阻塞在 select 操作上,这样 select/poll 可以侦测多个 fd 是否处于就绪状态。当有 fd 就绪时,立即回调函数 rollback,接下来就可以进行读取。
就像是我想吃顿好的,于是选择去吃自助餐,自助餐有很多餐区,我先看看哪个餐区有我想吃的菜,然后端着盘子去取就行了,一个人就可以取多个菜,肉、蔬菜、水果,什么都能吃一点,而且不用怎么等。
信号驱动式 IO(Signal-Driven I/O)
信号驱动式 IO 利用信号机制来进行数据传输。
进程首先告诉内核,当数据准备好时,请发送一个 SIGIO 信号。进程继续执行其他任务,等到收到信号后,再开始进行数据传输。
就像是我去吃饭,外带,跟服务员打声招呼,餐好了通知我,这时候我就可以去干其它事情,餐备好之后,服务员通知我,我取餐就行了。
异步 IO(Asynchronous I/O)
异步 IO 是指当发起一个 IO 操作后,系统会立即返回。异步 IO 操作在后台进行数据传输,数据传输完成后,系统将通知进程。这样,在整个数据传输的过程中,进程都可以执行其他任务,不需要等待。
就像是准备吃饭了,我自己懒得动,直接在某团上点个餐,点完之后爱干啥干啥,等着快递小哥给我送到就行了。
可以看到,阻塞和非阻塞主要指的是等待数据这个过程应用进程需不需要挂起,同步和异步指的是等待数据和数据拷贝这两个过程应用进程需不需要挂起,只有异步 IO 做到了完全异步。
那么最后一个问题,我到底吃了啥?——
搞点腊肉,煮了个面条,真香!
评论