Node.js 进程 Node TSL.js Node.js TTY Node.js 进程 本节介绍Node.js的process(过程)对象,它提供有关当前Node.js过程的信息和控制。 process是全局对象,能够在任意位置对其进行访问,而无需使用require(),是EventEmitter的实例。 退出状态码 当不需要处理新的异步的操作时,Node正常情况下退出时会返回状态码0。下面提供了一些表示其他状态的状态码: 1未捕获的致命异常-Uncaught Fatal Exception - 有未捕获异常,并且没有被域或 uncaughtException 处理函数处理。 2- Unused (保留) 3JavaScript解析错误-Internal JavaScript Parse Error - JavaScript的源码启动 Node 进程时引起解析错误。非常罕见,仅会在开发Node时才会有。 4JavaScript评估失败-Internal JavaScript Evaluation Failure - JavaScript的源码启动Node进程,评估时返回函数失败。非常罕见,仅会在开发 Node时才会有。 5致命错误-Fatal Error - V8里致命的不可恢复的错误。通常会打印到stderr,内容为:FATAL ERROR 6Non-function异常处理-Non-function Internal Exception Handler - 未捕获异常,内部异常处理函数不知为何设置为on-function,并且不能被调用。 7异常处理函数运行时失败-Internal Exception Handler Run-Time Failure - 未捕获的异常, 并且异常处理函数处理时自己抛出了异常。例如,如果 process.on('uncaughtException')或domain.on('error')抛出了异常。 8- Unused保留。 之前版本的Node, 状态码8有时表示未捕获异常。 9- 参数非法-Invalid Argument - 可能是给了未知的参数,或者给的参数没有值。 10运行时失败-Internal JavaScript Run-Time Failure - JavaScript的源码启动 Node 进程时抛出错误,非常罕见,仅会在开发 Node 时才会有。 11无效的Debug参数-Invalid Debug Argument - 设置了参数--debug或--debug-brk,但是选择了错误端口。 >12信号退出-Signal Exits - 如果Node接收到致命信号,比如SIGKILL或SIGHUP,那么退出代码就是128加信号代码。这是标准的Unix做法,退出信号代码放在高位。 事件: 'exit' 当进程准备退出时触发。此时已经没有办法阻止从事件循环中推出。因此,你必须在处理函数中执行同步操作。这是一个在固定事件检查模块状态(比如单元测试)的好时机。回调函数有一个参数,它是进程的退出代码。 监听exit事件的例子: process.on('exit', function(code) { // do *NOT* do this setTimeout(function() { console.log('This will not run'); }, 0); console.log('About to exit with code:', code); }); 事件: 'beforeExit' 当node清空事件循环,并且没有其他安排时触发这个事件。通常来说,当没有进程安排时node退出,但是'beforeExit'的监听器可以异步调用,这样node就会继续执行。 'beforeExit'并不是明确退出的条件,process.exit()或异常捕获才是,所以不要把它当做'exit'事件。除非你想安排更多的工作。 事件: 'uncaughtException' 当一个异常冒泡回到事件循环,触发这个事件。如果给异常添加了监视器,默认的操作(打印堆栈跟踪信息并退出)就不会发生。 监听uncaughtException的例子: process.on('uncaughtException', function(err) { console.log('Caught exception: ' + err); }); setTimeout(function() { console.log('This will still run.'); }, 500); // Intentionally cause an exception, but don't catch it. nonexistentFunc(); console.log('This will not run.'); 注意:uncaughtException是非常简略的异常处理机制。 尽量不要使用它,而应该用domains 。如果你用了,每次未处理异常后,重启你的程序。 不要使用node.js里诸如On Error Resume Next这样操作。每个未处理的异常意味着你的程序,和你的node.js扩展程序,一个未知状态。盲目的恢复意味着任何事情都可能发生 你在升级的系统时拉掉了电源线,然后恢复了。可能10次里有9次没有问题,但是第10次,你的系统可能就会挂掉。 Signal 事件 当进程接收到信号时就触发。信号列表详见标准的POSIX信号名,如SIGINT、SIGUSR1等。 监听SIGINT的例子: // Start reading from stdin so we don't exit. process.stdin.resume(); process.on('SIGINT', function() { console.log('Got SIGINT. Press Control-D to exit.'); }); 在大多数终端程序里,发送SIGINT信号的简单方法是按下信号Control-C。 注意: SIGUSR1node.js 接收这个信号开启调试模式。可以安装一个监听器,但开始时不会中断调试。 SIGTERM和SIGINT在非Windows系统里,有默认的处理函数,退出(伴随退出代码128 + 信号码)前,重置退出模式。如果这些信号有监视器,默认的行为将会被移除。 SIGPIPE默认情况下忽略,可以加监听器。 SIGHUP当Windowns控制台关闭的时候生成,其他平台的类似条件,参见signal(7)。可以添加监听者,Windows平台上10秒后会无条件退出。在非Windows平台上,SIGHUP的默认操作是终止node,但是一旦添加了监听器,默认动作将会被移除。 SIGTERMWindows不支持,可以被监听。 SIGINT所有的终端都支持,通常由CTRL+C生成(可能需要配置)。当终端原始模式启用后不会再生成。 SIGBREAKWindows里,按下CTRL+BREAK会发送。非Windows平台,可以被监听,但是不能发送或生成。 SIGWINCH- 当控制台被重设大小时发送。Windows系统里,仅会在控制台上输入内容时,光标移动,或者可读的tty在原始模式上使用。 SIGKILL不能有监视器,在所有平台上无条件关闭node。 SIGSTOP不能有监视器。 Windows不支持发送信号,但是node提供了很多process.kill()和child_process.kill()的模拟: 发送Sending信号0可以查找运行中得进程 发送SIGINT,SIGTERM和SIGKILL会引起目标进程无条件退出。 process.stdout 一个Writable Stream执向stdout(fd1). 例如:console.log的定义: console.log = function(d) { process.stdout.write(d + '\n'); }; process.stderr和process.stdout和 node 里的其他流不同,他们不会被关闭(end()将会被抛出),它们不会触发finish事件,并且写是阻塞的。 引用指向常规文件或TTY文件描述符时,是阻塞的。 引用指向pipe管道时: 在Linux/Unix里阻塞. 在Windows像其他流一样,不被阻塞 检查Node是否运行在TTY上下文中,从process.stderr,process.stdout或process.stdin里读取isTTY属性。 $ node -p "Boolean(process.stdin.isTTY)" true $ echo "foo" | node -p "Boolean(process.stdin.isTTY)" false $ node -p "Boolean(process.stdout.isTTY)" true $ node -p "Boolean(process.stdout.isTTY)" | cat false process.stderr 一个指向stderr (fd2)的可写流。 process.stderr和process.stdout和node里的其他流不同,他们不会被关闭(end()将会被抛出),它们不会触发finish事件,并且写是阻塞的。 引用指向常规文件或TTY文件描述符时,是阻塞的。 引用指向pipe管道时: 在Linux/Unix里阻塞 在Windows像其他流一样,不被阻塞 process.stdin 一个指向stdin (fd0)的可读流。 以下例子:打开标准输入流,并监听两个事件: process.stdin.setEncoding('utf8'); process.stdin.on('readable', function() { var chunk = process.stdin.read(); if (chunk !== null) { process.stdout.write('data: ' + chunk); } }); process.stdin.on('end', function() { process.stdout.write('end'); }); process.stdin可以工作在老模式里,和v0.10之前版本的node代码兼容。 更多信息参见Stream compatibility. 在老的流模式里,stdin流默认暂停,必须调用process.stdin.resume()读取。可以调用process.stdin.resume()切换到老的模式。 如果开始一个新的工程,最好选择新的流,而不是用老的流。 process.argv 包含命令行参数的数组。第一个元素是'node',第二个参数是JavaScript文件的名字,第三个参数是任意的命令行参数。 // print process.argv process.argv.forEach(function(val, index, array) { console.log(index + ': ' + val); }); 将会生成: $ node process-2.js one two=three four 0: node 1: /Users/mjr/work/node/process-2.js 2: one 3: two=three 4: four process.execPath 开启当前进程的执行文件的绝对路径。 例子: /usr/local/bin/node process.execArgv 启动进程所需的 node 命令行参数。这些参数不会在process.argv里出现,并且不包含node执行文件的名字,或者任何在名字之后的参数。这些用来生成子进程,使之拥有和父进程有相同的参数。 例子: $ node --harmony script.js --version process.execArgv 的参数: ['--harmony'] process.argv 的参数: ['/usr/local/bin/node', 'script.js', '--version'] process.abort() 这将导致node触发abort事件。会让node退出并生成一个核心文件。 process.chdir(directory) 改变当前工作进程的目录,如果操作失败抛出异常。 console.log('Starting directory: ' + process.cwd()); try { process.chdir('/tmp'); console.log('New directory: ' + process.cwd()); } catch (err) { console.log('chdir: ' + err); } process.cwd() 返回当前进程的工作目录: console.log('Current directory: ' + process.cwd()); process.env 包含用户环境的对象,参见environ(7). 这个对象的例子: { TERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node' } 你可以写入这个对象,但是不会改变当前运行的进程。以下的命令不会成功: node -e 'process.env.foo = "bar"' && echo $foo 这个会成功: process.env.foo = 'bar'; console.log(process.env.foo); process.exit([code]) 使用指定的code结束进程。如果忽略,将会使用code 0 使用失败的代码退出: process.exit(1); Shell将会看到退出代码为1. process.exitCode 进程退出时的代码,如果进程优雅的退出,或者通过process.exit()退出,不需要指定退出码。 设定process.exit(code)将会重写之前设置的process.exitCode。 process.getgid() 注意:这个函数仅在POSIX平台上可用(例如,非Windows和Android)。 获取进程的群组标识(参见getgid(2))。获取到得时群组的数字id,而不是名字。 if (process.getgid) { console.log('Current gid: ' + process.getgid()); } process.setgid(id) 注意:这个函数仅在POSIX平台上可用(例如,非Windows 和 Android)。 设置进程的群组标识(参见setgid(2))。可以接收数字ID或者群组名。如果指定了群组名,会阻塞等待解析为数字ID 。 if (process.getgid && process.setgid) { console.log('Current gid: ' + process.getgid()); try { process.setgid(501); console.log('New gid: ' + process.getgid()); } catch (err) { console.log('Failed to set gid: ' + err); } } process.getuid() 注意:这个函数仅在POSIX平台上可用(例如,非Windows和Android)。 获取进程的用户标识(参见getuid(2))。这是数字的用户id,不是用户名: if (process.getuid) { console.log('Current uid: ' + process.getuid()); } process.setuid(id) 注意:这个函数仅在POSIX平台上可用(例如,非Windows和Android)。 设置进程的用户标识(参见setuid(2))。接收数字ID或字符串名字。果指定了群组名,会阻塞等待解析为数字ID。 if (process.getuid && process.setuid) { console.log('Current uid: ' + process.getuid()); try { process.setuid(501); console.log('New uid: ' + process.getuid()); } catch (err) { console.log('Failed to set uid: ' + err); } } process.getgroups() 注意:这个函数仅在POSIX平台上可用(例如,非Windows和Android)。 返回进程的群组iD数组。POSIX系统没有保证一定有,但是node.js保证有。 process.setgroups(groups) 注意:这个函数仅在POSIX平台上可用(例如,非Windows和Android)。 设置进程的群组ID。这是授权操作,所有你需要有root权限,或者有CAP_SETGID能力。 列表可以包含群组IDs,群组名,或者两者都有。 process.initgroups(user, extra_group) 注意:这个函数仅在POSIX平台上可用(例如,非Windows和Android)。 读取/etc/group ,并初始化群组访问列表,使用成员所在的所有群组。这是授权操作,所有你需要有root权限,或者有CAP_SETGID能力。 user是用户名或者用户ID,extra_group是群组名或群组ID。 当你在注销权限 (dropping privileges) 的时候需要注意. 例子: console.log(process.getgroups()); // [ 0 ] process.initgroups('bnoordhuis', 1000); // switch user console.log(process.getgroups()); // [ 27, 30, 46, 1000, 0 ] process.setgid(1000); // drop root gid console.log(process.getgroups()); // [ 27, 30, 46, 1000 ] process.version 一个编译属性,包含NODE_VERSION。 console.log('Version: ' + process.version); process.versions 一个属性,包含了node的版本和依赖。 console.log(process.versions); 打印出来: { http_parser: '1.0', node: '0.10.4', v8: '3.14.5.8', ares: '1.9.0-DEV', uv: '0.10.3', zlib: '1.2.3', modules: '11', openssl: '1.0.1e' } process.config 一个包含用来编译当前node执行文件的javascript配置选项的对象。它与运行./configure 脚本生成的"config.gypi"文件相同。 一个可能的输出: { target_defaults: { cflags: [], default_configuration: 'Release', defines: [], include_dirs: [], libraries: [] }, variables: { host_arch: 'x64', node_install_npm: 'true', node_prefix: '', node_shared_cares: 'false', node_shared_http_parser: 'false', node_shared_libuv: 'false', node_shared_v8: 'false', node_shared_zlib: 'false', node_use_dtrace: 'false', node_use_openssl: 'true', node_shared_openssl: 'false', strict_aliasing: 'true', target_arch: 'x64', v8_use_snapshot: 'true' } } process.kill(pid[, signal]) 发送信号给进程。pid是进程id,并且signal是发送的信号的字符串描述。信号名是字符串,比如'SIGINT'或'SIGHUP'。如果忽略,信号会是'SIGTERM'。更多信息参见Signal 事件和kill(2) . 如果进程没有退出,会抛出错误。信号0可以用来测试进程是否存在。 注意,虽然这个这个函数名叫process.kill,它真的仅是信号发射器,就像kill系统调用。信号发射可以做其他事情,不仅是杀死目标进程。 例子, 给自己发信号: process.on('SIGHUP', function() { console.log('Got SIGHUP signal.'); }); setTimeout(function() { console.log('Exiting.'); process.exit(0); }, 100); process.kill(process.pid, 'SIGHUP'); 注意:当Node.js接收到SIGUSR1信号,它会开启debugger调试模式, 参见Signal Events. process.pid 当前进程的PID: console.log('This process is pid ' + process.pid); process.title 获取/设置(Getter/setter) 'ps'中显示的进程名。 使用setter时,字符串的长度由系统指定,可能会很短。 在Linux和OS X上,它受限于名称的长度加上命令行参数的长度,因为它会覆盖参数内存(argv memory)。 v0.8版本允许更长的进程标题字符串,也支持覆盖环境内存,但是存在潜在的不安全和混乱(很难说清楚)。 process.arch 当前CPU的架构:'arm'、'ia32'或者'x64'。 console.log('This processor architecture is ' + process.arch); process.platform 运行程序所在的平台系统'darwin','freebsd','linux','sunos'或者'win32' console.log('This platform is ' + process.platform); process.memoryUsage() 返回一个对象,描述了Node进程所用的内存状况,单位为字节。 var util = require('util'); console.log(util.inspect(process.memoryUsage())); 将会生成: { rss: 4935680, heapTotal: 1826816, heapUsed: 650472 } heapTotal和heapUsed指V8的内存使用情况。 process.nextTick(callback) callback{Function} 一旦当前事件循环结束,调用回到函数。 这不是setTimeout(fn, 0)的简单别名,这个效率更高。在任何附加I/O事件在子队列事件循环中触发前,它就会运行。 console.log('start'); process.nextTick(function() { console.log('nextTick callback'); }); console.log('scheduled'); // Output: // start // scheduled // nextTick callback 在对象构造后,在I/O事件发生前,你又想改变附加事件处理函数时,这个非常有用。 function MyThing(options) { this.setupOptions(options); process.nextTick(function() { this.startDoingStuff(); }.bind(this)); } var thing = new MyThing(); thing.getReadyForStuff(); // thing.startDoingStuff() gets called now, not before. 要保证你的函数一定是100%同步执行,或者100%异步执行。例子: // WARNING! DO NOT USE! BAD UNSAFE HAZARD! function maybeSync(arg, cb) { if (arg) { cb(); return; } fs.stat('file', cb); } 这个API非常危险. 如果你这么做: maybeSync(true, function() { foo(); }); bar(); 不清楚foo()或bar()哪个先执行。 更好的方法: function definitelyAsync(arg, cb) { if (arg) { process.nextTick(cb); return; } fs.stat('file', cb); } 注意:nextTick队列会在完全执行完毕之后才调用I/O操作。因此,递归设置nextTick的回调就像一个while(true);循环一样,将会阻止任何I/O操作的发生。 process.umask([mask]) 设置或读取进程文件的掩码。子进程从父进程继承掩码。如果mask参数有效,返回旧的掩码。否则,返回当前掩码。 var oldmask, newmask = 0022; oldmask = process.umask(newmask); console.log('Changed umask from: ' + oldmask.toString(8) + ' to ' + newmask.toString(8)); process.uptime() 返回Node已经运行的秒数。 process.hrtime() 返回当前进程的高分辨时间,形式为[seconds, nanoseconds]数组。它是相对于过去的任意事件。该值与日期无关,因此不受时钟漂移的影响。主要用途是可以通过精确的时间间隔,来衡量程序的性能。 你可以将之前的结果传递给当前的process.hrtime(),会返回两者间的时间差,用来基准和测量时间间隔。 var time = process.hrtime(); // [ 1800216, 25 ] setTimeout(function() { var diff = process.hrtime(time); // [ 1, 552 ] console.log('benchmark took %d nanoseconds', diff[0] * 1e9 + diff[1]); // benchmark took 1000000527 nanoseconds }, 1000); process.mainModule require.main的备选方法。不同点,如果主模块在运行时改变,require.main可能会继续返回老的模块。可以认为,这两者引用了同一个模块。 该process.mainModule属性提供了一种替代的检索方式require.main。不同之处在于,如果主模块在运行时发生更改,require.main可能仍会引用发生更改之前所需模块中的原始主模块。通常,假设这两个参照相同的模块是安全的。 和require.main一样, 如果没有入口脚本,将会返回undefined。 process.mainModule require.main的备选方法。不同点,如果主模块在运行时改变,require.main可能会继续返回老的模块。可以认为,这两者引用了同一个模块。 该process.mainModule属性提供了一种替代的检索方式require.main。不同之处在于,如果主模块在运行时发生更改,require.main可能仍会引用发生更改之前所需模块中的原始主模块。通常,假设这两个参照相同的模块是安全的。 和require.main一样, 如果没有入口脚本,将会返回undefined。 Node TSL.js Node.js TTY