本文测试环境是win7 32位。 先下载nodejs源代码以备查看之用。同时安装好node-inspector环境并配置好,命令: npm install -g node-inspector
写一段测试代码如下:
var childProcess = require('child_process');
var n = childProcess. fork( './son.js');
var n = childProcess. fork( './son.js');
保存为parent.js 然后调试运行 node --debug-brk parent.js 并且打开GoogleChrome进行调试
其中第一行 "(function (exports, require, module, __filename, __dirname) {" 是nodejs内核对我们编写的js代码的包装,将我们写的js代码包装到一个函数内,相关详细可自行查阅资料。
开始跟踪fork方法,跟踪堆栈如下:
首先fork将进入exports.fork。 我们打开源代码查看,源码位于child_process.js中
exports.fork = function (modulePath /*, args, options*/) {
// Get options and args arguments.
var options, args, execArgv;
if ( util. isArray( arguments[ 1])) {
args = arguments [1 ];
options = util ._extend ({}, arguments [2 ]);
} else if (arguments [1 ] && typeof arguments[1] !== 'object' ) {
throw new TypeError( 'Incorrect value of args option');
} else {
args = [];
options = util ._extend ({}, arguments [1 ]);
}
// Prepare arguments for fork:
execArgv = options .execArgv || process.execArgv;
args = execArgv. concat([modulePath], args);
// Leave stdin open for the IPC channel. stdout and stderr should be the
// same as the parent's if silent isn't set.
options. stdio = options. silent ? [ 'pipe', 'pipe', 'pipe', 'ipc'] :
[0, 1, 2, 'ipc' ];
options. execPath = options. execPath || process. execPath;
return spawn(options.execPath, args, options);
};
// Get options and args arguments.
var options, args, execArgv;
if ( util. isArray( arguments[ 1])) {
args = arguments [1 ];
options = util ._extend ({}, arguments [2 ]);
} else if (arguments [1 ] && typeof arguments[1] !== 'object' ) {
throw new TypeError( 'Incorrect value of args option');
} else {
args = [];
options = util ._extend ({}, arguments [1 ]);
}
// Prepare arguments for fork:
execArgv = options .execArgv || process.execArgv;
args = execArgv. concat([modulePath], args);
// Leave stdin open for the IPC channel. stdout and stderr should be the
// same as the parent's if silent isn't set.
options. stdio = options. silent ? [ 'pipe', 'pipe', 'pipe', 'ipc'] :
[0, 1, 2, 'ipc' ];
options. execPath = options. execPath || process. execPath;
return spawn(options.execPath, args, options);
};
流程将从exports.fork=>spawn中。
打开spawn函数,代码仍然位于child_process.js中:
var spawn = exports. spawn = function( /*file, args, options*/) {
var opts = normalizeSpawnArguments.apply( null, arguments);
var options = opts.options;
var child = new ChildProcess();
child. spawn({
file: opts.file,
args: opts.args,
cwd: options .cwd ,
windowsVerbatimArguments : !!options .windowsVerbatimArguments ,
detached: !!options .detached ,
envPairs: opts.envPairs ,
stdio: options .stdio ,
uid: options .uid ,
gid: options .gid
});
return child;
};
var opts = normalizeSpawnArguments.apply( null, arguments);
var options = opts.options;
var child = new ChildProcess();
child. spawn({
file: opts.file,
args: opts.args,
cwd: options .cwd ,
windowsVerbatimArguments : !!options .windowsVerbatimArguments ,
detached: !!options .detached ,
envPairs: opts.envPairs ,
stdio: options .stdio ,
uid: options .uid ,
gid: options .gid
});
return child;
};
流程将从exports.fork=>spawn=>ChildProcess. spawn中。
接下来看 ChildProcess. spawn函数,代码仍然位于child_process.js中,此函数比较长 这里只贴出关键代码:
var Process = process. binding( 'process_wrap').Process;
function ChildProcess() {
.......
this. _handle = new Process();
.......
this. _handle = new Process();
.......
}
ChildProcess. prototype. spawn = function(options) {
.......
var err = this._handle.spawn(options);
.......
var err = this._handle.spawn(options);
.......
}
流程将进入this. _handle. spawn中,此函数已经属于原生函数了,原理可自行了解process. binding的实现。
代码位于src\process_wrap.cc中
NODE_SET_PROTOTYPE_METHOD(constructor, "spawn", Spawn);
static void Spawn(const FunctionCallbackInfo<Value>& args) {
.......
int err = uv_spawn(env->event_loop(), &wrap->process_, &options);
......
}
流程将进入uv_spawn函数,此函数位于deps\uv\src\win\process.c中
int uv_spawn(uv_loop_t* loop,
uv_process_t* process,
const uv_process_options_t* options) {
.........
if (!CreateProcessW(application_path,
arguments,
NULL,
NULL,
1,
process_flags,
env,
cwd,
&startup,
&info)) {
/* CreateProcessW failed. */
err = GetLastError();
goto done;
}
.........
}
这下子终于看到windows下创建进程的api函数CreateProcessW了。
总结:
exports.fork(js层面)=>spawn(js层面)=>ChildProcess. spawn(js层面)=>Spawn(原生层面)=>uv_spawn(原生层面)=>CreateProcessW(操作系统api)