linux 下的io笔记

来记一笔linux下的io相关的系统调用select,poll,epoll。这三个系统调用的本质其实都是io多路复用,有人觉得epoll是信号驱动io,其实不然。

先来说下linux下的io大背景。在linux系统上一切皆文件,不管是读写文件,管道,网络io,或者其他外设,最后都会调用read和write系统调用。这两个系统调用都有两个流程,以read为列

1 把数据从外设读到内核的缓冲区

2 把数据从内核地址空间拷贝到用户地址空间。

这里的第二步是内存操作,对io操作的block时间可以忽略。所以绝大多数io是block在第一步。 即使是使用非阻塞模式运行,返回eagain,也要在循环里面多次轮询,浪费cpu时间。举个不恰当的例子,比如你要去便利店买东西,需要排队,而且每次只能买一样东西,你排着队好不容易轮到你,大爷告诉你没货,又要从队尾开始排。这样的用户体验肯定很糟。

于是乎,有天大爷装了个提示器,上面每次显示一串可以立即提货的货物。你要做的是逐一比对是否有你自己想要的。这其实就是io多路复用。select和poll每次都是轮询一堆描述符,然后返回准备好的描述符数量。区别在于poll要监视的fd数量没有上限。select和poll的这种实现决定了,这两个系统调用返回以后,都必须要遍历fds来获取准备好的fd,所以当监视的fd数量很多时候,性能降得厉害。

epoll和select,poll实现有些不同。epoll首先通过epoll_create创建一个efd,通过这个efd来监控其他fd,也就是通过epoll_ctl函数来把fd添加到efd监视列表里。这个列表是由kernel维护的。每次调用epoll_wait返回的fd全部都是已经准备好的fd,可以直接内存拷贝。这也是epoll为什么效率高于select和poll的原因,尤其是在idle fd数量较多的时候,这一优势更加明显。但本质上epoll还是io多路复用,只不过把更多的工作交给了kernel,用户进程只轮询efd一个fd。所以io多路复用还是有阻塞的,只不过不是在read,write上,而是阻塞在了select,poll,epoll上。

回到刚才买东西的例子,你也可以给大爷买包烟,然后请他老人家打电话通知你有你想要的东西,直接来提货。这就是信号驱动io。如果你想体验更好点,再送大爷包茶叶,大爷直接把货送到你家,然后通知你。这就是aio,真正意义上的异步io。

相比windows而言linux对aio的支持比较一般,直到2.6.22才有了些库。当然,异步io增加了kernel实现的复杂度,kernel做了更多的事。这和unix设计哲学不符。把更多的自由留给用户这也是xnix系统上编程框架百花齐放的原因。

你可能感兴趣的:(linux 下的io笔记)