热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Node多进程exec方法执行流程源码分析

Node多进程exec方法执行流程源码分析-在自研脚手架阶段,对node的同步异步执行进行了使用;但是未能深层次去了解为何这么设计,这次也是通过问题的方式来进行了一些思考。⚠️注:
在自研脚手架阶段,对node的同步/异步执行进行了使用;但是未能深层次去了解为何这么设计,这次也是通过问题的方式来进行了一些思考。
⚠️注: 目前是12版本的node;你可能现在用的版本是16版以上用TS来写的;但是node的基本上的思路是没有太多变化的。

思考

问题一:exec和execFile到底什么区别?
问题二: 为什么exec/execFile/fork都是通用spawn实现的,spawn的作用到底是什么?
问题三:为什么spawn没有回调;exec和execFile能够回调?
问题四:为什么spawn调用后,需手动调用child(spawn返回值).stdout.on('data',callback);spawn.stdout和spawn.stderr到底是什么?
问题五:为什么有data/errer/exit这么多种回调;他们的执行顺序到底是什么?

一、 源码分析

源码分析方法:根据方法的执行调用顺序来进行分析源码

exec源码分析

源码目录结构

child_process.js

  • exec
  • execFile
  • spawn

internal/child_process.js

  • ChildProcess
  • spawn
代码执行流程
  1. 执行本地代码

    首先在本地创建index.js;
    const cp = require('child-process');
    const child = cp.exec('ls -la|grep node_modules', function(err, stdout stderr){
        console.log(err, stdout, stderr);
    });

  1. 执行 cp.exec方法

    将会调用node的内置库【child_process】中的【exec】方法,进行参数的标准化处理【normalizeExecArgs】
function exec (command, options, callback) {
    const opts = noramlizeExeArgs(command, options, callback);
    return module.exports.execFile(opts.file,
                                   opts.optionns,
                                   opts.callback);
}
入参处理:
opts:{
file : "ls -al|grep node_modules",
options : {shell : ture},
callback : .....,
}
入参处理后的结果:
  • file 是输入的命令
  • options 添加了shell为true
  • callback 没有变化
返回结果:
  • return module.exports.execFile
  • 直接调用execFile方法


通过exec方法中的noramilizeExecArgs方法将参数转化成execFile方法的参数一样;

  1. 进入execFile方法

    a. 首先还是会进行一个参数的标准化处理【normalizeExecFileArgs】
    b. 调用spawn child方法,主要目的是创建一个子进程并且对它进行异步执行
const child = spawn (file, args, {
    cwd: options.cwd,
    env: options.env,
    gid: options.gid,
    uid: options.uid,
    shell: options.shell,
    .....
});
spawn参数说明:
  • file: "ls -al|grep node_modules",
  • agrs: 没有参数,
  • object:{shell: true}; 中只有shell参数显示true,需要用内部的shell脚本去执行.

  1. 调用spawn方法

    a. 进行参数的标准化处理【normalizeExecFileArgs】
    b. 调用 child = new ChildProcess()
function spawn(file, args, options) { 
    const opts = normalizeSpawnArguments(file, args, options);
    const child = new ChildProcess();
// 在child,创建的子进程当中有一个_handle是实际的进程,_handle= Process{onexit:Function,...};调用的方式是需要用spwan来调用。spwan最终执行的是_handle的spwan;
};
opts返回的参数处理:
file: '/bin/sh'

//这个是shell的主命令
在本地执行下:

shell的使用
方法一: 直接执行shell文件

    /bin/sh test.shell

方法二: 直接执行shell语句

    /bin/sh -c "ls-al|grep node_modules"

因为传入了参数: shell = true;所以file就设置为/bin/sh;表示用shell主命令来执行。

args: ["bin/sh", "-c", "ls -al|grep node_modules"]
options: {
cwd: null,
...
shell: true,
...
}
envPairs: // 操作系统的环境变量数据

  1. new ChildProcess

    a. 来源是node的内部库【interinternal/child_process】,node的内置库才可以调用到。
    b. 这个内部库中有一个ChildProcess类;对应的就是子进程类,(就是子进程);所以在spawn中new ChildProcess()就创建了一个子进程类;

child_process.js

const child_process = require('internal/child_process')
 
const {
    ...
    ChildProcess,
    ...
    } = child_process; 
  1. interinternal/child_process库中的ChildProcess

internal/child_process.js

const {Process} =  internalBinding('process_warp');
// 引入c++文件
 this._handle = Process;

  1. 创建子进程之后,调用spawn方法,child.spwan;利用子进程去执行命令,执行完之后,直接返回,return child

    function spawn(file, args, options) { 
     const opts = normalizeSpawnArguments(file, args, options);
     const child = new ChildProcess();
    ...
    child.spawn({
     file: opts.file, // "/bin/sh"
     args: opts.args,  // ["/bin/sh", "-c", "ls -a | grep node_modules"]
     cwd: options.cwd,
     ...
    })
    
     return child;

    child.spawn是把命令执行起来的方法,位于internal/child_process文件中,最核心的是this._handle.spawn方法,之前只是创建了进程对象,没有分配任何实际资源。调用this._handle.spawn进程就被执行起来。相应也会生成子进程ID;

  2. spawn执行完之后,回到execFile中,进行往下执行

    a. 对输出流进行监听: child.stdout.on('data')
    b. 对错误流进行监听: child.stderr.on('data')
....
if (child.stdout) {
    if(encoding) {
        child.stdout.setEncoding(encoding);
child,stdout.on('data', function onChildStdout(chunk){
        const encoding = child.stdout.readableEncoding;
            ...
        }
     }
}
...
if (child.stderr) {
    if(encoding) {
        child.stderr.setEncoding(encoding);
child,stdout.on('data', function onChildStderr(chunk){
        const encoding = child.stderr.readableEncoding;
            ...
        }
     }
}
...

child.addListener('close', exithandler);
child.addListener('error', errorhander);
return chuild;

这也是为什么execFile可以返回callback的原因;手动做了监听;

c. 对进程进行exiterror的监听;

总结

以上是exec方法执行流程的分析,如图:


推荐阅读
  • 本文介绍了在wepy中运用小顺序页面受权的计划,包含了用户点击作废后的从新受权计划。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文介绍了三种方法来实现在Win7系统中显示桌面的快捷方式,包括使用任务栏快速启动栏、运行命令和自己创建快捷方式的方法。具体操作步骤详细说明,并提供了保存图标的路径,方便以后使用。 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • 本文总结了在开发中使用gulp时的一些技巧,包括如何使用gulp.dest自动创建目录、如何使用gulp.src复制具名路径的文件以及保留文件夹路径的方法等。同时介绍了使用base选项和通配符来保留文件夹路径的技巧,并提到了解决带文件夹的复制问题的方法,即使用gulp-flatten插件。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • React项目中运用React技巧解决实际问题的总结
    本文总结了在React项目中如何运用React技巧解决一些实际问题,包括取消请求和页面卸载的关联,利用useEffect和AbortController等技术实现请求的取消。文章中的代码是简化后的例子,但思想是相通的。 ... [详细]
author-avatar
来杯冰柠檬水
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有