操作系统_进程与线程(一)

目录 

1. 进程与线程

1.1 进程的概念

1.2 进程的特征

1.3 进程的状态与转换

1.4 进程的组织

1.4.1 进程控制块PCB

1.4.2 程序段

1.4.3 数据段

1.5 进程控制

1.5.1 进程的创建

1.5.2 进程的终止

1.5.3 进程的阻塞和唤醒

1.6 进程的通信

1.6.1 共享存储

1.6.2 消息传递

1.6.3 管道通信

1.7 线程和多线程模型

1.7.1 线程的基本概念

1.7.2 线程与进程的比较

1.7.3 线程的属性

1.7.4 线程的状态与转换

1.7.5 线程的组织与控制

1.7.5.1 线程控制块

1.7.5.2 线程的创建

1.7.5.3 线程的终止

1.7.6 线程的实现方式

1.7.6.1 用户级线程(ULT)

1.7.6.2 内核级线程(KLT)

1.7.6.3 组合方式

1.7.7 多线程模型


1. 进程与线程

我们学习新的知识的时候,带着问题去学习, 这样会事半功倍。

1. 为什么引入进程?

2. 什么是进程?进程由什么组成?

3. 进程是如何解决问题的?

1.1 进程的概念

        在多道程序环境下,允许多个程序并发执行,此时它们将失去封闭性,并具有间断性及不可再现性的特点。为此引入进程(Process)的概念,以便更好地描述和控制程序的并发执行,实现操作系统的并发性和共享性(最基本的两个特性)。

举个例子:

        打开电脑任务管理器:

操作系统_进程与线程(一)_第1张图片

        进程就是我们电脑此刻正在运行的应用程序;

        这时我们打开我们电脑的QQ,并且多打开几个QQ

操作系统_进程与线程(一)_第2张图片

        此时会发现:我们的进程中会多出两个 QQ.exe 进程;所以说进程简单点理解就是我们电脑此刻运行的应用程序;


程序:是静态的,就是存放在磁盘里的可执行文件,也就是一系列的指令集合。

进程:是动态的,是程序的一次执行过程(同一个程序多次执行会对应多个进程)

        通俗的理解就是:我们知道想要运行一个程序,首先需要先将程序放到内存中,然后 CPU 运行该内存,程序才会对应的执行,所以程序就是一系列存放指令的文件,是静态的,只要调用 CPU 运行在内存中的程序即可;但是进程是动态的,进程描述了程序运行的周期过程,就像上图中的QQ一样,我们运行两个QQ,两个QQ都是同一个程序,但是在进程下却显示有两个QQ的进程;

同时这里我们也要思考了?两个QQ程序都是一样的程序,那么操作系统是如何区分这两个进程的呢?

        当进程被创建时,操作系统会为该进程分配一个唯一的、不重复的 “身份证号” ——PID(Process ID,进程 ID)

操作系统_进程与线程(一)_第3张图片

        

        打开我们电脑的任务管理器,可以看到操作系统记录了 PID、进程所属用户 ID(UID)(基本的进程描述信息,可以让操作系统区分各个进程),还要记录给进程分配了哪些资源(如:分配了多少内存、正在使用哪些 I/O 设备、正在使用哪些文件)(可用于实现操作系统对资源的管理),还要记录进程的运行情况(如:CPU 使用时间、磁盘使用情况、网络流量使用情况等)(可用于实现操作系统对进程的控制、调度)

        以上的这些信息都被保存在一个数据结构 PCB 中(Process Control Block),即进程控制块

        总之,操作系统需要对各个并发运行的进程进行管理,但凡管理时所需要的信息,都会被放在 PCB 中

        PCB 是进程存在的唯一标志,当进程被创建时,操作系统会为其创建 PCB,当进程结束时,操作系统又会回收其 PCB;

        为了使参与并发执行的每个程序(含数据)都能独立地运行,必须为之配置一个专门的数据结构,称为进程控制块(Process Control Block,PCB)系统利用 PCB 来描述进程的基本情况和运行状态,进而控制和管理进程。相应地,由程序段、相关数据段和 PCB 三部分构成了进程实体(又称进程映像)所谓创建进程,实质上是创建进程实体中的 PCB;而撤销进程,实质上是撤销进程的 PCB。值得注意的是,进程映像是静态的,进程则是动态的

注意:PCB 是进程中唯一存在的标志!

        从不同的角度,进程可以有不同的定义,比较典型的定义有:

        ① 进程是程序的一次执行过程。

        ② 进程是一个程序及其数据在处理机上顺序执行时所发生的活动。

        ③ 进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。

        引入进程实体的概念以后(进程实体包括程序段、相关数据和 PCB),我们可以把传统操作系统中的进程定义为:“进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位”。

        这里的系统资源指的是:处理机、存储器和其他设备服务于某个进程的 “时间” ,理解为处理机的时间片才是准确的。进程是这些资源分配和调度的独立单位,即 “时间片” 分配的独立单位,这就决定了进程一定是一个动态的、过程性的概念。

