进程 和 线程
进程
进程:是操作系统对一个正在运行的程序的一种抽象,在一个系统上可以同时运行务个
进程,而每个进程都好像在独占地使用硬件。
打开多个程序,是一个进程的指令和另一个进程的指令是交错执行的。在大多数系统中,需要运行的进程数是多于可以运行它们的CPU个数的。传统系统在一个时刻只能执行一个程序,而先进的多核处理器同时能够执行多个程序。无论是在单核还是多核系统中,一个CPU看上去都像是在并发地执行多个进程(CPU太太太快了,那当多个任务要执行的时候怎么办呢?轮流着来?或者谁优先级高谁来?不管怎么样的策略,一句话就是在CPU看来就是轮流着来),这是通过处理器在进程间切换来实现的.操作系统实现这种交错执行的机制称为上下文切换。
上下文切换:操作系统保持跟踪进程运行所需的所有状态信息。这种状态也就是上下文,包括许多信息,比如 PC 和寄存器文件的当前值,以及主存的内容(进程是cpu资源分配的最小单位)。在任何一个时刻,单处理希系统都只能执行一个进程的代码.当操作系统决定要把控制权从当前进程转移到某个新进程时,就会进行上下丈切挽,即保存当前进程的上下文(等着下一次 CPU 临幸)、恢复新进程的上下文,然后将控制权传递到新进程。新进程就会从它上次停止的地方开始。
线程
一个进程实际上可以由多个称为线程的 执行単元 组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。
比喻:
进程的颗粒度太大,每次都要有上下的调入,保存,调出。一个运行在电脑上的软件,那么一个软件的执行不可能是一条逻辑就能执行完的,必定有多个分支和多个程序段,就好比要实现程序A,实际分成 a,b,c等多个块组合而成。那么这里具体的执行就可能变成:
程序A得到CPU临幸:CPU加载上下文,开始执行程序 A 的 a 小段,然后执行A的 b 小段,然后再执行A的 c 小段,最后CPU保存A的上下文。
这里a,b,c的执行是共享了A的上下文,CPU在执行的时候没有进行上下文切换的。这里的a,b,c就是线程,也就是说线程是共享了进程的上下文环境,的更为细小的CPU时间段。
Node
线程是cpu调度的一个基本单位,一个cpu同时只能执行一个线程的任务,同样一个线程任务也只能在一个cpu上执行
- 这个线程挂掉后整个程序就会挂掉;
- 所以如果你运行Node.js的机器是像i5,i7这样多核cpu无法充分利用多核资源
Event loop
事件循环是 node 处理非阻塞 I/O 操作的机制。
macro-task 大概包括:
- setTimeout
- setInterval
- setImmediate
- script(整体代码)
- I/O 操作等。
micro-task 大概包括:
- process.nextTick(与普通微任务有区别,在微任务队列执行之前执行)
- new Promise().then() 回调等。
下面的图表展示了事件循环操作顺序的简化概览:
每个框被称为事件循环机制的一个阶段。
每个阶段都有一个 FIFO 队列来执行回调。虽然每个阶段都是特殊的,但通常情况下,当事件循环进入给定的阶段时,它将执行特定于该阶段的任何操作,然后执行该阶段队列中的回调,直到队列用尽或最大回调数已执行。当该队列已用尽或达到回调限制,事件循环将移动到下一阶段.
各个 Node 版本有差异,以 Node 11 往后的版本讨论,它比较趋于和浏览器行为一致:一旦执行一个阶段里的一个宏任务(setTimeout, setInterval和 setImmediate),就立刻执行对应的微任务队列
阶段概述
- 定时器:本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。
- 待定回调:执行延迟到下一个循环迭代的 I/O 回调。
- idle, prepare:仅系统内部使用。
- 轮询:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞。
- 检测:setImmediate() 回调函数在这里执行。
- 关闭的回调函数:一些关闭的回调函数,如:socket.on('close', ...)。
child_process
使用方法 :
- spawn spawn('node', ['worker.js']) 启动一个字进程来执行命令
- exec exec('node worker.js', (err, stdout, stderr) => {}) 启动一个子进程来执行命令,有回调
- execFile('worker.js') 启动一个子进程来执行可执行的文件
(头部要添加#!/usr/bin/env node)
- fork('worker.js') ,不过这里只需要自定js文件模块即可
子进程创建服务的时候,多个进程监听一个端口:
- 最简单的就是每个子进程都使用不同的端口
- 第二种方案是,在主进程进行端口的监听,然后将监听的套接字传给子进程。
cluster
它可以通过一个父进程管理一堆子进程的方式来实现集群的功能。
cluster 底层就是 child_process。
进程守护
- 普通的逻辑错误:监听 uncaughtException
- 僵尸进程(进程存在,但是啥活都干不了):心跳检测(每个 1min 我给你发一条消息,如果超过3次你没有回复我,认为你心跳停止)
- 内存监控
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。