Nodejs源码的阅读-事件循环的过程
2014-02-06 23:05
585 查看
Nodejs源码的阅读-事件循环的过程
解读基于node V0.2.0
之前我们对所有的watcher都介绍到了,后面就以具体的事件循环的例子来看,中途可能会遇到上面讲的watcher。
我以一个读文件的例子来说:
var fs = require('fs');
fs.readFile('./main.js','utf8',function(err,text){console.log(text);});
我们来看看readfile是怎么被加入到事件队列,以及readfile的回调函数是怎么被执行到的。
首先var fs = require('fs');这里的fs模块的源码就在fs.js。
在fs.js中我们可以看到:
var binding = process.binding('fs');
......
fs.readFile = function (path, encoding_, callback) {
binding.stat...
binding.open...
Binding.read...
}
其中process.binding是之前在main函数中绑定的函数,
NODE_SET_METHOD(process, "binding", binding);
它指向binding函数,这个函数是这么来获取模块的,
else if ((modp = get_builtin_module(*module_v)) != NULL) {
exports = Object::New();
modp->register_func(exports);
binding_cache->Set(module, exports);
}
因为fs是一个内置的模块,所以get_builtin_module就能够获取到值,它获取到的内容是node_file.cc的内容,所以process.binding('fs')将返回node_file.cc的实例。
因此fs.readFile实际调用的是node_file.cc中的stat,open,read函数,关键当然是read函数。
static Handle<Value> Read(const Arguments& args) {
......
//read函数的最后:
if (cb->IsFunction()) {
ASYNC_CALL(read, cb, fd, buf, len, pos);
} else {
// SYNC
//这里是同步调用的时候
}
}
这里是异步调用,所以走的是ASYNC_CALL(read, cb, fd, buf, len, pos);这个调用。
#define ASYNC_CALL(func, callback, ...) \
eio_req *req = eio_##func(__VA_ARGS__, EIO_PRI_DEFAULT, After, \
cb_persist(callback)); \
assert(req); \
ev_ref(EV_DEFAULT_UC); \
return Undefined();
把参数代入宏,之后:
eio_req *req = eio_read(fd, buf, len, pos, EIO_PRI_DEFAULT, After, cb_persist(cb));
assert(req);
ev_ref(EV_DEFAULT_UC);
return Undefined();
在eio_xx系列的函数中,都会组建一个eio_req结构体,
req->finish = cb; // cb这里是After
req->data = data; // data这里是 cb_persist(cb)
然后把这个结构体推入eio的请求队列。
接着ev_ref(EV_DEFAULT_UC);给我们的事件循环增加计数,目的是防止循环因为没有watcher而退出。这里其实并没有watcher加入循环队列,但是增加计数起到同样的作用。
这时用户的js执行完毕,继而执行process.loop();上一篇关于循环建立中我们知道事件循环这时开始启动。由于刚才引用计数增加了,所以循环一直不退出。
然后某一时刻,推入eio队列的请求完成,即文件读取完毕。
这时eio的完成队列从无变成有,由eio_init(node::EIOWantPoll, node::EIODonePoll);可知,node::EIOWantPoll此时被调用。
node::EIOWantPoll只有一句话ev_async_send(EV_DEFAULT_UC_ &eio_want_poll_notifier);作用是唤醒eio_want_poll_notifier这个watcher。
由ev_async_init(&node::eio_want_poll_notifier, node::WantPollNotifier);可知,node::WantPollNotifier将被调用,其核心代码是:
if (eio_poll() == -1) {
//一次没取完
ev_idle_start(EV_DEFAULT_UC_ &eio_poller);
}
取出每一个eio完成队列中的元素并处理,如果一次eio_poll没处理完,就启动eio_poller这个watcher,这个watcher在前面一篇讲过就是用于一次没处理完所有完成队列的元素,于是开启等下次启动以便再次进行处理。
在处理eio的完成队列的元素时,最终调用:
# define EIO_FINISH(req) ((req)->finish) && !EIO_CANCELLED (req) ? (req)->finish (req) : 0
也就是调用了(req)->finish (req),这个finish我们回头看下:
在eio_xx系列的函数中,都会组建一个eio_req结构体,
req->finish = cb; // cb这里是After
req->data = data; // data这里是 cb_persist(cb)
所以(req)->finish (req)将调用After这个函数。
Persistent<Function> *callback = cb_unwrap(req->data);
ev_unref(EV_DEFAULT_UC);
(*callback)->Call(v8::Context::GetCurrent()->Global(), argc, argv);
上面是After的几个关键代码,第一行是取出之前设置的回调函数,也就是我们js代码给readFile设置的回调函数,第二行是事件循环的计数减1,与之前计数加1相对应,因为读文件完成了,不需要它来保证循环队列非空了,第三行就是调用回调函数。
到这里所有代码都执行完成了。然后由于事件循环已经空了,所以循环也退出了,于是整个程序都退出了。
解读基于node V0.2.0
之前我们对所有的watcher都介绍到了,后面就以具体的事件循环的例子来看,中途可能会遇到上面讲的watcher。
我以一个读文件的例子来说:
var fs = require('fs');
fs.readFile('./main.js','utf8',function(err,text){console.log(text);});
我们来看看readfile是怎么被加入到事件队列,以及readfile的回调函数是怎么被执行到的。
首先var fs = require('fs');这里的fs模块的源码就在fs.js。
在fs.js中我们可以看到:
var binding = process.binding('fs');
......
fs.readFile = function (path, encoding_, callback) {
binding.stat...
binding.open...
Binding.read...
}
其中process.binding是之前在main函数中绑定的函数,
NODE_SET_METHOD(process, "binding", binding);
它指向binding函数,这个函数是这么来获取模块的,
else if ((modp = get_builtin_module(*module_v)) != NULL) {
exports = Object::New();
modp->register_func(exports);
binding_cache->Set(module, exports);
}
因为fs是一个内置的模块,所以get_builtin_module就能够获取到值,它获取到的内容是node_file.cc的内容,所以process.binding('fs')将返回node_file.cc的实例。
因此fs.readFile实际调用的是node_file.cc中的stat,open,read函数,关键当然是read函数。
static Handle<Value> Read(const Arguments& args) {
......
//read函数的最后:
if (cb->IsFunction()) {
ASYNC_CALL(read, cb, fd, buf, len, pos);
} else {
// SYNC
//这里是同步调用的时候
}
}
这里是异步调用,所以走的是ASYNC_CALL(read, cb, fd, buf, len, pos);这个调用。
#define ASYNC_CALL(func, callback, ...) \
eio_req *req = eio_##func(__VA_ARGS__, EIO_PRI_DEFAULT, After, \
cb_persist(callback)); \
assert(req); \
ev_ref(EV_DEFAULT_UC); \
return Undefined();
把参数代入宏,之后:
eio_req *req = eio_read(fd, buf, len, pos, EIO_PRI_DEFAULT, After, cb_persist(cb));
assert(req);
ev_ref(EV_DEFAULT_UC);
return Undefined();
在eio_xx系列的函数中,都会组建一个eio_req结构体,
req->finish = cb; // cb这里是After
req->data = data; // data这里是 cb_persist(cb)
然后把这个结构体推入eio的请求队列。
接着ev_ref(EV_DEFAULT_UC);给我们的事件循环增加计数,目的是防止循环因为没有watcher而退出。这里其实并没有watcher加入循环队列,但是增加计数起到同样的作用。
这时用户的js执行完毕,继而执行process.loop();上一篇关于循环建立中我们知道事件循环这时开始启动。由于刚才引用计数增加了,所以循环一直不退出。
然后某一时刻,推入eio队列的请求完成,即文件读取完毕。
这时eio的完成队列从无变成有,由eio_init(node::EIOWantPoll, node::EIODonePoll);可知,node::EIOWantPoll此时被调用。
node::EIOWantPoll只有一句话ev_async_send(EV_DEFAULT_UC_ &eio_want_poll_notifier);作用是唤醒eio_want_poll_notifier这个watcher。
由ev_async_init(&node::eio_want_poll_notifier, node::WantPollNotifier);可知,node::WantPollNotifier将被调用,其核心代码是:
if (eio_poll() == -1) {
//一次没取完
ev_idle_start(EV_DEFAULT_UC_ &eio_poller);
}
取出每一个eio完成队列中的元素并处理,如果一次eio_poll没处理完,就启动eio_poller这个watcher,这个watcher在前面一篇讲过就是用于一次没处理完所有完成队列的元素,于是开启等下次启动以便再次进行处理。
在处理eio的完成队列的元素时,最终调用:
# define EIO_FINISH(req) ((req)->finish) && !EIO_CANCELLED (req) ? (req)->finish (req) : 0
也就是调用了(req)->finish (req),这个finish我们回头看下:
在eio_xx系列的函数中,都会组建一个eio_req结构体,
req->finish = cb; // cb这里是After
req->data = data; // data这里是 cb_persist(cb)
所以(req)->finish (req)将调用After这个函数。
Persistent<Function> *callback = cb_unwrap(req->data);
ev_unref(EV_DEFAULT_UC);
(*callback)->Call(v8::Context::GetCurrent()->Global(), argc, argv);
上面是After的几个关键代码,第一行是取出之前设置的回调函数,也就是我们js代码给readFile设置的回调函数,第二行是事件循环的计数减1,与之前计数加1相对应,因为读文件完成了,不需要它来保证循环队列非空了,第三行就是调用回调函数。
到这里所有代码都执行完成了。然后由于事件循环已经空了,所以循环也退出了,于是整个程序都退出了。
相关文章推荐
- Nodejs源码的阅读-事件循环的建立
- 与AngularJS的约会之事件循环+watchers源码分析
- nodejs事件循环
- 安卓学习过程、怎样阅读 Android 系统源码
- Nginx源码分析-事件循环
- 安卓学习过程、怎样阅读 Android 系统源码
- redis源码阅读笔记-- 事件
- diamond源码阅读-循环探测配置信息是否变化rotateCheckConfigInfo
- nodejs 事件循环
- Spring MVC处理请求过程(Spring MVC源码阅读系列之二)
- POX控制器源码阅读(二) 组件如何监听事件/未完成
- Spring源码阅读——Bean的加载和获取过程
- ES源码阅读过程
- Android事件分发机制源码阅读
- Nodejs事件引擎libuv源码剖析之:句柄(handle)结构的设计剖析
- Yii2.0源码阅读-一次请求的完整过程
- nodejs---关于真正理解Node.js事件循环你需要了解的一切
- 带你根据源码了解View的事件触发流程,主要讲解为什么子View返回true,ViewGroup就无法接收到事件的过程
- zepto源码阅读心得与过程1
- Android VSync事件分发过程源码分析