1.2 进程的特征

        进程是由多道程序的并发执行而引出的,它和程序是两个截然不同的概念。进程的基本特征是对比单个程序的顺序执行提出的,也是对进程管理提出的基本要求。

        ① 动态性。进程是程序的一次执行过程,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态的产生、变化和消亡的。动态性是进程的最基本特征

        ② 并发性。指多个进程实体同存于内存中,能在一段时间内同时运行。引入进程的目的就是使进程能和其他进程并发执行。并发性是进程的重要特征,也是操作系统的重要特征

        ③ 独立性。指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。凡未建立 PCB 的程序,都不能作为一个独立的单位参与运行

        ④ 异步性。由于进程的相互制约,使得进程按各自独立的、不可预知的速度向前推进。异步性会导致执行结果的不可再现性,为此在操作系统中必须配置相应的进程同步机制

1.3 进程的状态与转换

        进程在其生命周期内,由于系统中各进程之间的相互制约及系统的运行环境的变换,使得进程的状态也在不断的发生变化。通常进程有以下 5 种状态,前 3 种是进程的基本状态。

        ① 运行态。进程正在处理机上运行。在单处理机中,每个时刻只有一个进程处于运行态。

        ② 就绪态进程获得除处理机外的一切所需资源,一旦得到处理机,便可立即运行。系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列

        ③ 阻塞态,又称等待态。进程正在等待某一事件而暂停运行,如等待某资源为可用(不包括处理机)或等待输入/输出完成。即使处理机空闲,该进程也不能运行。系统通常将处于阻塞态的进程也排成一个队列,甚至根据阻塞原因的不同,设置多个阻塞队列

        ④ 创建态。进程正在被创建,尚未转到就绪态。创建进程需要多个步骤:首先申请一个空白 PCB,并向 PCB 中填写用于控制和管理进程的信息;然后为该进程分配运行时所必须的资源;最后把该进程转入就绪态并插入就绪队列。但是,如果进程所需的资源尚不能得到满足,如内存不足,则创建工作尚未完成,进程此时所处的状态称为创建态

操作系统_进程与线程(一)_第4张图片

        ⑤ 结束态。进程正从系统中消失,可能是进程正常结束或其他原因退出运行。进程需要结束运行时,系统首先将该进程置为结束态,然后进一步处理资源释放和回收等工作

注意区别就绪态等待态

        就绪态是指进程仅缺少处理器,只要获得处理机资源就立刻运行;

        而等待态是指进程需要其他资源(除了处理机)或等待某一事件。之所以把处理机和其他资源划分开,是因为在分时系统的时间片轮转机制中,每个进程分到的时间片是若干毫秒。进程得到处理机的时间很短且非常频繁,进程在运行过程中实际上是频繁的转换到就绪态的;而其他资源的使用和分配(如外设)或某一事件的发生(如 I/O 操作的发生)对应的时间相对来说很长,进程转换到等待态的次数也相对较少。这样看来,就绪态和等待态是进程生命周期中两个完全不同的状态。

  •         就绪态 -> 运行态:处于就绪态的进程被调度后,获得处理机资源(分派处理机时间片),于是进程就由就绪态转换到了运行态。
  •         运行态 -> 就绪态:处于运行态的进程在时间片用完后,不得不让出处理机,从而进程由运行态转换为就绪态。此外,在可剥夺的操作系统(可剥夺的意思就是说当前进程正在运行,如果有优先级更高(原则,可按需求设定)的进程,则停止正在运行的进程,让优先级更高的进程上处理机运行;反之,不可剥夺的操作系统就是说哪怕有优先级更高的进程,也要等当前进程运行完再执行优先级更高的进程)中,当有更高优先级的进程就绪时,调度程序将正在执行的进程转换为就绪态,让更好优先级的进程先执行。
  •         运行态 -> 阻塞态:进程请求某一资源(如外设)的使用和分配或等待某一事件的发生(如 I/O 操作的完成)时,它就从运行态转换为阻塞态。说白了就是进程需要某一资源或者等待某一事件,首先需要和需要的外设产生通信,告诉外设我想要使用你的资源,这需要操作系统的介入,进程以系统调用的方式请求操作系统为其分配资源,这是一种特殊的、由运行用户态程序调用操作系统内核过程的形式。
  •         阻塞态 -> 就绪态:进程等待的事件到来时,如 I/O 操作结束或中断结束时,中断处理程序必须把相应进程的状态由阻塞态转换为就绪态。

操作系统_进程与线程(一)_第5张图片

需要注意的是:一个进程从运行态变为阻塞态是主动的行为,而从阻塞态变为就绪态是被动的行为,需要其他相关进程的协助

1.4 进程的组织

        进程是一个独立的运行单位,也是操作系统进行资源分配和调度的基本单位。它由以下三部分组成,其中最核心的是进程控制块(PCB)

