编译nodejs及其源码研究

本文将从 源码 研究nodejs 的原理、本质,探讨nodejs的应用场景,以及高性能开发实践指南。

目录:

第一节:编译node.js 

第二节:源码分析


进入主题:以下是在win7 64 下进行,其他平台编译 ,请参见官网。

第一节:编译node.js,过程很简单

1、下载源码。  git clone https://github.com/joyent/node

如果没有安装git客户端,可以在打开https://github.com/joyent/node  点击 Download ZIP,进行下载

2、安装 Python 2.6 or 2.7 和 Visual Studio 2010 or 2012,我这里是 Python 2.7.8  和 Visual Studio 2012

3、进入node目录  执行 vcbuild release


大概 10多分钟 就可以编译成功,在Release目录下会生成node.exe。下面是我编译成功的图。



 第二节:源码分析

我们在IDE 中 打开刚才的node目录,方便我们看源码。我这里是Idea查看。


benchmark : 一些nodejs 性能测试 代码

build:编译nodejs 生成目录

Debug:编译nodejs 生成目录

Release:编译nodejs 生成目录

deps:nodejs依赖 的工具包,包括 v8、http_parser、opensslzlib、zlib、uv。。。

doc:文档

lib:包含JavaScript源码

src:包含C++源码

test:测试代码

tools:编译时用到的工具

这里我们只需关注 src 和lib 文件夹。


1、从node.cc 文件 看 node进程 启动过程的

int Start(int argc, char** argv) {
  const char* replaceInvalid = getenv("NODE_INVALID_UTF8");

  if (replaceInvalid == NULL)
    WRITE_UTF8_FLAGS |= String::REPLACE_INVALID_UTF8;

#if !defined(_WIN32)
  // Try hard not to lose SIGUSR1 signals during the bootstrap process.
  InstallEarlyDebugSignalHandler();
#endif

  assert(argc > 0);

  // Hack around with the argv pointer. Used for process.title = "blah".
  argv = uv_setup_args(argc, argv);

  // This needs to run *before* V8::Initialize().  The const_cast is not
  // optional, in case you're wondering.
  int exec_argc;
  const char** exec_argv;
  Init(&argc, const_cast<const char**>(argv), &exec_argc, &exec_argv);

#if HAVE_OPENSSL
  // V8 on Windows doesn't have a good source of entropy. Seed it from
  // OpenSSL's pool.
  V8::SetEntropySource(crypto::EntropySource);
#endif

  int code;
  V8::Initialize();
  {
    Locker locker(node_isolate);
    Isolate::Scope isolate_scope(node_isolate);
    HandleScope handle_scope(node_isolate);
    Local<Context> context = Context::New(node_isolate);
    Environment* env = CreateEnvironment(
        node_isolate, context, argc, argv, exec_argc, exec_argv);
    // Assign env to the debugger's context
    if (debugger_running) {
      HandleScope scope(env->isolate());
      env->AssignToContext(v8::Debug::GetDebugContext());
    }
    // This Context::Scope is here so EnableDebug() can look up the current
    // environment with Environment::GetCurrent().
    // TODO(bnoordhuis) Reorder the debugger initialization logic so it can
    // be removed.
    {
      Context::Scope context_scope(env->context());
      bool more;
      do {
        more = uv_run(env->event_loop(), UV_RUN_ONCE);
        if (more == false) {
          EmitBeforeExit(env);

          // Emit `beforeExit` if the loop became alive either after emitting
          // event, or after running some callbacks.
          more = uv_loop_alive(env->event_loop());
          if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0)
            more = true;
        }
      } while (more == true);
      code = EmitExit(env);
      RunAtExit(env);
    }
    env->Dispose();
    env = NULL;
  }

  CHECK_NE(node_isolate, NULL);
  node_isolate->Dispose();
  node_isolate = NULL;
  V8::Dispose();

  delete[] exec_argv;
  exec_argv = NULL;

  return code;
}

