万字长文带你还原进程和线程(一)

简介: 我们平常说的进程和线程更多的是基于编程语言的角度来说的,那么你真的了解什么是线程和进程吗?那么我们就从操作系统的角度来了解一下什么是进程和线程。

我们平常说的进程和线程更多的是基于编程语言的角度来说的,那么你真的了解什么是线程和进程吗?那么我们就从操作系统的角度来了解一下什么是进程和线程。

进程

操作系统中最核心的概念就是 进程,进程是对正在运行中的程序的一个抽象。操作系统的其他所有内容都是围绕着进程展开的。进程是操作系统提供的最古老也是最重要的概念之一。即使可以使用的 CPU 只有一个,但它们也支持(伪)并发操作。它们会将一个单独的 CPU 变换为多个虚拟机的 CPU。没有进程的抽象,现代操作系统将不复存在。

所有现代的计算机会在同一时刻做很多事情,过去使用计算机的人可能完全无法理解现在这种变化,举个例子更能说明这一点:首先考虑一个 Web 服务器,请求都来自于 Web 网页。当一个请求到达时,服务器会检查当前页是否在缓存中,如果是在缓存中,就直接把缓存中的内容返回。如果缓存中没有的话,那么请求就会交给磁盘来处理。但是,从 CPU 的角度来看,磁盘请求需要更长的时间,因为磁盘请求会很慢。当硬盘请求完成时,更多其他请求才会进入。如果有多个磁盘的话,可以在第一个请求完成前就可以连续的对其他磁盘发出部分或全部请求。很显然,这是一种并发现象,需要有并发控制条件来控制并发现象。

现在考虑只有一个用户的 PC。当系统启动时,许多进程也在后台启动,用户通常不知道这些进程的启动,试想一下,当你自己的计算机启动的时候,你能知道哪些进程是需要启动的么?这些后台进程可能是一个需要输入电子邮件的电子邮件进程,或者是一个计算机病毒查杀进程来周期性的更新病毒库。某个用户进程可能会在所有用户上网的时候打印文件以及刻录 CD-ROM,这些活动都需要管理。于是一个支持多进程的多道程序系统就会显得很有必要了。

在许多多道程序系统中,CPU 会在进程间快速切换,使每个程序运行几十或者几百毫秒。然而,严格意义来说,在某一个瞬间,CPU 只能运行一个进程,然而我们如果把时间定位为 1 秒内的话,它可能运行多个进程。这样就会让我们产生并行的错觉。有时候人们说的 伪并行(pseudoparallelism) 就是这种情况,以此来区分多处理器系统(该系统由两个或多个 CPU 来共享同一个物理内存)

再来详细解释一下伪并行:伪并行是指单核或多核处理器同时执行多个进程,从而使程序更快。通过以非常有限的时间间隔在程序之间快速切换CPU,因此会产生并行感。缺点是时间可能分配给下一个进程,也可能不分配给下一个进程。

我们很难对多个并行进程进行跟踪,因此,在经过多年的努力后,操作系统的设计者开发了用于描述并行的一种概念模型(顺序进程),使得并行更加容易理解和分析,对该模型的探讨,也是本篇文章的主题

进程模型

在进程模型中,所有计算机上运行的软件,通常也包括操作系统,被组织为若干顺序进程(sequential processes),简称为 进程(process) 。一个进程就是一个正在执行的程序的实例,进程也包括程序计数器、寄存器和变量的当前值。从概念上来说,每个进程都有各自的虚拟 CPU,但是实际情况是 CPU 会在各个进程之间进行来回切换。

微信图片_20220412211551.png

如上图所示,这是一个具有 4 个程序的多道处理程序,在进程不断切换的过程中,程序计数器也在不同的变化。

微信图片_20220412211556.png

在上图中,这 4 道程序被抽象为 4 个拥有各自控制流程(即每个自己的程序计数器)的进程,并且每个程序都独立的运行。当然,实际上只有一个物理程序计数器,每个程序要运行时,其逻辑程序计数器会装载到物理程序计数器中。当程序运行结束后,其物理程序计数器就会是真正的程序计数器,然后再把它放回进程的逻辑计数器中。

从下图我们可以看到,在观察足够长的一段时间后,所有的进程都运行了,但在任何一个给定的瞬间仅有一个进程真正运行

微信图片_20220412211559.png

在我们接下来的讨论中,我们假设只有一个 CPU 的情形。当然,这个假设通常放到现在不会存在了,因为新的芯片通常是多核芯片,包含 2 个、4 个或更多的 CPU。但是现在,一次只考虑一个 CPU 会便于我们分析问题。因此,当我们说一个 CPU 只能真正一次运行一个进程的时候,即使有 2 个核(或 CPU),每一个核也只能一次运行一个线程。