1.4.1 进程控制块PCB

        进程创建时,操作系统为它新建一个 PCB,该结构之后常驻内存,任何时刻都可以存取,并在进程结束时删除。PCB 是进程实体的一部分,是进程存在的唯一标志

        进程执行时,系统通过其 PCB 了解进程的现行状态信息,以便操作系统对其进行控制和管理;进程结束时,系统收回其 PCB,该进程随之消亡。

        当操作系统欲调度某进程运行时,要从该进程的 PCB 中查出现行状态及优先级;在调度到某进程后,要根据其 PCB 中所保存的处理机状态信息,设置该进程恢复运行的现场。(所谓恢复运行的现场就是讲多个进程是根据时间片轮转进行调度的,每个时间片的时间很短,所以多个进程是不断在运行态和就绪态之间轮转的,恢复运行的现场就是说进程上一次运行结束时的状态,下一次调用该进程时直接从上一次运行结束时的状态开始运行)。并根据其 PCB 中的程序和数据的内存始址,找到其程序和数据;进程在运行过程中,当需要和与之合作的进程实现同步、通信或访问文件时,也需要访问 PCB;当进程由于某些原因而暂停运行时,又需将其断点的处理机环境保存在 PCB 中。可见,在进程的整个生命周期中,系统总是通过 PCB 对进程进行控制的,也就是系统唯有通过进程的 PCB 才能感知到该进程的存在。

        PCB 主要包括进程描述信息、进程控制和管理信息、资源分配清单和处理机相关信息等。

操作系统_进程与线程(一)_第6张图片

① 进程描述信息

        进程标识符:标志各个进程,每个进程都有一个唯一的标识号。

        用户标识符:进程归属的用户,用户标识符主要为共享和保护服务。

② 进程控制和管理信息

        进程当前状态:描述进程的状态信息,作为处理机分配调度的依据。

        进程优先级:描述进程抢占处理级的优先级,优先级高的进程可优先获得处理机。

③ 资源分配清单

        用于说明有关内存地址空间或虚拟地址空间的状况,所打开文件的列表和所使用的输入/输出设备信息。

④ 处理机相关信息,也称处理机的上下文,主要指处理机中各寄存器的值

        当进程处于执行态时,处理机的许多信息都在寄存器中。当进程被切换时,处理机状态信息都必须保存在相应的 PCB 中,以便在该进程重新执行时,能从断点继续执行。

        在一个系统中,通常存在着许多进程的 PCB,有的处于就绪态,有的处于阻塞态,而且阻塞的原因各不相同。为了方便进程的调度和管理,需要将各进程的 PCB 用适当的方法组织起来。目前,常用的组织方法有链接方式索引方式两种

        链接方式将同一状态的 PCB 链接成一个队列,不同状态对应不同的队列,也可把处于阻塞态进程的 PCB,根据其阻塞原因的不同,排成多个阻塞队列。

        索引方式将同一状态的进程组织在一个索引表中,索引表的表项指向相应的 PCB,不同状态对应不同的索引表,如就绪索引表和阻塞索引表等。

1.4.2 程序段

        程序段就是能被进程调度程序调度到 CPU 执行的程序代码段。注意,程序可被多个进程共享,即多个进程可以运行同一个程序。

1.4.3 数据段

        一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行时产生的中间或最终结果。

PCB 是给操作系统用的;

程序段数据段是给进程自己用的;

举个例子来具体的解释上述的现象:

操作系统_进程与线程(一)_第7张图片

1.5 进程控制

        进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程(说到底就是从无到有创建一个进程)撤销已有进程(实现把一个进程从有到无)实现进程状态转换等功能。(说白了,进程控制就是实现进程的状态转换)在操作系统中,一般把进程控制用的程序段称为原语,原语的特点是执行期间不允许中断,它是一个不可分割的基本单位。

1.5.1 进程的创建

        允许一个进程创建另一个进程,此时创建者称为父进程被创建的进程称为子进程。子进程可以继承父进程所拥有的资源。当子进程被撤销时,应将其从父进程那里获得的资源归还给父进程。此外,在撤销父进程时,通常也会同时撤销其所有的子进程。

        在操作系统中,终端用户登录系统、作业调度、系统提供服务、用户程序的应用请求等都会引起进程的创建。

原语:

        在操作系统中,有一种特殊的程序,它的执行具有原子性,也就是说,这段程序的运行必须一气呵成,不可以被中断。


举个例子来看为什么原语程序的运行要一气呵成?


 

操作系统_进程与线程(一)_第8张图片


接下来,我们再来看一下为什么原语这种程序可以一气呵成,不被打断?


 实际上,是用 “关中断指令” 和 “开中断指令” 这两个特权指令来实现的。
 

