程序,进程和线程有什么区别?

程序,进程和线程有什么区别?

本文非原创,译自 www.backblaze.com/blog/whats-...

程序、进程和线程都是与软件执行相关术语,但你可能不清楚它们的真正含义。无论你是经验丰富的开发者、有抱负的爱好者,还是只是在打开电脑的任务管理器或Mac的活动监视器时想知道自己在看什么,学习这些术语对于理解计算机的工作方式至关重要。

什么是计算机程序?

什么是计算机程序? 程序是一系列编码指令,告诉计算机执行给定的任务。程序有很多种类型,包括内置在操作系统(OS)中的程序和用于完成特定任务的程序。一般来说,特定任务的程序被称为应用程序(或应用)。例如,你可能正在使用谷歌Chrome、Mozilla Firefox或苹果Safari等网页浏览器应用阅读这篇文章。其他常见的应用包括电子邮件客户端、文字处理器和游戏。

创建计算机程序的过程包括设计算法、用编程语言编写代码,然后编译或解释这些代码,将其转化为计算机可以执行的机器可读指令。

什么是编程语言?

编程语言是人类和计算机相互交流的方式。它们是一套规范化的规则和语法。

编译型与解释型程序

许多程序是用编译型语言编写的,并使用诸如C、C++、C#等编程语言创建。最终结果是一份代码的文本文件,它被编译成二进制形式以在计算机上运行(关于二进制形式的更多内容将在接下来的几段中介绍)。这个文本文件直接与你的计算机交流。尽管它们通常很快,但与解释型程序相比,它们也是固定的。这既有好处也有坏处:你可以更好地控制像内存管理这样的事情,但你依赖于平台,如果你需要在代码中更改某些内容,通常需要更长的时间来构建和测试。

还有另一种称为解释型程序的程序。它们需要一个额外的程序来接收你的程序指令,并将其翻译成计算机可以理解的代码。与编译型语言相比,这些类型的程序是平台独立的(你只需要找到一个不同的解释器,而不是编写一个全新的程序),并且它们通常占用的空间更小。一些最常见的解释型编程语言包括Python、PHP、JavaScript和Ruby。

最终,这两种类型的程序都以二进制形式运行并加载到内存中。程序必须以二进制运行,因为你的计算机CPU只能理解二进制指令。

什么是二进制代码?

二进制是计算机的基础语言。在最基本的层面上,计算机只使用两种电流状态------开和关。开状态用1表示,关状态用0表示。二进制与我们日常生活中使用的十进制不同。在十进制中,每个数字位置可以是从0到9的任何数字。在二进制系统中,每个位置要么是0,要么是1。

你可能听过一个关于程序员的笑话,"世界上只有10种人,懂二进制的人和不懂二进制的人"

程序通常以计算机可执行的形式存储在磁盘或非易失性存储器上。在此之前,它们使用如C、Lisp、Pascal或其他编程语言编写而成,这些语言包含逻辑、数据处理、设备管理、递归以及用户交互的指令。为了在计算机上运行,程序最终被编译成二进制形式(即由1和0组成)的代码文件。另一种类型的程序是"解释型语言",它们在运行时才被解释为可执行代码,而不是预先编译。常见的解释型语言包括Python、PHP、JavaScript和Ruby。

不管采用哪种方式,程序运行时都以二进制形式加载到内存中。因为计算机的CPU(中央处理单元)只理解二进制指令,所以当CPU运行程序时,程序需要转换成二进制形式。

二进制是计算机的原生语言,因为电子电路有两种基本状态:开或关,分别用0和1表示。我们平时使用的是基于10进制的通用编码系统,其中每个数位可以是从0到9的任意数字。而在二进制中,每个位置只能是0或1。

计算机程序如何存储和运行?

程序通常以可执行格式存储在磁盘或非易失性内存中。让我们详细解释一下这个过程。

在这个上下文中,我们会讨论计算机拥有两种类型的内存:易失性和非易失性。易失性内存是临时的,并实时处理。它更快、更容易访问,并提高了计算机的效率。然而,它不是永久的。当计算机关闭时,这种类型的内存会重置。

另一方面,非易失性内存除非被删除,否则是永久的。虽然访问速度较慢,但它可以存储更多信息。因此,它更适合用来存储程序。可执行格式的文件简单来说就是运行程序的文件。它可以由你的CPU(即处理器)直接运行。这些文件类型的例子包括Windows中的.exe和Mac中的.app。

程序运行需要哪些资源?

