You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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程序完整的流程呈现给大家,后面会对其中涉及的一些点以及一些模块进行分别介绍。
The text was updated successfully, but these errors were encountered: