进程Process

场景

  • notepad.exe是一个程序,不是进程
  • 双击notepad.exe,操作系统会开启一个进程

定义

  • 进程是程序的执行实例
  • 程序在CPU上执行时的活动叫做进程
  • 实际上并没有明确的定义,只有一些规则

特点

  • 一个进程可以创建另一个进程(父进程和子进程)
  • 通过任务管理器可以看到进程

    CPU

  • 一个单核CPU,在一个时刻,只能做一件事情

  • 那么如何让用户同时看电影、听声音、写代码呢?
  • 就是在不同进程中快速切换

多程序并发执行

  • 指多个程序在宏观上并行,微观上串行
  • 每个进程会出现【执行-暂停-执行】的规律
  • 多个进程之前会出现抢资源(如打印机)的现象

    进程的两个状态

    image.png
    image.png

    阻塞进程

    等待执行的进程中

  • 都是非运行态

  • 一些(A)在等待CPU资源
  • 另一些(B)在等待I/O完成(如文件读取)
  • 如果这个时候把CPU分配给B进程,B还是在等I/O
  • 我们把这个B叫做阻塞进程
  • 因此,分派程序只会把CPU分配给非阻塞进程

    进程的三个状态

    image.png

    线程Thread

    在面向进程设计的系统中,进程是程序的基本执行实体
    在面向线程设计的系统张,进程本神不是基本运行单位,而是线程的容器

  • 进程是执行的基本实体,也是资源分配的基本实体

  • 导致进程的创建、切换、销毁太消耗CPU时间
  • 引入线程,线程作为执行的基本实体
  • 而进程只作为资源分配的基本实体

    概念

  • CPU调度和执行的最小单元

  • 一个进程中至少有一个线程,可以有多个线程
  • 一个进程中的线程共享该进程中的所有资源
  • 进程的第一个线程叫做初始化线程
  • 线程的调度可以由操作系统负责,也可以用户自己负责

    e.g.

  • 浏览器进程界面有渲染引擎、V8引擎、存储模块、网络模块、用户界面模块等

  • 每个模块都可以放在一个线程里

子进程 vs 线程

优先使用线程

Node.js操作进程

child_process

使用目的

  • 子进程的运行结果储存在系统缓存中(最大200kb)
  • 等到子进程运行结束以后,主进程再用回调函数读取子进程的运行结果

    API

    exec(cms,options,fn)

    • execute的缩写,用于执行bash命令
    • 同步版本:execSync
      1. const child_process = require('child_process')
      2. const {
      3. exec
      4. } = child_process
      5. exec('ls ../', (error, stdout, stderr) => {
      6. console.log(error)
      7. console.log(stdout)
      8. console.log(stderr)
      9. })
    • 返回一个流 ```javascript const child_process = require(‘child_process’) const { exec } = child_process const stream = exec(‘ls -l ../‘) stream.stdout.on(‘data’, (chunk) => { console.log(‘得到了数据’) console.log(chunk) })

stream.stderr.on(‘data’,(data)=>{ console.log(data) })

stream.on(‘close’, function(code) { console.log(‘closing code: ‘ + code); });

  1. - Promise
  2. - 可以使其Promise化(用util.promisify
  3. ```javascript
  4. const util = require('util')
  5. const child_process = require('child_process')
  6. const {
  7. exec
  8. } = child_process
  9. const exec2 = util.promisify(exec)
  10. exec2('ls ../').then(data => {
  11. console.log(data.stdout)
  12. })
  • 漏洞
    • 如果cmd被注入了,可能执行意外代码 ```javascript const util = require(‘util’) const child_process = require(‘child_process’) const { exec } = child_process

const exec2 = util.promisify(exec) const userInput=’. && pwd’//注入 exec2(ls ${userInput}).then(data => { console.log(data.stdout) })

  1. <a name="ZYvcT"></a>
  2. ### 推介使用execFile
  3. ```javascript
  4. const child_process = require('child_process')
  5. const {
  6. execFile
  7. } = child_process
  8. const userInput = ". && pwd"//pwd无法执行,不会引发注入
  9. execFile('ls', ["-la", userInput], (error, stdout) => {
  10. console.log(error)
  11. console.log(stdout)
  12. })
  • options

    • cwd-Current working directory 命令在哪个目录执行
    • env-命令行环境变量
    • shell-用什么shell,默认bash
    • maxBuffer-最大缓存,默认10242字节
      1. const userInput = "."
      2. execFile('ls', ["-la", userInput], {
      3. cwd: "C:\\",
      4. env: {
      5. NODE_ENV: 'development'
      6. },
      7. shell: '...shell的路径..',
      8. maxBuffer: 1024 * 1024 * 2
      9. }, (error, stdout) => {
      10. console.log(error)
      11. console.log(stdout)
      12. })

      spawn

  • 用法与execFile方法类似

  • 没有回调函数,只能通过流事件获取结果
  • 没有最大200kb的限制(因为是流)
  • 经验:能用spawn得实惠就不要用execFile

    1. const {
    2. spawn
    3. } = child_process
    4. const userInput = "."
    5. const streams = spawn('ls', ["-la", userInput], {
    6. cwd: "C:\\",
    7. env: {
    8. NODE_ENV: 'development'
    9. }
    10. })
    11. streams.stdout.on('data', (chunk) => {
    12. console.log(chunk)
    13. })

    fork

  • 创建一个子进程,执行Node脚本(只能执行js脚本,bash不行)

    • fork(‘./child.js’)相当于spawn(‘node’,[‘./child.js’])
  • 特点
    • 会多出一个message事件,用于父子通信
    • 会多出一个send方法

n.js

  1. //主进程
  2. const child_process = require('child_process')
  3. var n = child_process.fork('./child.js');
  4. n.on('message', function (m) {
  5. console.log('PARENT got message:', m);
  6. });
  7. n.send({
  8. hello: 'world'
  9. });

child.js

  1. //子进程
  2. process.on('message', function(m) {
  3. console.log('CHILD got message:', m);
  4. });
  5. process.send({ foo: 'bar' });

image.png

Node.js操作线程

一些历史

  • child_process.exec
    • v0.1.90加入Node.js
  • new Worker
    • v10.5.0加入Node.js
    • v11.7.0之前需要 —experimental-worker开启
  • 这个线程API胎心了
    • 所以不经常用到
  • 效率

    • 目前效率并不高,文档

      worker_threads API

  • API

    • isMain Thread
    • new Workder(filename)
    • parentPort
    • postMessage
  • 事件列表
    • message
    • exit