Node学习七 —— 创建和控制外部进程

23 篇文章 0 订阅

创建和控制外部进程

Node是被设计用来高效处理/O操作的,但正如你所见,某些类型的程序并不适用于这种模式。比如当用Node处理一个CPU密集型任务时可能会阻塞事件循环,所以应该将CPU密集任务分配给另一个进程去处理,释放事件循环,分担主进程的压力。

Node中可以创建进程,并把这些进程当成当前启动他的进程的子进程。

也许你不是很了解进程和线程,但这不是本文的重点,所以如果想要了解的更清楚,请查一查,简单了解一下。

本文的内容在于:

  • 如果生成外部命令
  • 创建子进程
  • 通信
  • 终止
  • 在Node进程之外实现多任务操作

提一句,后面用到的process对象是Node中的一个全局对象。

执行外部命令

使用child_process模块来执行外部的shell命令或者可执行文件。

使用exec函数:

const child_process = require('child_process');
child_process.exec("命令","可选参数","回调");

第一个参数为字符串类型的待执行的shell命令,第二参数是回调。回调应该有三个参数:error、stdout、stderr。如果出现错误,第一个参数接收,如果第一个参数不包含错误,第二个参数接收命令的的输出信息,最后一个包含命令的错误输出信息。

三个参数为啥叫这个名字: std => standard ; out => output ; err => error

可选参数是一个可传可不传的对象,他可以包含很多配置属性,比如超时时间、当前工作的目录(如果想命令到指定地方工作)、编码格式、环境变量等等等等,很多,可以自己查阅文档。例如,也许你想给子进程提供一组环境变量,作为父进程环境变量的扩展,如果直接修改process.env,就会导致每个模块都被修改,所以你可以复制process.env到一个本地变量,然后进行修改,最后把这个本地变量传入你的进程。

可以在同一个目录创建两个文件,par.jschild.js,通过前者启动后者。

// 这是par.js里面的代码
const exec = require('child_process').exec;
exec('node child.js' ,{env: { number: 123 }},function(err ,stdout ,stderr) {
    if(err) return;
    console.log(stdout);
    console.log(stderr);
})
// 这是child.js中的代码
const number = process.env.number;
console.log(number); // 输出一个字符串

结合上面的介绍体会体会。

生成子进程

使用exec函数启动外部进程,并且在结束时回调一个函数,不过存在缺点:

  • 除了命令行参数和环境变量,没有通信手段。
  • 子进程的输出被缓存,导致无法对其进行流操作,可能导致内存耗尽。

** 不过child_process可以实现更精细的工作。上面的例子是执行了一个外部进程,这个进程是外部的,不是父进程创建的,而是启动的。所以通过创建一个子进程,我们可以获取更多的操作权限。

创建子进程

const spawn = require("child_process").spawn;
const child = spawn("tail",['-f', '/var/log/system.log']);

我们创建了一个子进程,依据传递中括号中的两个参数执行tail命令,tail会监视路径标识的那个文件,将输出的数据加载到stdout流。spawn会返回一个ChildProcess对象,他是一个句柄,封装了对实际进程的访问。

句柄:一种特殊的智能指针,当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄。句柄与普通指针的区别在于,指针包含的是引用对象的内存地址,而句柄则是由系统所管理的引用标识,该标识可以被系统重新定位到一个内存地址上。这种间接访问对象的模式增强了系统对引用对象的控制。

监听子进程的输出数据

spawn返回的对象都具有一个 stdout属性,它以流的形式表示标准输出信息,可以在其上绑定事件监听:

child.stdout.on('data', function(data){
	console.log(data);
})

每当子进程将数据标准输出的时候,父进程就会触发事件,调用回调函数打印数据。

除了标准输出之外,还有一个默认输出流(standard error),通常用于输出错误消息。

例如,文件不存在,那么tail进程就会报错没有该文件。父进程通过监听stderr流获取通知:

chlid.stderr.on('data', function(data){
    console.log("error: " + data);
})

向子进程发送信息

标准输入流使用childProcess.stdin。子进程也可以使用process.stdin监听数据。

默认process.stdin是暂停状态,所以先要恢复他

stdin => standard input

子进程程序(C.js):

// 恢复标准输入流
process.stdin.resume();
process.stdin.on('data', function(data) {
	console.log(data);
	// 输出
	process.stdout.write("childe data : " + data);
})

如果你执行运行这个程序,那么在窗口中会等待你输入一个值,按下回车之后会在屏幕上看到输出值。

父进程程序(P.js):

// 使用node进程创建一个子进程执行子进程程序
const child = spawn('node', ['C.js']); // 中括号中可只填写一个路径,此时两个js文件在同一目录下,加上node拼接成一句完整命令
// 随便发送一个数据到子进程
child.stdin.write("childe");
child.stdout.once('data', function(data){
    console.log("子进程传递过来的数据/n");
    console.log(data);
})

为什么要是用noce?如果子进程在不同时间多次发送数据,我们想获取所有数据,可以设置一个定时器,如果在定时器中使用on绑定事件,那么随着时间推移将要注册多个回调函数(回到函数不是覆盖而是追加!因为并没有清楚之前的回调函数!),这样每次触发事件,所有回调都会被调用。所以使用once是个良好的习惯。

当子进程退出时获得通知

child.on("exit", function(code) {
    // code: 子进程终止的退出码,非0则是不正常退出
    console.log(code);
})

退出之后,会自动打印终止的有关信息。

如果子进程是被一个信号终止的(kill函数,看下面),那么相应的信号会作为第二个参数传递给回调。

终止子进程

信号是父子通信的简单方式,还可以用来终止进程。

一般可以使用child.kill方法向子进程发送一个信号,默认发送的是’SIGTERM’,还可以手动传递一个信号给kill函数。

尽管方法叫kill,但是发送的信息却不一定会终止进程!

子进程可以定义程序重写信号默认行为:

process.on('SIGUSR2', function() {
	...
})

SIGKILL、SIGSTOP比较特殊,无法重写默认行为。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Node中,进程和线程是两个重要的概念。进程是计算机中运行的程序的实例,它具有独立的内存空间和系统资源,可以独立执行任务。而线程是进程内的执行单元,一个进程可以包含多个线程,线程共享进程的内存空间和资源。 Node是单线程应用,意味着它只有一个主线程在执行任务。然而,Node通过创建多个进程来实现多线程的效果,以提高CPU的利用率。这是因为Node使用了非阻塞型I/O操作模型。在执行I/O操作时,Node将I/O任务放入线程池中,并等待事件完成后执行回调函数,然后将线程归还给线程池。这种事件循环实现方式使得Node能够高效处理大量的并发请求,且不会出现阻塞的情况。 虽然Node是单线程的,无法利用多核CPU,但是Node提供了一些API和第三方工具来解决这个问题。通过使用Cluster模块可以创建多个进程并进行负载均衡,使得Node能够充分利用多核CPU。此外,也可以使用Worker Threads模块来创建多个线程,进一步提高Node的并发能力。这些工具可以在Node中实现并行处理和高性能的应用程序。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [node进程与线程](https://blog.csdn.net/WH_Crx/article/details/119116325)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [深入理解 Node.js 进程与线程](https://blog.csdn.net/qq_41581588/article/details/126115598)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沧州刺史

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值