操作系统_进程与线程(一)_第9张图片

        操作系统创建一个新进程的过程如下(创建原语):

       为新进程分配一个唯一的进程标识号,并申请一个空白 PCB(PCB是有限的)。若 PCB 申请失败,则创建失败。

       为进程分配其运行所需的资源,如内存、文件、I/O 设备和 CPU 时间等(在 PCB 中体现)。这些资源或从操作系统中获得,或仅从其父进程获得。如果资源不足(如内存),则并不是创建失败,而是处于创建态,等待内存资源。

       初始化 PCB,主要包括初始化标志信息、初始化处理机状态信息和初始化处理机控制信息,以及设置进程的优先级等。

        若进程就绪队列能够接纳新进程,则将新进程插入就绪队列,等待被调度运行。

1.5.2 进程的终止

        引起进程终止的事件主要有:

        ① 正常结束,表示进程的任务已完成并准备退出运行。(exit 系统调用)

        ② 异常结束,表示进程在运行时,发生了某种异常事件,使程序无法继续执行,如存储区越界、保护错、非法指令、特权指令错、运行超时、算术运算错、I/O 故障等。

        ③ 外界干预,指进程应外界的请求而终止运行,如操作员或操作系统干预、父进程请求和父进程终止。(Ctrl + Alt + Delete 强制打开任务管理器)

        操作系统终止进程的过程如下(终止原语):

        根据被终止进程的标识符,检索出该进程的 PCB,从中读出该进程的状态。

       若被终止进程处于执行状态,立即终止该进程的执行,将处理机资源分配给其他进程。

       若该进程还有子孙进程,则应将其所有子孙进程终止。

        ④ 将该进程所拥有的全部资源,或归还给其父进程,或归还给操作系统。

       将该 PCB 从所在队列(链表)中删除。

1.5.3 进程的阻塞和唤醒

        正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新任务可做等,进程便通过调用阻塞原语(Block),使自己由运行态变为阻塞态。可见,阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得 CPU),才可能将其转为阻塞态

        阻塞原语的执行过程如下:

        找到将要被阻塞进程的标识号对应的 PCB。

        若该进程为运行态,则保护其现场,将其状态转为阻塞态,停止运行。

        把该 PCB 插入相应事件的等待队列,将处理机资源调度给其他就绪进程。

        当被阻塞进程所期待的事件出现时,如它所启动的 I/O 操作已完成或其所期待的数据已到达,由相关进程(比如,释放该 I/O 设备的进程,或提供数据的进程)调用唤醒原语(Wakeup),将等待该事件的进程唤醒。

        唤醒原语的执行过程如下:

        在该事件的等待队列中找到相应进程的 PCB。

        将其从等待队列中移出,并置其状态为就绪态。

        将该 PCB 插入就绪队列,等待调度程序调度。

        应该注意:Block 原语和 Wakeup 原语是一对作用刚好相反的原语,必须成对使用。如果在某进程中调用了 Block 原语,则必须在与之合作的或其他相关的进程中安排一条相应的 Wakeup 原语,以便唤醒阻塞进程;否则,阻塞进程将会因不能被唤醒而永久地处于阻塞状态。

程序是如何运行的?


 

操作系统_进程与线程(一)_第10张图片

举个例子介绍一下保存现场的意思?
 

        如果存在另外一个进程,那么另外一个进程也会使用上述一系列寄存器,那么可能会导致另一个进程的值覆盖上一个进程的值,为了防止这种现象的发生,上一个进程的时间片结束后,需要保存上一个进程的现场;

1.6 进程的通信

        进程通信是指进程之间的信息交换。PV 操作是低级通信方式,高级通信方式是指以较高的效率传输大量数据的通信方式。高级通信方法主要有以下三类: 

1.6.1 共享存储

        在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行 写/读 操作实现进程之间的信息交换

操作系统_进程与线程(一)_第11张图片

        在对共享空间进行写/读操作时,需要使用同步互斥工具(如 P 操作、V 操作),通过这些工具对共享空间进行写/读控制。共享存储分为两种

        低级方式的共享是基于数据结构的共享;共享空间只能放一个长度为10的数组。这种共享方式速度慢,限制多。

        高级方式的共享则是基于存储区的共享。操作系统在内存中划出一块共享存储区,数据的形式、存放位置都由通信进程控制,而不是操作系统。这种共享方式速度很快;

        操作系统只负责为通信进程提供可共享使用的存储空间和同步互斥工具,而数据交换则由用户自己安排读/写指令完成。

        注意:进程空间一般都是独立的,进程运行期间一般不能访问其他进程的空间,想让两个进程共享空间,必须通过特殊的系统调用实现,而进程内的线程是自然共享进程空间的。简单理解就是:甲进程和乙进程中间有一个大布袋,这个大布袋就是共享空间,甲和乙交换物品是通过大布袋来实现的,甲把物品放到大布袋里,乙可以从大布袋里拿走;但乙不能直接到甲的手中拿东西,甲也不能直接到乙的手中拿东西。

1.6.2 消息传递

        在消息传递系统中,进程间的数据交换以格式化的消息(Message)为单位。若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方法实现进程通信。

格式化消息的意思是:

操作系统_进程与线程(一)_第12张图片

        进程通过系统提供的发送消息接收消息两个原语进行数据交换。这种方式隐藏了通信实现细节,使通信过程对用户透明(简单来说就是进程双方进行通信的过程是透明的,因为在共享存储过程中,甲进程需要将资源放到共享空间中,乙进程需要从共享空间中取走甲进程的资源,所以通信的过程对用户来说并不是透明的。相反,消息传递的过程是没有中间的共享空间的),简化了通信程序的设计,是当前应用最广泛的进程间通信机制在微内核操作系统中,微内核与服务器之间的通信就采用了消息传递机制。由于该机制能很好地支持多处理机系统、分布式系统和计算机网络,因此也成为这些领域最主要的通信工具。

        ① 直接通信方式。发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列中,接收进程从消息缓冲队列中取得消息。

操作系统_进程与线程(一)_第13张图片

        ② 间接通信方式。发送进程把消息发送到某个中间实体,接收进程从中间实体中取得消息。这种中间实体一般称为信箱。该通信方式广泛应用于计算机网络中。

        简单理解就是,甲要告诉乙某些事情,那么就要通过写信的方式,然后通过邮差送给乙。直接通信就是邮差把信直接送到乙的手上;间接通信就是乙家门口有一个邮箱,邮差把信放到邮箱里。

1.6.3 管道通信

        管道通信是消息传递的一种特殊方式。所谓 “管道”是指用于连接一个读进程和一个写进程以实现它们之间的通信的一个共享文件,又叫做 pipe 文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入(写)管道;而接收管道输出的接收进程(即读进程)则从管道中接收(读)数据。为了协调双方的通信,管道机制必须提供以下三方面的协调能力:互斥、同步和确定对方的存在。

        在 Linux 中,管道是一种使用非常频繁的通信机制。从本质上讲,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题

        ① 限制管道的大小。实际上,管道是一个固定大小的缓冲区在 Linux 中,该缓冲区的大小为 4KB,这使得它的大小不像文件那样不加检验地增加。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,这种情况发生时,随后对管道的 write() 调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供 write() 调用写。

        ② 读进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的 read() 调用将默认地被阻塞,等待某些数据被写入,这就解决了 read() 调用返回文件结束的问题。

        注意:从管道读数据是一次性操作,数据一旦被读取,就释放空间以便写更多数据。管道只能采用半双工通信,即某一时刻只能单向传输。要实现父子进程互动通信,需要定义两个管道。

        管道可以理解为共享存储的优化和发展,因为在共享存储中,若某进程要访问共享存储空间,则必须没有其他进程在该共享存储空间中进行写操作,否则访问行为就会被阻塞。

        而管道通信中,存储空间进化成了缓冲区,缓冲区只允许一边写入、另一边读出,写进程往管道写数据,即便管道没被写满,只要管道没空,读进程就可以从管道中读数据。读进程从管道读数据,即使管道没被读空,只要管道没满,写进程就可以往管道写数据。当然,这也决定了管道通信必然是半双工通信

1.7 线程和多线程模型

1.7.1 线程的基本概念

首先什么是线程?为什么要引入线程?


 

操作系统_进程与线程(一)_第14张图片

 
 

操作系统_进程与线程(一)_第15张图片

        引入进程的目的是更好地使多道程序并发执行,提高资源利用率和系统吞吐量;而引入线程的目的则是减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能

        线程最直接的理解就是 “轻量级进程”,它是一个基本的 CPU 执行单元,也是程序执行流的最小单元由线程 ID、程序计数器、寄存器集合和堆栈组成线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属于一个进程的其他进程共享进程所拥有的全部资源。

        一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。

        引入线程后,进程的内涵发生了改变,进程只作为除 CPU 外的系统资源的分配单元线程则作为处理机的分配单元。由于一个进程内部有多个线程,若线程的切换发生在同一个进程内部,则只需要很少的时空开销。

1.7.2 线程与进程的比较

        ① 调度。在传统的操作系统中,拥有资源和独立调度的基本单位都是进程,每次调度都需要进行上下文切换,开销较大。在引入线程的操作系统中,线程是独立调度的基本单位,并且线程切换的代价远低于进程。在同一进程中,线程的切换不会引起进程切换。但是从一个进程的线程切换到另一个进程中的线程时,会引起进程切换。

        ② 并发性。在引入线程的操作系统中,不仅仅进程之间可以并发执行,而且一个进程中的多个线程之间也可以并发执行,甚至不同进程中的线程也能并发执行,从而使操作系统具有更好的并发性,提高了系统资源的利用率和系统的吞吐量。

        ③ 拥有资源。进程是系统中拥有资源的基本单位,而线程不拥有系统资源(仅有的一点是必不可少的、能保证独立运行的资源),但线程可以访问其隶属进程的系统资源,这主要表现在属于同一进程的所有线程都具有相同的地址空间。要知道,若线程也是拥有资源的单位,则切换线程就需要较大的时空开销,线程这个概念的提出也就没有意义。

        ④ 独立性。每个进程都拥有独立的地址空间和资源,除了共享全局变量,不允许其他进程访问。某进程中的线程对其他进程不可见。同一进程中的不同线程是为了提高并发性及进行相互之间的合作而创建的,它们共享进程的地址空间和资源。

        ⑤ 系统开销。在创建或撤销进程时,系统都要为之分配或回收进程控制块 PCB 及其他资源,如内存空间、I/O 设备等。操作系统为此所付出的开销,明显大于创建或撤销线程时的开销。类似地,在进程切换时涉及进程上下文切换,而线程切换时只需保存和设置少量寄存器内容,开销很小。此外,由于同一进程内的多个线程共享进程的地址空间,因此这些线程之间的同步与通信非常容易实现,甚至无须操作系统的干预。

