Pulpcode

捕获,搅碎,拼接,吞咽

0%

为什么epoll不支持磁盘IO

对epoll不支持磁盘IO这件事,很长一段时间都是只知道结论,但是从来没搞清楚原因,网上找了一些中文的解释都让人看的云里雾里。最近在查找资料的时候,无意中发现如果自己按照“DISK Io”这样的方式去搜索英文资料,那可能不会找到想要的答案,因为在linux官方的叫法中,磁盘文件准确的称呼应该是“regular files”。然后我按照“epoll regular files”的方式得到的内容一下多了许多。最后也是得到了比较满意的答案,这里写出来分享一下。

阻塞和非阻塞

首先在谈论epoll不支持磁盘IO文件之前,先理清阻塞和非阻塞指的阶段是什么,我们在讨论IO操作阻塞非阻塞时,是指当你发起一个IO操作后,如果IO没有准备好,你的线程是否要停留在那里等待IO准备好。这里特别说明了只是说等待io准备好,系统真正开始io操作的时候,虽然也有耗时,但是那并不算讨论阻塞非阻塞的范畴。

接着在说回到磁盘IO这件事上,虽然linux的哲学是一切都是文件,磁盘和网络的读写都可以理解为文件的读写,但是文件和文件还是有不同的。比如说你并不能对一个磁盘文件描述符设置非阻塞,但是可以对一个网络io设置非阻塞。理解这事的核心点是理解阻塞发生的原因, 既你向操作系统发起io操作的时候,IO还没有准备好,比如在读请求的时候,缓冲区里的数据不够,或者在执行写操作的时候,缓冲区满了,这对网络来说是常态,因为你根本不确定网络连接的另一头到底什么时候会和你收发数据,所以默认阻塞是常态,然后才为了可以让你的程序不用傻等在这里,才设计了非阻塞。但是对于磁盘来说,根本就没有等待可读或可写一说,因为它一直是就绪的,始终可读可写,POSIX规范中明确指出了这一点。我猜测是设计者当时也觉得给磁盘文件io设置非阻塞没什么意义。

非阻塞不一定高效

这里说个题外话,貌似很多人总喜欢把非阻塞和高效率联系到一起,但你仔细想想,无论是阻塞还是非阻塞,就IO本身所花费的时间并没有变化。只不过是在等待io的过程中,你是否还要程序的控制权而已,即使你可以在IO准备好之前做一些别的事,来提高你代码逻辑本身的效率,但那又是另一个话题了,和IO本身无关。即使读写普通文件可能会花费很长时间,比如DiskIo非常的Busy,但是你非阻塞也没有什么用,你在检查磁盘是否可读可写的时候,还是会立刻可读可写的。

epoll为什么不支持磁盘

上面解释了为啥磁盘不支持非阻塞,再来说说为啥epoll不支持磁盘文件了,看下epoll的核心代码就会发现(之前的博客有分析过),其实它是在同时监听多个io是否可读可写(是否Ready),但是前面提到磁盘文件始终是Ready的状态,而实际读取的时候如果文件数据不在内存缓存中的话,read本身还是会”阻塞”住等待数据从磁盘读出来。这看上去使用epoll来处理磁盘IO就完全没什么意义。

为什么kqueue支持磁盘

说完epoll,不得不说另一个多路复用的系统调用kqueue,epoll是POSIX的,而kqueue是FreeBSD的。我们在linux下用的多路复用就是epoll,而在mac这种unix的操作系统中,多路复用的库就是kqueue。虽然epoll不支持磁盘IO,但kqueue却是支持的,为此我又查了一些资料发现了一个关键点,就是kqueue的事件模型要比epoll看上去更“高级”。可能epoll是从select到poll进化来的,所以看上去主要的发力点是对poll的优化。而kqueue抽象出了高级的event事件,相比于epoll只能等io是否ready,kqueue可以直接监听类似“文件数据已经加载到内存”这样的事件,这看上去就比epoll有意义多了。所以kqueue可以支持磁盘io。它相当于托管了整个磁盘IO操作,做到让磁盘io看上去是“非阻塞”的。

相关链接

探讨 regular 文件非阻塞
StackOverflow 磁盘文件用epoll
StackOverflow 磁盘文件用kqueue