本文非原创,译自https://www.backblaze.com/blog/whats-the-diff-programs-proces...
第一次翻译,文中有错误的地方还请指正,谢谢。
你可能经常听说计算机程序中的“线程”,但不确定其具体含义。那么,进程又是什么呢?你可能知道线程在某种程度上与程序和进程密切相关。如果您不是计算机科学专业的学生,可能对此了解有限。
对程序员来说,理解这些术语是必要的。即便是普通计算机用户,了解它们也很有用处。这些知识可以帮助你使用Mac的“活动监视器”、Windows的“任务管理器”或Linux的“Top”(一种性能分析工具),以识别和解决计算机上出现的问题,或判断是否需要增加内存来优化系统运行。
让我们花几分钟时间探索计算机程序的世界,并理解这些术语的含义。我们将简化和概括一些概念,以帮助区分这些术语之间的不同。
Programs(程序)
首先,你可能知道程序是存储在计算机上的旨在完特定任务的代码,程序有多种类型,其中包括计算机的函数程序和操作系统部分的程序,以及特定工作的其它程序。这些特定任务的程序也被称为“应用程序”,它包括诸如文本处理程序,web浏览程序,或通过电子邮件将消息发送到另一台计算机的程序。
程序通常以计算机可执行的形式存储在磁盘或非易失性存储器上。在此之前,它们使用如C、Lisp、Pascal或其他编程语言编写而成,这些语言包含逻辑、数据处理、设备管理、递归以及用户交互的指令。为了在计算机上运行,程序最终被编译成二进制形式(即由1和0组成)的代码文件。另一种类型的程序是“解释型语言”,它们在运行时才被解释为可执行代码,而不是预先编译。常见的解释型语言包括Python、PHP、JavaScript和Ruby。
不管采用哪种方式,程序运行时都以二进制形式加载到内存中。因为计算机的CPU(中央处理单元)只理解二进制指令,所以当CPU运行程序时,程序需要转换成二进制形式。
二进制是计算机的原生语言,因为电子电路有两种基本状态:开或关,分别用0和1表示。我们平时使用的是基于10进制的通用编码系统,其中每个数位可以是从0到9的任意数字。而在二进制中,每个位置只能是0或1。
你可能听过一个关于程序员的笑话,“世界上只有10种人,懂二进制的人和不懂二进制的人”
十进制 | 二进制 |
---|---|
0 | 0000 |
1 | 0001 |
2 | 0010 |
3 | 0011 |
4 | 0100 |
5 | 0101 |
6 | 0110 |
7 | 0111 |
8 | 1000 |
9 | 1001 |
进程如何工作?
程序已经以二进制的形式加载到计算机内存中了。那么接下来呢?
执行中的程序不只是需要告诉计算机做什么的二进制代码。程序还需要内存和各种操作系统资源才能运行。“进程”就是我们说的程序,它和它所需要去操作的所有资源一起被加载到内存中。“操作系统”是分配所有资源的大脑,它有多种类型,诸如macOS,IOS,Microsoft Windows,Linux,以及Android。操作系统负责管理将程序转换为运行中的进程所需的资源。
任何进程都需要一些必要的资源,如寄存器,程序计数器和栈。“寄存器”是计算机处理器(CPU)用于数据存储区域的部分。寄存器可以存放指令,存储地址,或进程需要的其它类型的数据。“程序计数器”也被称为指令指针,它用于指示计算机在其程序序列中的位置。“栈”是一种存储计算机程序的当前子程序信息(这里个人理解为你调试代码时的执行上下文)的数据结构,被用于进程的暂存空间。它跟为进程动态分配的内存(被称为“堆”)是需要区分开来的。
单个程序可以有多个实例,且运行中的程序的每个实例都是一个进程。每个进程有单独的内存地址空间,这意味着进程是独立运行的并且和其它进程是相互隔离的。它不能直接访问其它进程的共享数据。从一个进程切换到另一个进程需要一些时间(相对地)来保存和加载寄存器,内存映射以及其它资源。
进程的这种独立是很重要的,因为操作系统会尽可能的隔离进程以保证一个进程的问题不会影响到另一个进程。你肯定碰到过计算机一个应用程序卡死或者出现问题的情况,并且你能够在不影响其它程序的情况下退出该程序。
线程如何工作?
接下来终于讲到了线程!
线程是进程的执行单元。一个进程可以有一个或者多个线程。
当一个进程开始时,它会被指定内存和资源。进程中的每个线程共享内存和资源。在单线程进程中,进程只包含一个线程。进程和线程是相同的,并且只有一件事在发生。
在多线程进程中,进程包含多个线程,而且该进程在同时做多个事情(从技术角度而言,有时几乎等于同时——想了解更多参见“并行和并发?”下面的部分)
我们谈到了进程或线程可用的两种内存栈和堆。区分这两类进程内存是很重要的,因为每个线程都有它自己的栈,但是进程中的所有线程将共享堆。
线程有时被称为轻量级进程,是由于它们都有自己的栈而且可以访问共享数据。因为线程跟进程以及进程中的其它线程共享相同的地址空间(表示计算机实体所占用的内存大小),两个线程之间的通讯开销就很低,这是有利的。不利的是进程中的一个线程有问题必定会影响到其它线程以及进程本身的存活。
进程 VS 线程
我们来回顾一下:
- 程序以编程代码的文本文件开始
- 程序会被编译或解释成二进制的形式
- 程序会被加载进内存
- 程序可以运行一个或多个进程
- 进程通常是和其它进程独立开的
- 线程作为进程的子集存在
- 线程之间通讯比进程之间通讯要容易很多
- 线程更容易被相同进程下的其它线程导致的问题影响
线程 vs 进程 —— 优势 和 劣势
线程 | 进程 |
---|---|
进程操作是重量级的 | 线程操作是更轻量的 |
每个进程都有自己的内存空间 | 线程使用它们所属进程的内存 |
进程之间通讯较慢,因为进程都有不同的内存地址 | 线程之间通讯比进程之间通讯更快,因为统一进程的线程共享它们所属进程的内存 |
进程之间上下文切换是代价更高的 | 相同进程下的线程上下文切换是代价较低的 |
进程不与其它进程共享内存 | 线程和同一进程下的其它线程共享内存 |
并行和并发?
你也许有这样的疑问,进程或者线程是否能在同时运行?答案是:这要看情况。对于多处理器或CPU内核(与现代处理器一样)的系统,多个进程或线程可以并行的执行。对于单个处理器,它是不可能有多个线程或进程真正的同时执行。在这种情况下,CPU在运行的进程或线程之间共享,使用进程调度算法来划分CPU时间,进而产生并行执行的错觉。分配给每个任务的时间被称为“时间切片”。在任务之间来回切换很快,几乎是无法察觉的。术语并行(真正同时执行)和并发(进程及时的交替执行产生的同时执行的现象),用真正同时执行的操作和接近同时执行的操作来区分。
为什么选择进程而不是线程,或者为什么选择线程而不是进程?
那么 当程序员在创建同时执行多个任务的程序时该怎样去选择进程或线程呢?我们在上面已经概括了它们的差异,现在我们来看一个真实的例子,我们很多人都在使用的程序——Coogle Chrome。
Google在设计Chrome浏览器的时候,它们必需考虑如何同时处理许多需要计算机、通信和网络资源的不同任务。每个浏览器窗口或者选项卡与网络的多个服务通信去获取文本,程序,图像,音频,视频,以及其它资源,继而渲染这些数据以供显示和与用户交互。除此之外,浏览器可以打开多个窗口,每个窗口都有很多任务。
Google必需考虑如何去处理任务的隔离。他们选择把Chrome中每个浏览窗口作为一个单独的进程去运行,而不是像其它浏览器一样作为一个或多个线程去运行。这样做给Google带来了许多好处。将运行中的每个窗口作为一个进程保护了总体应用程序不被渲染引擎的异常和故障所影响,并限制了每个渲染引擎进程对其他进程和系统其余部分的访问。隔离了进程中的JavaScript程序防止它占用太多的CPU时间和内存从而导致整个浏览器失去响应。
Google仔细权衡了多进程的设计。每个浏览器窗口开始一个新的进程在内存和资源方面比起使用线程有更高的固定开销。但是他们敢说他们的方法最终会减少总体的内存膨胀。
当内存不够的时使用进程替代线程也能提供更好的内存用法。不活跃的窗口会被操作系统视为较低的优先级,并且当其它进程需要内存时有资格被调换到磁盘中去。这有助于保持用户显示的窗口更好的响应。如果窗口使用的是线程,想要完全的分隔开已用和未使用的内存是将会更困难,无疑浪费了内存和性能。
您可以在Google的Chromium blog或Chrome Introduction Comic上了解更多的Google对Chrome的设计决策。
下面的截图将展示运行在MacBook Air上打开了多个选项卡的Google Chrome进程。有些Chrome进程使用了很多的CPU时间和资源,而有些进程使用的非常少。你可以看到每个进程也有许多线程正在运行。
系统上的活动监视器或任务管理器在微调你的计算机或者定位解决问题时是个不错的帮手。如果你的计算机或程序运行的很慢,或者浏览器窗口在一段时间没有响应,你可以使用系统监视器检查它的状态。有时你将会看到进程被标记为“没有响应”。尝试退出那个进程再看看你的系统是否会运行的更快。如果某个应用程序占用着大量的内存,你就应该考虑选择其它的应用程序来完成这个任务了。
剩余一点附录未完全翻译,有兴趣请移步原文。https://www.backblaze.com/blog/whats-the-diff-programs-proces...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。