系统开销:

        传统的进程间并发,需要切换进程的运行环境,系统开销很大

        线程间并发,如果是同一进程内的线程切换,则不需要切换进程环境,系统开销小

        引入线程后,并发所带来的系统开销减小


举个例子:

        去图书馆看书;

        因为你去的比较早,这个时候你单独使用一张桌子,你把你的书放到了桌子上,(现在进程就是你在图书馆看书这件事,运行环境现在就是这张桌子)有个不认识的人现在要一起使用这张桌子,这个时候你需要把你的书移到另一个地方,(现在你和共同使用这个桌子的人属于两个不同的进程,你移动书的过程其实就是在进程切换,你会发现进程切换很浪费时间)

        但是如果这个使用桌子的人是你舍友,这个时候在不影响舍友学习的情况下,你可以不移走这些书,(你和你的舍友在图书馆看书的过程就属于同一个进程下的不同线程),这个时候就不需要进行进程切换,系统开销相对就会变小;

        ⑥ 支持多处理机系统。对于传统的单线程进程,不管有多少处理机,进程只能运行在一个处理机上。而对于多线程进程,可以将进程中的多个线程分配到多个处理机上执行。

1.7.3 线程的属性

        多线程操作系统中的进程已不再是一个基本的执行实体,但它仍具有与执行相关的状态。所谓进程处于 “执行” 状态,实际上是指该进程中的某个线程正个执行。线程的主要属性如下;

        ① 线程是一个轻型实体,它不拥有系统资源,但每个线程都应有一个唯一的标识符和一个线程控制块线程控制块记录了线程执行的寄存器和栈等现场状态

        ② 不同的线程可以执行相同的程序,即同一个服务程序被不同的用户调用时,操作系统把它们创建成不同的线程。

        ③ 同一进程中的各个线程共享该进程所拥有的资源。

        ④ 线程是处理机的独立调度单位,多个线程是可以并发执行的。在单 CPU 的计算机系统中,各线程可交替地占用 CPU;在多 CPU 的计算机系统中,各线程可同时占用不同的 CPU,若各个 CPU 同时为一个进程内的各线程服务,则可缩短进程的处理时间。

        ⑤ 一个进程被创建后,便开始了它的生命周期,直至终止。线程在生命周期内会经历阻塞态、就绪态和运行态等各种状态变化。

为什么线程的提出有利于提高系统并发性?

        可以这么理解:由于有了线程,线程切换时,有可能会发生进程切换,也有可能不发生进程切换,平均而言每次切换所需的开销就变小了,因此能够让更多的线程参与并发,而不会影响到响应时间等问题。

操作系统_进程与线程(一)_第16张图片

1.7.4 线程的状态与转换

        与进程一样,各线程之间也存在共享资源和相互合作的制约关系,致使线程在运行时也具有间断性。相应的,线程在运行时也具有下面三种基本状态:

        执行状态:线程已获得处理机而正在运行。

        就绪状态:线程已具备各种执行条件,只需再获得 CPU 便可立即执行。

        阻塞状态:线程在执行中因某事件受阻而处于暂停状态。

        线程这三种基本状态之间的轮换和进程基本状态之间的轮换是一样的。

操作系统_进程与线程(一)_第17张图片

1.7.5 线程的组织与控制

1.7.5.1 线程控制块

        与进程类似,系统也为每个线程配置一个线程控制块 TCB用于记录控制和管理线程的信息

        线程控制块通常包括:

                ① 线程标识符;TID,与 PID 类似;

                一组寄存器,包括程序计数器、状态寄存器和通用寄存器;线程目前执行到哪里,线程运行的中间结果;

                ③ 线程运行状态,用于描述线程正处于何种状态;运行/就绪/阻塞;

                ④ 优先级;线程调度,资源分配的参考;

                ⑤ 线程专有存储区,线程切换时用于保存现场等;

                ⑥ 堆栈指针,用于过程调用时保存局部变量及返回地址等。堆栈保存函数调用信息、局部变量等;


线程切换时要保存/恢复的是:

        程序计数器PC、其他寄存器、堆栈指针

        也就是线程目前执行到哪里、线程运行的中间结果、堆栈保存函数调用信息、局部变量等;

        同一进程中的所有线程都完全共享进程的地址空间和全局变量。各个线程都可以访问进程地址空间的每个单元,所以一个线程可以读、写甚至清除另一个线程的堆栈。

