File IO
The previous chapter discussed the thread pool model, which gave readers a rough understanding of the process of asynchronous IO from initiation to completion.
The fs module also provides synchronous interfaces, such as readFileSync
, which is rare in the core modules of node.js's asynchronous model.
Request Object
Asynchronous file reading interface definition:
fs.readFile = function(path, options, callback_)
Synchronous file reading interface definition:
fs.readFileSync = function(path, options)
The obvious difference between the two is the third parameter callback_
. Asynchronous requests submit the request and wait for the callback, while synchronous requests block until they return.
Let's take a look at the impact of the third parameter on the implementation.
In the implementation of asynchronous requests, a request object is created and the callback and context environment are bound to it. This request object is exported by C++ bindings.
FSReqWrap (node.js)
FSReqWrap is implemented and exported by src/node_file.cc
for use by JavaScript.
FSReqWrap inherits from ReqWrap<uv_fs_t>, ReqWrap is a template class, T req_;
stores requests of different types, and after template compilation, uv_fs_t req_;
stores the uv_fs_t request object. This comes from a performance optimization submission.
fs: improve
readFile
performance
This commit improves
readFile
performance by reducing number of closure allocations and usingFSReqWrap
directly.
For more information, see https://github.com/iojs/io.js/pull/718.
After the request is initiated in JavaScript, it will come to the C++ binding layer,
Here, the libuv request object required is generated, and for read requests, uv_fs_read
is called to submit the request and specify the callback function as After
.
uv_fs_t (libuv)
Let's take a look at the asynchronous file reading code in libuv, deps/uv/src/unix/fs.c:
It can be seen that an asynchronous file reading operation is encapsulated in a uv_fs_t structure in the libuv layer, and req->cb is the callback function from the upper layer (the After function in node C++ layer: src/node_file.cc).
The main body of the io operation uv__fs_work
is executed in the thread pool at this time. However, uv__fs_done
must be called back in the thread of the event loop, because this function will eventually call back to the user's js code callback function, and all the code in the js code must be in the same thread.
Thread Pool Request Object —— struct uv__work
First, let's look at the definition of uv__work
:
Let's take a look at what uv__work_submit
does:
uv__work_submit
encapsulates uv__fs_work
and uv__fs_done
into a uv__work
structure, which represents a request for a thread operation. The request is submitted to the thread pool through post
.
In the post
function, QUEUE_INSERT_TAIL
adds the uv__work
object to the wq
linked list. wq
is a global static variable. That is to say, all threads in the process space share the same wq
linked list.
When post
sends a signal to the corresponding condition variable cond
through uv_cond_signal
, a thread that is suspended and waiting in uv_cond_wait
is activated.
The worker thread continues to execute, takes out w
from wq
, and executes w->work()
.
After the worker thread completes the task, it calls uv_async_send
to notify the main thread's unified IO observer to execute the callback.
Callback
Summary
Through the sorting of request objects at each layer, the context of a read request is also detailed, giving readers a rational understanding.
Reference
Last updated