一旦程序以二进制形式加载到内存中,接下来会发生什么?

正在执行的程序需要从操作系统和内存中获取资源来运行。没有这些资源,你无法使用程序。幸运的是,你的操作系统自动管理分配资源给程序的工作。无论你使用的是Microsoft Windows、macOS、Linux、Android还是其他系统,你的操作系统总是在努力指导计算机的资源,以将你的程序转化为正在运行的过程。

除了操作系统和内存资源之外,每个程序都需要一些基本的资源。

  • 寄存器。它是计算机处理器(CPU)内部的一种非常快速的存储设备,它包含进程可能需要的数据,如指令、存储地址或其他数据。
  • 程序计数器。也称为指令指针,程序计数器扮演组织角色。它跟踪计算机在其程序序列中的位置。
  • 栈。栈是一种数据结构,用于存储有关计算机程序活动子程序的信息。它被用作进程的临时空间。它与为进程动态分配的内存区别开来,后者称为"堆"。

什么是计算机进程?

当程序连同其运行所需的所有资源一起加载到内存中时,它被称为进程。你可能会有一个单一程序的多个实例。在这种情况下,该运行程序的每个实例都是一个进程。

每个进程都有一个独立的内存地址空间。这个独立的内存地址很有用,因为它意味着进程独立运行,并与其他进程隔离。然而,进程不能直接访问其他进程中的共享数据。从一个进程切换到另一个进程需要一些时间(相对而言)来保存和加载寄存器、内存映射和其他资源。

拥有独立进程对用户来说很重要,因为这意味着一个进程不会损坏或破坏其他进程。如果一个单独的进程出现问题,你可以关闭那个程序并继续使用你的计算机。实际上,这意味着你可以结束一个功能失常的程序,并以最小的中断继续工作。

什么是线程?

最后一个问题是线程。线程是进程内的执行单元。一个进程可能只有一个线程,也可能有多个线程。

进程启动时,会收到内存和其他计算资源的分配。进程中的每个线程共享这些内存和资源。对于单线程进程,进程只包含一个线程。

在多线程进程中,进程包含多个线程,同时(更准确地说是"虚拟地"同时------你可以在下面关于并发的部分阅读更多相关内容)完成多项任务。

前面我们讨论了栈和堆,这是线程或进程可用的两种内存类型。区分这些内存类型很重要,因为每个线程都将拥有自己的栈。然而,一个进程中的所有线程将共享堆。

有些人将线程称为轻量级进程,因为它们有自己的栈,但可以访问共享数据。由于线程与进程和进程内的其他线程共享相同的地址空间,线程间的通信变得容易。缺点是,进程中一个功能失常的线程可能影响整个进程的可行性。

线程和进程的工作步骤

以下是你在计算机上打开应用程序时发生的事情:

  • 程序最初是编程代码的文本文件。
  • 程序被编译或解释为二进制形式。
  • 程序被加载到内存中。
  • 程序变成一个或多个正在运行的进程。进程通常相互独立。
  • 线程作为进程的子集存在。
  • 线程之间的通信比进程之间更容易。
  • 线程更容易受到同一进程中其他线程引起的问题的影响。
对比 进程 线程
定义 拥有自己内存空间的独立程序。 进程的轻量、较小单位,共享内存。
创建开销 由于有独立内存空间,开销较高。 由于共享相同的内存空间,开销较低。
隔离性 进程彼此隔离。 线程共享相同的内存空间。
资源分配 每个进程都有自己的系统资源集。 线程在同一进程中共享资源。
独立性 进程相互之间更加独立。 线程在进程内相互依赖。
故障影响 一个进程的故障不会直接影响其他进程。 一个线程的故障可能影响同一进程中的其他线程。
同步需求 由于进程是隔离的,同步需求较少。 由于资源共享,需要仔细同步。
示例用途 运行多个独立应用程序。 在单个应用程序内进行多线程处理,以实现并行性。
内存使用 通常消耗更多内存。 与进程相比,消耗较少的内存。

关于并发和并行