Environment* CreateEnvironment(Isolate* isolate,
                               Handle<Context> context,
                               int argc,
                               const char* const* argv,
                               int exec_argc,
                               const char* const* exec_argv) {
  HandleScope handle_scope(isolate);

  Context::Scope context_scope(context);
  Environment* env = Environment::New(context);

  uv_check_init(env->event_loop(), env->immediate_check_handle());
  uv_unref(
      reinterpret_cast<uv_handle_t*>(env->immediate_check_handle()));
  uv_idle_init(env->event_loop(), env->immediate_idle_handle());

  // Inform V8's CPU profiler when we're idle.  The profiler is sampling-based
  // but not all samples are created equal; mark the wall clock time spent in
  // epoll_wait() and friends so profiling tools can filter it out.  The samples
  // still end up in v8.log but with state=IDLE rather than state=EXTERNAL.
  // TODO(bnoordhuis) Depends on a libuv implementation detail that we should
  // probably fortify in the API contract, namely that the last started prepare
  // or check watcher runs first.  It's not 100% foolproof; if an add-on starts
  // a prepare or check watcher after us, any samples attributed to its callback
  // will be recorded with state=IDLE.
  uv_prepare_init(env->event_loop(), env->idle_prepare_handle());
  uv_check_init(env->event_loop(), env->idle_check_handle());
  uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_prepare_handle()));
  uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_check_handle()));

  if (v8_is_profiling) {
    StartProfilerIdleNotifier(env);
  }

  Local<FunctionTemplate> process_template = FunctionTemplate::New(isolate);
  process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "process"));

  Local<Object> process_object = process_template->GetFunction()->NewInstance();
  env->set_process_object(process_object);

  SetupProcessObject(env, argc, argv, exec_argc, exec_argv);
  Load(env);

  return env;
}

void SetupProcessObject(Environment* env,
                        int argc,
                        const char* const* argv,
                        int exec_argc,
                        const char* const* exec_argv) {
  HandleScope scope(env->isolate());

  Local<Object> process = env->process_object();

  process->SetAccessor(env->title_string(),
                       ProcessTitleGetter,
                       ProcessTitleSetter);

  // process.version
  READONLY_PROPERTY(process,
                    "version",
                    FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION));

  // process.moduleLoadList
  READONLY_PROPERTY(process,
                    "moduleLoadList",
                    env->module_load_list_array());

  // process.versions
  Local<Object> versions = Object::New(env->isolate());
  READONLY_PROPERTY(process, "versions", versions);

  const char http_parser_version[] = NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR)
                                     "."
                                     NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR);
  READONLY_PROPERTY(versions,
                    "http_parser",
                    FIXED_ONE_BYTE_STRING(env->isolate(), http_parser_version));

  // +1 to get rid of the leading 'v'
  READONLY_PROPERTY(versions,
                    "node",
                    OneByteString(env->isolate(), NODE_VERSION + 1));
  READONLY_PROPERTY(versions,
                    "v8",
                    OneByteString(env->isolate(), V8::GetVersion()));
  READONLY_PROPERTY(versions,
                    "uv",
                    OneByteString(env->isolate(), uv_version_string()));
  READONLY_PROPERTY(versions,
                    "zlib",
                    FIXED_ONE_BYTE_STRING(env->isolate(), ZLIB_VERSION));

  const char node_modules_version[] = NODE_STRINGIFY(NODE_MODULE_VERSION);
  READONLY_PROPERTY(
      versions,
      "modules",
      FIXED_ONE_BYTE_STRING(env->isolate(), node_modules_version));

void Load(Environment* env) {
  HandleScope handle_scope(env->isolate());

  // Compile, execute the src/node.js file. (Which was included as static C
  // string in node_natives.h. 'natve_node' is the string containing that
  // source code.)

  // The node.js file returns a function 'f'
  atexit(AtExit);

  TryCatch try_catch;

  // Disable verbose mode to stop FatalException() handler from trying
  // to handle the exception. Errors this early in the start-up phase
  // are not safe to ignore.
  try_catch.SetVerbose(false);

  Local<String> script_name = FIXED_ONE_BYTE_STRING(env->isolate(), "node.js");
  Local<Value> f_value = ExecuteString(env, MainSource(env), script_name);
  if (try_catch.HasCaught())  {
    ReportException(env, try_catch);
    exit(10);
  }
  assert(f_value->IsFunction());
  Local<Function> f = Local<Function>::Cast(f_value);

  // Now we call 'f' with the 'process' variable that we've built up with
  // all our bindings. Inside node.js we'll take care of assigning things to
  // their places.

  // We start the process this way in order to be more modular. Developers
  // who do not like how 'src/node.js' setups the module system but do like
  // Node's I/O bindings may want to replace 'f' with their own function.

  // Add a reference to the global object
  Local<Object> global = env->context()->Global();


大致的过程是这样的 :

加载 V8 、OpenSSL ... 

创建 Environment 环境

设置 Process 进程对象

执行 node.js 文件


2、从 node.js 文件 看 global 配置过程,吐槽一下,nodejs的源码写的太搓了,C系语言出生的风格?

这个文件大致是 是配置 全局变量、配置process、定义模块对象。


后面将深入 讲解 node.js 这个文件 、以及结合 src 的C++类,与lib 下的 js代码 讲解nodejs。

我们可以得出一个结论: nodejs  = node API + V8;




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。