由于 CPU 会在各个进程之间来回快速切换,所以每个进程在 CPU 中的运行时间是无法确定的。并且当同一个进程再次在 CPU 中运行时,其在 CPU 内部的运行时间往往也是不固定的。进程和程序之间的区别是非常微妙的,但是通过一个例子可以让你加以区分:想想一位会做饭的计算机科学家正在为他的女儿制作生日蛋糕。他有做生日蛋糕的食谱,厨房里有所需的原谅:面粉、鸡蛋、糖、香草汁等。在这个比喻中,做蛋糕的食谱就是程序、计算机科学家就是 CPU、而做蛋糕的各种原谅都是输入数据。进程就是科学家阅读食谱、取来各种原料以及烘焙蛋糕等一系例了动作的总和。

现在假设科学家的儿子跑过来告诉他,说他的头被蜜蜂蜇了一下,那么此时科学家会记录出来他做蛋糕这个过程到了哪一步,然后拿出急救手册,按照上面的步骤给他儿子实施救助。这里,会涉及到进程之间的切换,科学家(CPU)会从做蛋糕(进程)切换到实施医疗救助(另一个进程)。等待伤口处理完毕后,科学家会回到刚刚记录做蛋糕的那一步,继续制作。

这里的关键思想是认识到一个进程所需的条件,进程是某一类特定活动的总和,它有程序、输入输出以及状态。单个处理器可以被若干进程共享,它使用某种调度算法决定何时停止一个进程的工作,并转而为另外一个进程提供服务。另外需要注意的是,如果一个进程运行了两遍,则被认为是两个进程。

进程的创建

操作系统需要一些方式来创建进程。在非常简单的系统中,或者操作系统被设计用来运行单个应用程序(例如微波炉中的控制器),可能在系统启动时,也需要所有的进程一起启动。但在通用系统中,然而,需要有某种方法在运行时按需创建或销毁进程,现在需要考察这个问题,下面是创建进程的方式

  • 系统初始化
  • 正在运行的程序执行了创建进程的系统调用(比如 fork)
  • 用户请求创建一个新进程
  • 初始化一个批处理工作

启动操作系统时,通常会创建若干个进程。其中有些是前台进程(numerous processes),也就是同用户进行交互并替他们完成工作的进程。一些运行在后台,并不与特定的用户进行交互,但是后台进程也有特定的功能。例如,设计一个后台进程来接收发来的电子邮件,这个进程大部分的时间都在休眠但是只要邮件到来后这个进程就会被唤醒。还可以设计一个后台进程来接收对该计算机上网页的传入请求,在请求到达的进程唤醒来处理网页的传入请求。进程运行在后台用来处理一些活动像是 e-mail,web 网页,新闻,打印等等被称为 守护进程(daemons)。大型系统会有很多守护进程。在 UNIX 中,ps 程序可以列出正在运行的进程, 在 Windows 中,可以使用任务管理器。

除了在启动阶段创建进程之外,一些新的进程也可以在后面创建。通常,一个正在运行的进程会发出系统调用以创建一个或多个新进程来帮助其完成工作。当可以轻松地根据几个相关但相互独立的交互过程来共同完成一项工作时,创建新进程就显得特别有用。例如,如果有大量的数据需要经过网络调取并进行顺序处理,那么创建一个进程读数据,并把数据放到共享缓冲区中,而让第二个进程取走并正确处理会比较容易些。在多处理器中,让每个进程运行在不同的 CPU 上也可以使工作做的更快。

在许多交互式系统中,输入一个命令或者双击图标就可以启动程序,以上任意一种操作都可以选择开启一个新的进程,在基本的 UNIX 系统中运行 X,新进程将接管启动它的窗口。在 Windows 中启动进程时,它一般没有窗口,但是它可以创建一个或多个窗口。每个窗口都可以运行进程。通过鼠标或者命令就可以切换窗口并与进程进行交互。

交互式系统是以人与计算机之间大量交互为特征的计算机系统,比如游戏、web浏览器,IDE 等集成开发环境。

最后一种创建进程的情形会在大型机的批处理系统中应用。用户在这种系统中提交批处理作业。当操作系统决定它有资源来运行另一个任务时,它将创建一个新进程并从其中的输入队列中运行下一个作业。

从技术上讲,在所有这些情况下,让现有流程执行流程是通过创建系统调用来创建新流程的。该进程可能是正在运行的用户进程,是从键盘或鼠标调用的系统进程或批处理程序。这些就是系统调用创建新进程的过程。该系统调用告诉操作系统创建一个新进程,并直接或间接指示在其中运行哪个程序。

