网上有很多介绍libeio的文章(Google中搜索libeio,可以搜到《libeio-异步I/O库初窥-奋斗小子的专栏-博客频道-CSDN.NET》、《Node.js代码阅读笔记之libeio- CNode》等),但是对demo程序的讲解不是很细致,这里我有点心得补充,希望给看不懂原文的朋友一些帮助。
1、libeio也是使用多线程来实现异步IO的。简化demo中want_poll和done_poll运行在libeio的线程中;eio_mkdir注册的回调函数res_cb是真正用来处理数据的函数,该函数在主线程上下文中执行的。
2、简化demo中管道respipe的作用是给主线程一个运行的时机。我们的期望的行为是:没有事件时主线程阻塞在poll等事件点上,到io完成时唤醒主线程处理数据。管道respipe的作用正是如此,管道的作用就是让主线程读取管道阻塞。当事件来临时,libeio的线程执行want_poll回调函数通过写入管道来唤醒主线程。其实可以用除了管道和poll之外任何其他的异步事件机制,这里只是举例子。
3、want_poll在异步操作完成后调用,代表libeio有事件要通知主线程;done_poll是为了和want_poll成对出现使用,目的是提供一个时机可以释放want_poll中申请的资源。例如读出向管道中写入的字符。
使用libeio的流程是:1、调用eio_init初始化并注册回调函数;2、调用eio_xxxx等函数执行异步操作;3、在得到want_poll函数通知的时候由主线程调用eio_poll处理请求。eio_poll中会调用eio_xxxx时注册的回调函数来处理读取到的数据,这样可以保证这个回调函数的执行是在主线程上下文中执行的。
其中如何通过want_poll唤醒主线程进行执行呢?最简单的方法就是使用管道,主线程读取空管道而阻塞,在want_poll函数中写入管道从而唤醒主线程(done_poll用于清除管道中写入的字符,这样做原因有二:1、将字符从管道中读出,这样内核就会释放用于保存管道内容的缓存;2、将管道恢复成为空的状态,防止主线程使用level-triger方式时反复收到事件)。当然除了管道和poll也可以配合libev使用,官方帮助文档中给出了一个例子,用的是ev_async的监听器,由于这种监听器不需要释放资源因此done_poll可以为NULL。如果遇到不明白的地方还是建议看看官方的man手册(http://pod.tst.eu/http://cvs.schmorp.de/libeio/eio.pod),还是官方的手册比较详细和准确。