1.7.5.2 线程的创建

        线程也是具有生命周期的它由创建而产生,由调度而执行,由终止而消亡。相应地,在操作系统中就有用于创建线程和终止线程的函数(或系统调用)。

        用户程序启动时,通常仅有一个称为 “初始化线程” 的线程正在执行,其主要功能是用于创建新线程。在创建新线程时,需要利用一个线程创建函数,并提供相应的参数,如指向线程主程序的入口指针、堆栈的大小、线程优先级等。线程创建函数执行完以后,将返回一个线程标识符

1.7.5.3 线程的终止

        当一个线程完成自己的任务后,或线程在运行中出现异常而要被强制终止时,由终止线程调用相应的函数执行终止操作但是有些线程(主要是系统线程)一旦被建立,便一直运行而不会被终止。通常,线程被终止后并不立即释放它所占有的资源,只有当进程中的其他线程执行了分离函数后,被终止线程才与资源分离,此时的资源才能被其他线程利用。

        被终止但尚未释放资源的线程仍可被其他线程调用,以使被终止线程重新恢复运行。

1.7.6 线程的实现方式

        线程的实现可以分为两类用户级线程(User - Level Thread,ULT)内核级线程(Kernel - Level Thread,KLT)内核级线程又称为内核支持的线程

1.7.6.1 用户级线程(ULT)

        在用户级线程中,有关线程管理(创建、撤销和切换等)的所有工作都由应用程序在用户空间中完成,内核意识不到线程的存在。应用程序可以通过使用线程库设计成多线程程序。通常,应用程序从单线程开始,在该线程中开始运行,在其运行的任何时刻,可以通过调用线程库中的派生例程创建一个在相同进程中运行的新线程。

操作系统_进程与线程(一)_第18张图片

操作系统_进程与线程(一)_第19张图片

        对于设置了用户级线程的系统,其调度仍是以进程为单位进行的,各个进程轮流执行一个时间片。假设进程 A 包含 1 个用户级线程,进程 B 包含 100 个用户级线程,这样,进程 A 中线程的运行时间将是进程 B 中各线程运行时间的 100 倍,因此对线程来说实质上是不公平的。

举个例子来看用户级线程的实现方式:

操作系统_进程与线程(一)_第20张图片


 

操作系统_进程与线程(一)_第21张图片

这种实现方式的优点如下:

        ① 线程切换不需要转换到内核空间,节省了模式切换的开销。

        ② 调度算法可以是进程专用的,不同的进程可根据自身的需要,对自己的线程选择不同的调度算法。

        ③ 用户级线程的实现与操作系统平台无关,对线程管理的代码是属于用户程序的一部分。

这种实现方法的缺点如下:

        ① 系统调用的阻塞问题,当线程执行一个系统调用时,不仅该线程被阻塞,而且进程内的所有线程都被阻塞。

        ② 不能发挥多处理机的优势,内核每次分配给一个进程的仅有一个 CPU,因此进程中仅有一个线程能执行。

1.7.6.2 内核级线程(KLT)

        在操作系统中,无论是系统进程还是用户进程,都是在操作系统的内核支持下运行的,与内核紧密相关。内核级线程同样也是在内核的支持下运行的,线程管理的所有工作也是在内核空间内实现的。内核空间也为每个内核级线程设置一个线程控制块,内核根据该控制块感知某线程的存在,并对其加以控制。

操作系统_进程与线程(一)_第22张图片

操作系统_进程与线程(一)_第23张图片

这种实现方式的优点如下:

        ① 能发挥多处理机的优势,内核能同时调度同一进程下的多个线程并行执行。

        ② 如果进程中的一个线程被阻塞,内核可以调度该进程中的其他线程占用处理机,也可运行其他进程中的线程。

        ③ 内核支持线程具有很小的数据结构和堆栈,线程切换比较快、开销小。

        ④ 内核本身也可采用多线程技术,可以提高系统的执行速度和效率。

这种实现方法的缺点如下:

        同一进程中的线程切换,需要从用户态转到核心态进行,系统开销较大。这是因为用户进程的线程在用户态运行,而线程调度和管理是在内核实现的。

1.7.6.3 组合方式

        有些系统使用组合方式的多线程实现。在组合实现方式中,内核支持多个内核级线程的建立、调度和管理,同时允许用户程序的建立、调度和管理用户级线程。一些内核级线程对应多个用户级线程,这是用户级线程通过时分多路复用内核级线程实现的。同一进程中的多个线程可以同时在多处理机上并行执行,且在阻塞一个线程时不需要将整个进程阻塞,所以组合方式能结合 KLT 和 ULT 的优点,并且克服各自的不足。

操作系统_进程与线程(一)_第24张图片

在线程实现方式的介绍中,提到了通过线程库来创建和管理线程。