在 UNIX 中,这仅有一个系统调用来创建一个新的进程,这个系统调用就是 fork。这个调用会创建一个与调用进程相关的副本。在 fork 后,一个父进程和子进程会有相同的内存映像,相同的环境字符串和相同的打开文件。通常,子进程会执行 execve 或者一个简单的系统调用来改变内存映像并运行一个新的程序。例如,当一个用户在 shell 中输出 sort 命令时,shell 会 fork 一个子进程然后子进程去执行 sort 命令。这两步过程的原因是允许子进程在 fork 之后但在 execve 之前操作其文件描述符,以完成标准输入,标准输出和标准错误的重定向。

在 Windows 中,情况正相反,一个简单的 Win32 功能调用 CreateProcess,会处理流程创建并将正确的程序加载到新的进程中。这个调用会有 10 个参数,包括了需要执行的程序、输入给程序的命令行参数、各种安全属性、有关打开的文件是否继承控制位、优先级信息、进程所需要创建的窗口规格以及指向一个结构的指针,在该结构中新创建进程的信息被返回给调用者。除了 CreateProcess Win 32 中大概有 100 个其他的函数用于处理进程的管理,同步以及相关的事务。下面是 UNIX 操作系统和 Windows 操作系统系统调用的对比

微信图片_20220412211605.jpg

在 UNIX 和 Windows 中,进程创建之后,父进程和子进程有各自不同的地址空间。如果其中某个进程在其地址空间中修改了一个词,这个修改将对另一个进程不可见。在 UNIX 中,子进程的地址空间是父进程的一个拷贝,但是确是两个不同的地址空间;不可写的内存区域是共享的。某些 UNIX 实现是正文区在两者之间共享,因为它不能被修改。或者,子进程共享父进程的所有内存,但是这种情况下内存通过 写时复制(copy-on-write) 共享,这意味着一旦两者之一想要修改部分内存,则这块内存首先被明确的复制,以确保修改发生在私有内存区域。再次强调,可写的内存是不能被共享的。但是,对于一个新创建的进程来说,确实有可能共享创建者的资源,比如可以共享打开的文件呢。在 Windows 中,从一开始父进程的地址空间和子进程的地址空间就是不同的。

相关文章
|
27天前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第16天】进程、线程和协程是计算机程序执行的三种基本形式。进程是操作系统资源分配和调度的基本单位,具有独立的内存空间,稳定性高但资源消耗大。线程是进程内的执行单元,共享内存,轻量级且并发性好,但同步复杂。协程是用户态的轻量级调度单位,适用于高并发和IO密集型任务,资源消耗最小,但不支持多核并行。
40 1
|
6天前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
10天前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
7天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
18 1
|
13天前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
28 2
|
14天前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
25天前
|
Python
Python中的多线程与多进程
本文将探讨Python中多线程和多进程的基本概念、使用场景以及实现方式。通过对比分析,我们将了解何时使用多线程或多进程更为合适,并提供一些实用的代码示例来帮助读者更好地理解这两种并发编程技术。
|
27天前
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第15天】进程、线程和协程是操作系统中三种不同的执行单元。进程是资源分配和调度的基本单位,每个进程有独立的内存空间;线程是进程内的执行路径,共享进程资源,切换成本较低;协程则更轻量,由用户态调度,适合处理高并发和IO密集型任务。进程提供高隔离性和安全性,线程支持高并发,协程则在资源消耗和调度灵活性方面表现优异。
44 2
|
1月前
|
算法 安全 调度
深入理解操作系统:进程与线程的管理
【10月更文挑战第9天】在数字世界的心脏跳动着的,不是别的,正是操作系统。它如同一位无形的指挥家,协调着硬件与软件的和谐合作。本文将揭开操作系统中进程与线程管理的神秘面纱,通过浅显易懂的语言和生动的比喻,带你走进这一复杂而又精妙的世界。我们将从进程的诞生讲起,探索线程的微妙关系,直至深入内核,理解调度算法的智慧。让我们一起跟随代码的脚步,解锁操作系统的更多秘密。
37 1
|
19天前
|
Linux 调度
探索操作系统核心:进程与线程管理
【10月更文挑战第24天】在数字世界的心脏,操作系统扮演着至关重要的角色。它不仅是计算机硬件与软件之间的桥梁,更是管理和调度资源的大管家。本文将深入探讨操作系统的两大基石——进程与线程,揭示它们如何协同工作以确保系统运行得井井有条。通过深入浅出的解释和直观的代码示例,我们将一起解锁操作系统的管理奥秘,理解其对计算任务高效执行的影响。

热门文章

最新文章

相关实验场景

更多