选取js read来理解nodejs文件操作的流程
首先来看下,JS文件read函数的实现
未尾一句话,表明它调用了C层的read函数。
在src/node_file.cc里面的initialize函数初始化,绑定了JS与C的操作接口
我们这里以read为例研究流程,那么就跳到read实现,如下图
这里核心调用就是AsyncCall,来看它的实现
关键调用req_wrap->Dispatch.来找找它的实现,在src/req_wrap-inl.h文件里面
那么callLibuvFunction又是什么,如下图代码实现
看到这里就知道了 fn 就是uv_fs_read , read函数实际就是调用uv_fs_read这个函数。然后我们来看uv_fs_read这个接口的实现这里进行了一些初始化的动作,还没有到IO操作。继续往下跟,看宏POST的实现
这里传进去两个函数指针,一个是需要执行的操作,一个是执行完后需要的操作
到这里就看到了read的具体实现uv__fs_read,这个函数就是具体实现。看到我们熟悉的read操作啦。那么done又是什么,看实现
就一句话调用req->cb函数指针。cb是什么呢,往上爬楼。可以看到cb就是我们JS里面传递进来的回调函数.到这里脑补下基本上就能圆上来了,继续往下看
这里又有个 post来看看它的实现。重头戏来了,它的实现在deps/uv/src/unix/threadpool.里面
这段代码的意思,就是把work加入到低速工作队列slow_io_pending_wq末尾.那么slow_io_pending_wq又是在什么时候初始化的呢,找了下也是在同一个文件里面好么,这里进行的初始化.然后后面就开始创建线程了,不是说nodejs只有一个线程么?不管了继续看吧,这一下就创建了四个线程,看下线程执行函数worker.就是从run_slow_work_message队列中取出一个QUEUE.然后执行work(这里就是uv__fs_read).
再把自己加入到UV轮循主线和的 wq队列中
然后调用uv_async_send通知nodejs轮循事件主线程,告诉它事情做完了.并将它自己挂到主线程的列队中去,以便主线程调用JS回调函数
就是向主线的观察描述符随便写点数据,以便触发主线async_io_watcher的观察者
当async_io_watcher里面有数据了以后.会跳出这个for循环,然后把自己加入到async_handle队列.同时调用async_cb函数.这个函数指针哪里来,也在同文件里面
可以看到在初始化的时候对其进行赋值.这个初始化调用者就是uv轮循事件主线
在deps/uv/src/unix/loop.c里面,uv_loop_t 结构体初始化函数里面
这里就会对uv__fs_done进行调用了.而uv__fs_done又会调用JS的回调函数.至此流程结束,全是代码不太好看.