线程库(Thread Library):是为程序员提供创建和管理线程的 API。

实现线程库的主要方法有两种:

① 在用户空间中提供一个没有内核支持的库。这种库的所有代码和数据结构都位于用户空间中。这意味着,调用库内的一个函数只能导致用户空间中的一个本地函数的调用。

② 实现由操作系统直接支持的内核级的一个库。对于这种情况,库内的代码和数据结构位于内核空间。调用库中的一个 API 函数通常会导致对内核的系统调用。

目前使用的三种主要线程库是:POSIX Pthreads、Windows API、JAVA。

        Pthreads 作为 POSIX 标准的扩展,可以提供用户级或内核级的库。

        Windows 线程库是用于 Windows 操作系统的内核级线程库。

        Java 线程 API 允许线程在 Java 程序中直接创建和管理。然而,由于 JVM 实例通常运行在宿主操作系统之上,Java 线程 API 通常采用宿主系统的线程库来实现,因此在 Windows 系统中 Java 线程通常采用 Windows API 来实现,在类 UNIX 系统中采用 Pthreads 来实现。

1.7.7 多线程模型

        有些系统同时支持用户线程和内核线程,由于用户级线程和内核级线程连接方式的不同,从而形成了下面三种不同的多线程模型:

        ① 多对一模型。将多个用户级线程映射到一个内核级线程。

操作系统_进程与线程(一)_第25张图片

        这些用户线程一般属于一个进程。线程的调度和管理在用户空间完成。仅当用户线程需要访问内核时,才将其映射到一个内核级线程上,但是每次只允许一个线程进行映射。

优点:线程管理是在用户空间进行的,因而效率比较高。

缺点:如果一个线程在访问内核时发生阻塞,则整个进程都会被阻塞;在任何时刻,只有一个线程能够访问内核,多个线程不能同时在多个处理机上运行。

操作系统_进程与线程(一)_第26张图片

        ② 一对一模型。将每个用户级线程映射到一个内核级线程。

操作系统_进程与线程(一)_第27张图片

优点:当一个线程被阻塞时,允许调度另一个线程运行,所以并发能力较强。

缺点:每创建一个用户线程,相应地就需要创建一个内核线程,开销较大。

操作系统_进程与线程(一)_第28张图片

        ③ 多对多模型。将 n 个用户线程映射到 m 个内核级线程上,要求 n\geqslant m ;

操作系统_进程与线程(一)_第29张图片

特点:既克服了多对一模型并发度不高的特点,又克服了一对一模型的一个用户进程占用太多内核级线程而开销太大的特点。

操作系统_进程与线程(一)_第30张图片

最后来解答一下本文开篇提出的问题?

1. 为什么引入进程?

        在多道程序同时运行的背景下,进程之间需要共享系统资源,因而会导致各程序在执行过程中出现相互制约的关系,程序的执行会表现出间断性的特征。这些特征都是在程序的执行过程中发生的,是动态的过程,而传统的程序本身是一组指令的集合,是一个静态的概念,无法描述程序在内存中的执行情况,即我们无法从程序的字面上看出它何时执行、何时停顿,也无法看出它与其他执行程序的关系,因此,程序这个静态的概念已不能如实反映程序并发执行过程的特征。为了深刻描述程序动态执行过程的性质乃至更好的支持和管理多道程序的并发执行,人们引入了进程的概念。

2. 什么是进程?进程由什么组成?

        进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码本身,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。

        一个进程实体由程序段、相关数据段和 PCB 三部分构成,其中 PCB 是标志一个进程存在的唯一标识,程序段是进程运行的程序的代码,数据段则存储程序运行过程中相关的一些数据。

3. 进程是如何解决问题的?

        进程把能够识别程序运行态的一些变量存放在 PCB 中,通过这些变量系统能够更好地了解进程的状况,并在适当时进行进程的切换,以避免一些资源的浪费,甚至划分为更小的调度单位——线程来提高系统的并发度。

我们用一个不太严谨的例子来类比进程:
       
我们用 “人的生命历程” 来类比进程。

        首先,人的生命历程一定是一个动态的、过程性的概念,要研究人的生命历程,先要介绍经历这个历程的主体是什么?主体当然是人。

        主体是人,相当于经历进程的主体是进程映像,人有自己的身份,相当于进程映像里的 PCB;

        人的历程会经历好几种状态:

        出生的时候、弥留的时候、充满斗志的时候、发愤图强的时候及失落的时候,相当于进程有创建、撤销、就绪、运行、阻塞等状态,这几种状态会发生改变,人会充满斗志而转向发愤图强,发愤图强获得进步之后又会充满斗志预备下一次发愤图强,或者发愤图强后遇到阻碍会进入失落状态,然后在别人的开导之下又重新充满斗志。类比进程,会由就绪态转向运行态,运行态转向就绪态,或者运行态转向阻塞态,然后在别的进程帮助下返回就绪态。

你可能感兴趣的:(操作系统,单片机,嵌入式硬件,数据结构)