你可能会问,进程或线程是否可以同时运行。答案是:这取决于具体情况。在拥有多个处理器或CPU核心的环境中,同时执行多个进程或线程是可行的。然而,在单处理器系统上,真正的同时执行是不可能的。在这些情况下,会采用进程调度算法来在运行的进程或线程之间共享CPU,从而创造出并行执行的假象。每个任务被分配一个"时间片",任务之间的快速切换通常对用户来说是不可察觉的,看起来就像是同时发生的。术语"并行性"(指真正的同时执行)和"并发性"(指随时间交错进行的进程以模拟同时执行)区分了这两种操作模式,无论是真正的同时进行还是近似模拟。

  • 并发(Concurrency) :在单处理器或多处理器系统中,通过进程或线程的交错执行(即它们在不同时间点运行),来模拟多任务同时进行。在单处理器系统中,由于只能在任一时刻执行一个任务,因此并发是实现多任务处理的唯一方式。 并行(Parallelism) :在多处理器系统中,不同的进程或线程可以在相同的时间真正同时执行。这利用了多核或多处理器的硬件优势,以实现更高效的任务处理。

Google Chrome 如何使用进程和线程

为了说明进程和线程的影响,我们来考虑一个实际例子:许多人使用的程序------Google Chrome 浏览器。

在设计 Chrome 浏览器时,Google 面临了几个重要的决策。例如,Chrome 应该如何处理在使用浏览器时经常同时发生的许多不同任务的问题?每个浏览器窗口(或标签页)可能会与互联网上的多个服务器通信,下载音频、视频、文本和其他资源。此外,许多用户大多数时间都会打开 10 到 20 个(或更多)浏览器标签页,每个标签页可能执行多个任务。

Google 必须决定如何处理所有这些任务。他们选择将 Chrome 中的每个浏览器窗口作为一个独立的进程运行,而不是作为一个或多个线程。这种方法带来了几个好处。

  • 将每个窗口作为一个进程运行,可以保护整个应用程序免受程序错误和故障的影响。
  • 将 JavaScript 程序隔离在一个进程中,可以防止它占用过多的 CPU 时间和内存,导致整个浏览器变得无响应。

话虽如此,Google 的设计决策也有权衡成本。为每个浏览器窗口启动一个新进程,与使用线程相比,在内存和资源上有更高的固定成本。他们赌的是,他们的方法最终会导致整体上的内存消耗较少。

在内存较低时,使用进程而不是线程可以提供更好的内存使用效率。实际上,一个不活跃的浏览器窗口被视为较低优先级。这意味着当需要内存用于其他进程时,操作系统可能会将其交换到磁盘。如果窗口是线程化的,就更难有效分配内存,最终导致计算机性能下降。

想要了解更多关于 Google 对 Chrome 的设计决策,可以查阅 Chromium 博客Chrome 介绍漫画

下面的屏幕截图显示了在一台打开了许多标签页的 MacBook Air 上运行的 Google Chrome 进程。你可以看到,一些 Chrome 进程正在使用相当多的 CPU 时间和资源(例如,顶部的一个正在使用 44 个线程),而其他一些则使用较少。

Mac 上的"活动监视器"(或 Windows 中的"任务管理器")可以成为优化计算机性能或排查问题的有价值助手。如果你的计算机运行缓慢,或者某个程序或浏览器窗口长时间无响应,你可以使用系统监视器检查其状态。

在某些情况下,你可能会看到某个进程被标记为"无响应"。尝试退出那个进程,看看你的系统是否运行得更好。如果某个应用程序占用了大量内存,你可能会考虑选择另一个能完成相同任务的应用程序。

相关推荐
洛寒瑜11 小时前
【读书笔记-《30天自制操作系统》-23】Day24
开发语言·汇编·笔记·操作系统·应用程序
码农明明16 小时前
Android源码分析:从源头分析View事件的传递
android·操作系统·源码阅读
_小猪沉塘19 小时前
L6&7 【哈工大_操作系统】操作系统历史 &学习任务
操作系统
Freestyle Coding2 天前
使用rust自制操作系统内核
c语言·汇编·microsoft·rust·操作系统
打鱼又晒网2 天前
Linux进程间通信——探索共享内存—— 剖析原理, 学习接口应用
linux·运维·服务器·后端·操作系统
skaiuijing4 天前
巧用二级指针
c语言·开发语言·算法·架构·操作系统
打鱼又晒网5 天前
linux进程间通信——学习与应用命名管道, 日志程序的使用与实现
linux·运维·服务器·后端·操作系统
OpenAnolis小助手6 天前
专访AMD:AMD 正式加入龙蜥社区首秀:开源协作与 AI 创新的交汇点
ai·开源·操作系统·龙蜥社区·龙蜥操作系统大会
zhangxueyi6 天前
word文档转换为PPT文档最佳方案
操作系统·文档转换
洛寒瑜6 天前
【读书笔记-《30天自制操作系统》-18】Day19
c语言·开发语言·汇编·笔记·学习·操作系统·文件读取