int main(int argc, char *argv[]) {
#if defined(__POSIX__) && defined(NODE_SHARED_MODE)
// In node::PlatformInit(), we squash all signal handlers for non-shared lib
// build. In order to run test cases against shared lib build, we also need
// to do the same thing for shared lib build here, but only for SIGPIPE for
// now. If node::PlatformInit() is moved to here, then this section could be
// removed.
// socket一端clode的情况下,进程第二次write会触发操作系统给进程发送SIGPIPE信号,默认处理操作是关闭进程
// SIG_IGN作为处理函数,将忽略该信号
{
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, nullptr);
}
#endif
#if defined(__linux__)
char** envp = environ;
while (*envp++ != nullptr) {}
Elf_auxv_t* auxv = reinterpret_cast<Elf_auxv_t*>(envp);
for (; auxv->a_type != AT_NULL; auxv++) {
if (auxv->a_type == AT_SECURE) {
node::linux_at_secure = auxv->a_un.a_val;
break;
}
}
#endif
// Disable stdio buffering, it interacts poorly with printf()
// calls elsewhere in the program (e.g., any logging from V8.)
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
return node::Start(argc, argv);
}
#endif
int Start(int argc, char** argv) {
atexit([] () { uv_tty_reset_mode(); });
PlatformInit();
performance::performance_node_start = PERFORMANCE_NOW();
CHECK_GT(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
{
std::string extra_ca_certs;
if (SafeGetenv("NODE_EXTRA_CA_CERTS", &extra_ca_certs))
crypto::UseExtraCaCerts(extra_ca_certs);
}
#ifdef NODE_FIPS_MODE
// In the case of FIPS builds we should make sure
// the random source is properly initialized first.
OPENSSL_init();
#endif // NODE_FIPS_MODE
// V8 on Windows doesn't have a good source of entropy. Seed it from
// OpenSSL's pool.
V8::SetEntropySource(crypto::EntropySource);
#endif // HAVE_OPENSSL
v8_platform.Initialize(v8_thread_pool_size);
// Enable tracing when argv has --trace-events-enabled.
v8_platform.StartTracingAgent();
V8::Initialize();
performance::performance_v8_start = PERFORMANCE_NOW();
v8_initialized = true;
const int exit_code =
Start(uv_default_loop(), argc, argv, exec_argc, exec_argv);
v8_platform.StopTracingAgent();
v8_initialized = false;
V8::Dispose();
// uv_run cannot be called from the time before the beforeExit callback
// runs until the program exits unless the event loop has any referenced
// handles after beforeExit terminates. This prevents unrefed timers
// that happen to terminate during shutdown from being run unsafely.
// Since uv_run cannot be called, uv_async handles held by the platform
// will never be fully cleaned up.
v8_platform.Dispose();
delete[] exec_argv;
exec_argv = nullptr;
return exit_code;
}
1.PlatformInit
inline void PlatformInit() {
#ifdef __POSIX__
#if HAVE_INSPECTOR
// 信号集,描述信号的集合
// 每个信号占用一位(64位)
sigset_t sigmask;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGUSR1);
// 屏蔽了除SIGUSR1外的所有信号
// 一般按照sigdelset(&set, SIGALRM);pthread_sigmask(SIG_SETMASK, &set, NULL);方式使用
const int err = pthread_sigmask(SIG_SETMASK, &sigmask, nullptr);
#endif // HAVE_INSPECTOR
// Make sure file descriptors 0-2 are valid before we start logging anything.
for (int fd = STDIN_FILENO; fd <= STDERR_FILENO; fd += 1) {
struct stat ignored;
if (fstat(fd, &ignored) == 0)
continue;
// Anything but EBADF means something is seriously wrong. We don't
// have to special-case EINTR, fstat() is not interruptible.
if (errno != EBADF)
ABORT();
if (fd != open("/dev/null", O_RDWR))
ABORT();
}
#if HAVE_INSPECTOR
CHECK_EQ(err, 0);
#endif // HAVE_INSPECTOR
#ifndef NODE_SHARED_MODE
// Restore signal dispositions, the parent process may have changed them.
struct sigaction act;
memset(&act, 0, sizeof(act));
// The hard-coded upper limit is because NSIG is not very reliable; on Linux,
// it evaluates to 32, 34 or 64, depending on whether RT signals are enabled.
// Counting up to SIGRTMIN doesn't work for the same reason.
// 跟main中一样,忽略SIGPIPE信号
// sigaction与pthread_sigmask区别在于线程中调用signal或者sigaction等函数会改变所有线程中的信号处理函数
for (unsigned nr = 1; nr < kMaxSignal; nr += 1) {
if (nr == SIGKILL || nr == SIGSTOP)
continue;
act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL;
CHECK_EQ(0, sigaction(nr, &act, nullptr));
}
#endif // !NODE_SHARED_MODE
RegisterSignalHandler(SIGINT, SignalExit, true);
RegisterSignalHandler(SIGTERM, SignalExit, true);
// Raise the open file descriptor limit.
// 提高进程打开文件数量
struct rlimit lim;
if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) {
// Do a binary search for the limit.
rlim_t min = lim.rlim_cur;
rlim_t max = 1 << 20;
// But if there's a defined upper bound, don't search, just set it.
if (lim.rlim_max != RLIM_INFINITY) {
min = lim.rlim_max;
max = lim.rlim_max;
}
do {
lim.rlim_cur = min + (max - min) / 2;
if (setrlimit(RLIMIT_NOFILE, &lim)) {
max = lim.rlim_cur;
} else {
min = lim.rlim_cur;
}
} while (min + 1 < max);
}
#endif // __POSIX__
#ifdef _WIN32
for (int fd = 0; fd <= 2; ++fd) {
auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
if (handle == INVALID_HANDLE_VALUE ||
GetFileType(handle) == FILE_TYPE_UNKNOWN) {
// Ignore _close result. If it fails or not depends on used Windows
// version. We will just check _open result.
_close(fd);
if (fd != _open("nul", _O_RDWR))
ABORT();
}
}
#endif // _WIN32
}
void Init(int* argc,
const char** argv,
int* exec_argc,
const char*** exec_argv) {
// Initialize prog_start_time to get relative uptime.
prog_start_time = static_cast<double>(uv_now(uv_default_loop()));
// Register built-in modules
// 注册内置模块
RegisterBuiltinModules();
// Make inherited handles noninheritable.
// disable掉继承过来的handle
uv_disable_stdio_inheritance();
#if defined(NODE_V8_OPTIONS)
// Should come before the call to V8::SetFlagsFromCommandLine()
// so the user can disable a flag --foo at run-time by passing
// --no_foo from the command line.
// 设置v8虚拟机启动的命令行标志
V8::SetFlagsFromString(NODE_V8_OPTIONS, sizeof(NODE_V8_OPTIONS) - 1);
#endif
// 从环境变量中获取各种参数
{
std::string text;
config_pending_deprecation =
SafeGetenv("NODE_PENDING_DEPRECATION", &text) && text[0] == '1';
}
// Allow for environment set preserving symlinks.
{
std::string text;
config_preserve_symlinks =
SafeGetenv("NODE_PRESERVE_SYMLINKS", &text) && text[0] == '1';
}
if (config_warning_file.empty())
SafeGetenv("NODE_REDIRECT_WARNINGS", &config_warning_file);
#if HAVE_OPENSSL
if (openssl_config.empty())
SafeGetenv("OPENSSL_CONF", &openssl_config);
#endif
#if !defined(NODE_WITHOUT_NODE_OPTIONS)
std::string node_options;
if (SafeGetenv("NODE_OPTIONS", &node_options)) {
// Smallest tokens are 2-chars (a not space and a space), plus 2 extra
// pointers, for the prepended executable name, and appended NULL pointer.
size_t max_len = 2 + (node_options.length() + 1) / 2;
const char** argv_from_env = new const char*[max_len];
int argc_from_env = 0;
// [0] is expected to be the program name, fill it in from the real argv.
argv_from_env[argc_from_env++] = argv[0];
char* cstr = strdup(node_options.c_str());
char* initptr = cstr;
char* token;
while ((token = strtok(initptr, " "))) { // NOLINT(runtime/threadsafe_fn)
initptr = nullptr;
argv_from_env[argc_from_env++] = token;
}
argv_from_env[argc_from_env] = nullptr;
int exec_argc_;
const char** exec_argv_ = nullptr;
ProcessArgv(&argc_from_env, argv_from_env, &exec_argc_, &exec_argv_, true);
delete[] exec_argv_;
delete[] argv_from_env;
free(cstr);
}
#endif
// 获取node和v8的参数
ProcessArgv(argc, argv, exec_argc, exec_argv);
#if defined(NODE_HAVE_I18N_SUPPORT)
// If the parameter isn't given, use the env variable.
if (icu_data_dir.empty())
SafeGetenv("NODE_ICU_DATA", &icu_data_dir);
// Initialize ICU.
// If icu_data_dir is empty here, it will load the 'minimal' data.
if (!i18n::InitializeICUDirectory(icu_data_dir)) {
fprintf(stderr,
"%s: could not initialize ICU "
"(check NODE_ICU_DATA or --icu-data-dir parameters)\n",
argv[0]);
exit(9);
}
#endif
// Needed for access to V8 intrinsics. Disabled again during bootstrapping,
// see lib/internal/bootstrap/node.js.
// 允许用户代码去调用v8的内置函数
// 调用方式以%开头,谋面大家会看见
const char allow_natives_syntax[] = "--allow_natives_syntax";
V8::SetFlagsFromString(allow_natives_syntax,
sizeof(allow_natives_syntax) - 1);
// We should set node_is_initialized here instead of in node::Start,
// otherwise embedders using node::Init to initialize everything will not be
// able to set it and native modules will not load for them.
node_is_initialized = true;
}
extern "C" void node_module_register(void* m) {
struct node_module* mp = reinterpret_cast<struct node_module*>(m);
if (mp->nm_flags & NM_F_BUILTIN) {
mp->nm_link = modlist_builtin;
modlist_builtin = mp;
} else if (mp->nm_flags & NM_F_INTERNAL) {
mp->nm_link = modlist_internal;
modlist_internal = mp;
} else if (!node_is_initialized) {
// "Linked" modules are included as part of the node project.
// Like builtins they are registered *before* node::Init runs.
mp->nm_flags = NM_F_LINKED;
mp->nm_link = modlist_linked;
modlist_linked = mp;
} else {
modpending = mp;
}
}
其实就是把上面定义的module加到了modlist_builtin链表里。
uv_disable_stdio_inheritance
void uv_disable_stdio_inheritance(void) {
int fd;
/* Set the CLOEXEC flag on all open descriptors. Unconditionally try the
* first 16 file descriptors. After that, bail out after the first error.
*/
for (fd = 0; ; fd++)
if (uv__cloexec(fd, 1) && fd > 15)
break;
}
if (state == ONCE_STATE_UNINITIALIZED) {
// We are the first thread to call this function, so we have to call the
// function.
init_func();
Release_Store(once, ONCE_STATE_DONE);
void LoadEnvironment(Environment* env) {
HandleScope handle_scope(env->isolate());
TryCatch try_catch(env->isolate());
// 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);
// The bootstrapper scripts are lib/internal/bootstrap/loaders.js and
// lib/internal/bootstrap/node.js, each included as a static C string
// defined in node_javascript.h, generated in node_javascript.cc by
// node_js2c.
Local<String> loaders_name =
FIXED_ONE_BYTE_STRING(env->isolate(), "internal/bootstrap/loaders.js");
// LoadersBootstrapperSource从node_js2c中获取loaders.js的ascII源码
Local<Function> loaders_bootstrapper =
GetBootstrapper(env, LoadersBootstrapperSource(env), loaders_name);
Local<String> node_name =
FIXED_ONE_BYTE_STRING(env->isolate(), "internal/bootstrap/node.js");
Local<Function> node_bootstrapper =
GetBootstrapper(env, NodeBootstrapperSource(env), node_name);
// Add a reference to the global object
Local<Object> global = env->context()->Global();
#if defined HAVE_DTRACE || defined HAVE_ETW
InitDTrace(env, global);
#endif
#if defined HAVE_PERFCTR
InitPerfCounters(env, global);
#endif
// Enable handling of uncaught exceptions
// (FatalException(), break on uncaught exception in debugger)
//
// This is not strictly necessary since it's almost impossible
// to attach the debugger fast enough to break on exception
// thrown during process startup.
try_catch.SetVerbose(true);
env->SetMethod(env->process_object(), "_rawDebug", RawDebug);
// Expose the global object as a property on itself
// (Allows you to set stuff on `global` from anywhere in JavaScript.)
global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global);
// Create binding loaders
v8::Local<v8::Function> get_binding_fn =
env->NewFunctionTemplate(GetBinding)->GetFunction(env->context())
.ToLocalChecked();
v8::Local<v8::Function> get_linked_binding_fn =
env->NewFunctionTemplate(GetLinkedBinding)->GetFunction(env->context())
.ToLocalChecked();
v8::Local<v8::Function> get_internal_binding_fn =
env->NewFunctionTemplate(GetInternalBinding)->GetFunction(env->context())
.ToLocalChecked();
Local<Value> loaders_bootstrapper_args[] = {
env->process_object(),
get_binding_fn,
get_linked_binding_fn,
get_internal_binding_fn
};
// Bootstrap internal loaders
Local<Value> bootstrapped_loaders;
if (!ExecuteBootstrapper(env, loaders_bootstrapper,
arraysize(loaders_bootstrapper_args),
loaders_bootstrapper_args,
&bootstrapped_loaders)) {
return;
}
// Bootstrap Node.js
Local<Value> bootstrapped_node;
// bootstrapped_loaders中是loaders_bootstrapper执行返回的{ internalBinding, NativeModule }
Local<Value> node_bootstrapper_args[] = {
env->process_object(),
bootstrapped_loaders
};
if (!ExecuteBootstrapper(env, node_bootstrapper,
arraysize(node_bootstrapper_args),
node_bootstrapper_args,
&bootstrapped_node)) {
return;
}
}
v8_platform.StopTracingAgent();
v8_initialized = false;
V8::Dispose();
// uv_run cannot be called from the time before the beforeExit callback
// runs until the program exits unless the event loop has any referenced
// handles after beforeExit terminates. This prevents unrefed timers
// that happen to terminate during shutdown from being run unsafely.
// Since uv_run cannot be called, uv_async handles held by the platform
// will never be fully cleaned up.
v8_platform.Dispose();
delete[] exec_argv;
exec_argv = nullptr;
return exit_code;
本文从node入口出发,一步一步的阅读源码,直到运行结束。
node入口
node的入口是node/src/node_main.cc文件,main函数代码如下:
这里主要做了三件事:
node::Start执行流程
node::Start代码如下:
1.PlatformInit
主要以下几件事:
下面我将挑一些重点的点来讲解。
pthread_sigmask sigaction
pthread_sigmask用来设置线程的信号屏蔽集,注意这里是线程自己的;sigaction用来安装信号的处理函数,这里操作的进程的,进程中所有线程会共享这个出个处理函数。也就是说线程可以有自己的信号屏蔽集,但是处理函数是进程中所有线程共享的。
提高进程打开文件描述符数量
依据上述代码,我们发现其使用的是setrlimit方法,当rlimit中有max属性时,直接setrlimit;没有max属性时,从lim.rlim_cur到2的19次方之间指数递增。
2.uv_setup_args(argc, argv)
其实就是复制一份argv,返回new_argv,给process.title用。
3.Init
Init方法主要做了以下几件事:
还是挑几个重点讲解一下
RegisterBuiltinModules
注册内置模块,也就是src里的.cc文件。
RegisterBuiltinModules做了两件事:
NODE_BUILTIN_MODULES也是一个宏定义,定义如下:
NODE_BUILTIN_STANDARD_MODULES定义如下:
也就是注册每个模块,其实调用了register##modname()。
_register_##modname()
定义如下:node_module_register定义在src/node.cc中,源码如下:
其实就是把上面定义的module加到了modlist_builtin链表里。
uv_disable_stdio_inheritance
其实就是利用了cloexec,在子进程执行时,关闭相应文件描述符。这里多说几句,为什么要这样呢?原因在于当fork子进程时,会将父进程文件描述符及堆栈信息复制到子进程,但当子进程执行时,原有执行栈被重置,原有的文件描述符对应变量也就不见了,所以将无法关闭对应文件描述符。cloexec就是为了解决这个问题的,在子进程执行时,关闭文件描述符。
--allow_natives_syntax
V8通过设置--allow_natives_syntax来允许用户的代码调用v8的内置函数,但调用时要以%开头。
4.判断OPENSSL
主要判断是否需要openssl,如果需要从NODE_EXTRA_CA_CERTS中取证书。
5.v8_platform.Initialize
主要对V8做了线程池容积的初始化。
6.V8::Initialize();
这里是v8的初始化,定义再src/deps/v8/src/v8.cc中,
InitializeOncePerProcess做了什么呢?
CallOnce
CallOnce顾名思义就是只调用一次,其通过判断init_once是否为ONCE_STATE_DONE来判断是否曾经调用过。
其中Acquire_Load为原子性的获取once的值,CallOnceImpl则再其中修改once值,并且执行init_func。
下面我们看下Acquire_Load的定义:
__atomic_load_n即为原子性的加载ptr指针所指向的内存所存储的变量。
CallOnceImpl代码如下:
主要做了两件事:
InitializeOncePerProcessImpl
这里主要做了两件事:
Isolate::InitializeOncePerProcess
主要做了三件事:
7.Start(uv_default_loop(), argc, argv, exec_argc, exec_argv)
这里主要做了如下几件事:
array_buffer_allocator
ArrayBufferAllocator::Allocate其实就是调用了realloc,在原来基础上将pointer所指向的内存大小增加到full_size。
Isolate添加监听回调
以AddMessageListener为例,其实最终调用的是Isolate::AddMessageListenerWithErrorLevel,代码如下:
其实就是给堆内存增加了监听,在message_listeners中加入对应listener。
8.Start(isolate, &isolate_data, argc, argv, exec_argc, exec_argv)
这里主要做了如下几件事:
LoadEnvironment()
这里主要做了以下几件事:
node_js2c
下面便是node_javascript.cc中的一部分:
我们可以看到两个数组和两个struct,其中raw_internal_bootstrap_loaders_key和raw_internal_bootstrap_loaders_value分别记录bootstrap_loaders的key和value(文件内容),两个结构体internal_bootstrap_loaders_key和internal_bootstrap_loaders_value均有方法ToStringChecked,而ToStringChecked其实会去找data()方法,也就是说internal_bootstrap_loaders_value.ToStringChecked()便会返回对应的ascII码。
node_javascript.cc又是如何产生的呢?
我看看到在node.gyp中定义了action,其实就是调用了python tools/js2c.py,这个后面文章再来介绍吧,这里先简单提一下。
GetBinding
getBinding又是干什么的呢?
我们不难发现,逻辑上有三个分叉:
get_builtin_module又是怎么做的呢?
很简单,就是从modlist_builtin里面遍历,上述的Init函数中调用RegisterBuiltinModules将所有的内置模块加入到链表modlist_builtin中。
InitModule其实就是执行了module::Initialize(),以async_wrap为例:
上述async_wrap中可以看到其实就是在exports上挂载各种方法,然后初始化。
DefineJavaScript干了什么呢?
我们看到其实就是将node_javascript.cc中的模块以key/value的形式挂载到exports,这里可以注意下上面提到的ToStringChecked。
ExecuteBootstrapper
这里就是执行internal/loader.js和internal/node.js,这里先简单讲下,后面会做详细介绍。其最主要的逻辑如下:
检测语法,然后执行。
8.资源释放
这里把v8_platform、exec_argv等资源释放,此次运行结束。
总结
本次主要沿着node::Start函数的逻辑,将运行一个node程序完整的流程呈现给大家,后面会对其中涉及的一些点以及一些模块进行分别介绍。