聊聊No.js---基于V8和io_uring的JS运行时

前言:阅读Node.js的源码已经有一段时间了,最近也看了一下新的JS运行时Just的一些实现,就产生了自己写一个JS运行时的想法,虽然几个月前就基于V8写了一个简单的JS运行时,但功能比较简单,这次废弃了之前的代码,重新写了一遍,写这个JS运行时的目的最主要是为了学习,事实也证明,写一个JS运行时的确可以学到很多东西。本文介绍运行时No.js的一些设计和实现,取名No.js一来是受Node.js的影响,二来是为了说明不仅仅是JS,也就是利用V8拓展了JS的功能,同时,前端开发者要学习的知识也不仅仅是JS了。

1 为什么选io_uring

io_uring是Linux下新一代的高性能异步IO框架,也是No.js的核心。在No.js中,io_uring用于实现事件循环。为什么不选用epoll呢?因为epoll不支持文件IO,如果选用epoll,还需要自己实现一个线程池,还需要实现线程和主线程的通信,以及线程池任务和事件循环的融合,No.js希望把事件变得纯粹,简单。而io_uring是支持异步文件IO的,并且io_uring是真正的异步IO框架,支持的功能也非常丰富,比如在epoll里我们监听一个socket后,需要把socket fd注册到epoll中,等待有连接时执行回调,然后调用accept获取新的fd,而io_uring直接就帮我们获取新的fd,io_uring通知我们的时候,我们就已经拿到新的fd了,epoll时代,epoll通知我们可以做什么事情了,然后我们自己去做,io_uring时代,io_uring通知我们什么事情完成了。

2 No.js框架的设计

No.js目前的实现比较清晰简单,所有的功能都通过c和c++实现,然后通过V8暴露给JS实现。No.cc是初始化的入口,core目录是所有功能实现的地方,core下面按照模块功能划分。下面我们看看整体的框架实现。

 
 
 
  1. int main(int argc, char* argv[]) { 
  2.   // ... 
  3.   Isolate* isolate = Isolate::New(create_params); 
  4.   { 
  5.     Isolate::Scope isolate_scope(isolate); 
  6.     HandleScope handle_scope(isolate); 
  7.     // 创建全局对象 
  8.     Local global = ObjectTemplate::New(isolate); 
  9.     // 创建执行上下文 
  10.     Local context = Context::New(isolate, nullptr, global); 
  11.     Environment * env = new Environment(context); 
  12.     Context::Scope context_scope(context); 
  13.     // 创建No,核心对象 
  14.     Local No = Object::New(isolate); 
  15.     // 注册c、c++模块 
  16.     register_builtins(isolate, No); 
  17.     // 获取全局对象 
  18.     Local globalInstance = context->Global(); 
  19.     // 设置全局属性 
  20.     globalInstance->Set(context, String::NewFromUtf8Literal(isolate, "No",  
  21.     NewStringType::kNormal), No); 
  22.     // 设置全局属性global指向全局对象 
  23.     globalInstance->Set(context, String::NewFromUtf8Literal(isolate,  
  24.       "global",  
  25.       NewStringType::kNormal), globalInstance).Check(); 
  26.     { 
  27.       // 打开文件 
  28.       int fd = open(argv[1], O_RDONLY); 
  29.       struct stat info; 
  30.       // 取得文件信息 
  31.       fstat(fd, &info); 
  32.       // 分配内存保存文件内容 
  33.       char *ptr = (char *)malloc(info.st_size + 1); 
  34.       read(fd, (void *)ptr, info.st_size); 
  35.       // 要执行的js代码 
  36.       Local source = String::NewFromUtf8(isolate, ptr, 
  37.                           NewStringType::kNormal, 
  38.                           info.st_size).ToLocalChecked(); 
  39.  
  40.       // 编译 
  41.       Local