这些内容是操作系统中相当重要的,进程(Process)和线程(Thread)要区分

1、进程的一些概念

(1)进程的定义:

狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。 

广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次动态执行过程。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。 

进程的概念主要有两点:第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。 

进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。

操作系统学习6进程和线程管理

如上图这样,一哥代码段,经过编译后形成执行文件,这个执行文件是静态的,只是放在我们的存储器中,此时他不是进程。只有操作系统执行他时候,是一个动态执行的过程,他才是进程。

(2)进程的组成:

操作系统学习6进程和线程管理

(3)程序与进程的关系

操作系统学习6进程和线程管理

操作系统学习6进程和线程管理

(3)进程的特点

操作系统学习6进程和线程管理

回顾并发、并行和同步、互斥(这四个概念都是用于形容进程之间的相互关系的,很容易混淆,要分清楚),另外还有阻塞和非阻塞(这两个概念用于形容进程的状态):

对于并行和并发:

并发是指一个处理器同时处理多个任务。时间粒度很小,看起来像是同时在处理多个任务,但是事实上一个时间点只有一个任务在执行。 

并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。 

并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。

对于同步和互斥:

现代操作系统基本都是多任务操作系统,即同时有大量可调度实体在运行。在多任务操作系统中,同时运行的多个任务可能:

  • 都需要访问/使用同一种资源
  • 多个任务之间有依赖关系,某个任务的运行依赖于另一个任务

这两种情形是多任务编程中遇到的最基本的问题,也是多任务编程中的核心问题,同步和互斥就是用于解决这两个问题的。


互斥:是指散步在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。最基本的场景就是:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。


同步:是指散步在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。最基本的场景就是:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。比如 A 任务的运行依赖于 B 任务产生的数据。


显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。也就是说互斥是两个任务之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)!因此互斥具有唯一性和排它性,但互斥并不限制任务的运行顺序,即任务是无序的,而同步的任务之间则有顺序关系。


对于阻塞和非阻塞:

正在进行的进程由于发生某事件而暂时无法继续执行时,便放弃处理机而处于暂停状态,亦即进程的执行受到阻塞,我们把这种暂停状态叫阻塞进程阻塞,有时也成为等待状态或封锁状态。

(4)进程控制块(Process Control Block,PCB)

PCB其实指的是操作系统管理控制进程运行所用的信息集合。为了对进程进行管理,操作系统为每个进程都维护了一个PCB,用来保存与该进程有关的各种状态信息。操作系统用PCB来描述进程的基本情况以及运行变化过程。PCB是进程存在的唯一标志。

 进程的创建:为该进程生成一个PCB

进程的终止:回收该进程PCB

进程的组织管理:通过对PCB组织管理来实现

操作系统学习6进程和线程管理

操作系统学习6进程和线程管理

PCB的组织方式(一般常用的是链表,因为进程需要频繁创建和删除的话,链表可以方便地进行插入等操作):

操作系统学习6进程和线程管理


(5)进程的生命周期(注意,要跟状态区分)

1)进程的生命周期:创建、运行、等待(阻塞)、唤醒、结束

创建:系统初始化时、用户请求创建一个新进程、正在执行的进程执行了创建进程的系统调用

注意,创建进程都是由操作系统最终来创建的,而且创建了新的进程后,不一定能运行。操作系统还要选择一个或者多个可以运行的进程。


运行:内核选择一个就绪的进程,让它占用处理机并且运行


等待(阻塞):请求并等待系统服务,无法马上完成、启动某种服务,无法马上完成、需要的数据没有到达

进程只能自己阻塞自己,因为只有进程自身才知道何时需要等待某种事情发生,而无法由用户或者别的进程来让该进程阻塞


唤醒:被阻塞的进程需要的资源可以被满足、被阻塞的进程等待的事件到达、将该进程的PCB插入到就绪队列

进程只能被别的进程或者操作系统唤醒


结束:正常退出(自愿)、错误退出(自愿)、致命错误(强制性)、被其他进程所杀(强制性)

(6)进程的状态(必须结合进程的生命周期来理解)一般都是三状态模型

操作系统学习6进程和线程管理

这三种是最基本的状态,也可以把创建状态和结束状态考虑进去:

操作系统学习6进程和线程管理

操作系统学习6进程和线程管理

这里注意的是,在这里并没有提到挂起态和睡眠态。这两个态网上的资料众说纷纭。有的说法是:睡眠态其实就是阻塞态。挂起的标志就是换出到外存,在外存的进程肯定是不能执行的,所以挂起的目的就很明显,在内存资源不足时,需要把一些进程换出到外存,给着急运行的进程腾地方。挂起倾向于换出阻塞态的进程,也可以是就绪态的进程。只是这个转换几乎不会采用,因为任意时刻,肯定可以找到在内存中的阻塞态进程,但也不能缺少这种直接把就绪转换到挂起的能力。除了挂起态,其他态此时都在内存中。但是作为一个进程来说,肯定不会所有页面都在内存中。所以,这里权当了解即可。

操作系统学习6进程和线程管理

在这里,阻塞挂起态,即进程在外存中并等待某事件发生

就绪挂起态,即进程在外存,但是只要进入内存,即可运行

操作系统学习6进程和线程管理

总的原则是,让系统尽可能顺畅地运行,不等待太长时间。

操作系统学习6进程和线程管理

(7)如何对进程的各种状态进行管理?很重要的要使用一个状态队列

操作系统学习6进程和线程管理

而在此状态队列中,越高优先级的越接近于被执行,越接近于处理器。

操作系统学习6进程和线程管理

2、线程(它是比进程更小的独立运行的基本单位):进程当中的一条执行流程。

实体之间可以并发执行;

实体之间共享相同的地址空间(这个跟进程不一样,进程地址空间是不同的。这里就带来了问题就是进程之间通信和线程之间通信是不太一样的,后面会说)。

(1)线程有TCB,与进程的PCB不一样。有一些各自独立的信息,也有很多共享的资源。

操作系统学习6进程和线程管理


操作系统学习6进程和线程管理

可以看出,线程之间可以共享很多资源,有同一个地址空间,但是有各自独立的一些寄存器,栈等,保证各自控制流的相对独立。

寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。在中央处理器的控制部件中,包含的寄存器有指令寄存器(IR)、程序计数器(PC)、堆栈寄存器(SP)。在中央处理器的算术及逻辑部件中,寄存器有累加器(ACC)。

(2)线程的优缺点:

操作系统学习6进程和线程管理

注意,线程是并发不是并行,因为并行是相对于多CPU的进程执行来说的,不可能会有同属一个进程的线程在两个不同CPU上运行。

(3)进程和线程的比较

操作系统学习6进程和线程管理

(4)线程的实现:

操作系统学习6进程和线程管理

1)用户线程:操作系统看不到,由应用态的应用程序的线程库管理

操作系统学习6进程和线程管理

操作系统学习6进程和线程管理

操作系统学习6进程和线程管理

2)内核线程:在内核中实现,由操作系统管理

操作系统学习6进程和线程管理

3)轻量级进程:在内核中实现,支持用户线程

操作系统学习6进程和线程管理

(5)上下文切换。一般来说,上下文切换都是对于进程来说的。

上下文切换,有时也称做进程切换、任务切换或环境切换,是指CPU 从一个进程或线程切换到另一个进程或线程。

在操作系统中,CPU切换到另一个进程需要保存当前进程的状态并恢复另一个进程的状态:当前运行任务转为就绪(或者挂起、删除)状态,另一个被选定的就绪任务成为当前任务。上下文切换包括保存当前任务的运行环境,恢复将要运行任务的运行环境。
进程上下文用进程的PCB(进程控制块,也称为PCB,即任务控制块)表示,它包括进程状态,CPU寄存器的值等。
通常通过执行一个状态保存来保存CPU当前状态,然后执行一个状态恢复重新开始运行。
支持多任务处理是CPU设计史上最大的跨越之一。在计算机中,多任务处理是指同时运行两个或多个程序。从使用者的角度来看,这看起来并不复杂或者难以实现,但是它确实是计算机设计史上一次大的飞跃。在多任务处理系统中,CPU需要处理所有程序的操作,当用户来回切换它们时,需要记录这些程序执行到哪里。上下文切换就是这样一个过程,他允许CPU记录并恢复各种正在运行程序的状态,使它能够完成切换操作。
在上下文切换过程中,CPU会停止处理当前运行的程序,并保存当前程序运行的具体位置以便之后继续运行。从这个角度来看,上下文切换有点像我们同时阅读几本书,在来回切换书本的同时我们需要记住每本书当前读到的页码。在程序中,上下文切换过程中的“页码”信息是保存在进程控制块(PCB)中的。PCB还经常被称作“切换帧”(switchframe)。“页码”信息会一直保存到CPU的内存中,直到他们被再次使用。
在三种情况下可能会发生上下文切换:中断处理,多任务处理,用户态切换。在中断处理中,其他程序”打断”了当前正在运行的程序。当CPU接收到中断请求时,会在正在运行的程序和发起中断请求的程序之间进行一次上下文切换。在多任务处理中,CPU会在不同程序之间来回切换,每个程序都有相应的处理时间片,CPU在两个时间片的间隔中进行上下文切换。对于一些操作系统,当进行用户态切换时也会进行一次上下文切换,虽然这不是必须的。
操作系统或者计算机硬件都支持上下文切换。一些现代操作系统通过系统本身来控制上下文切换,整个切换过程中并不依赖于硬件的支持,这样做可以让操作系统保存更多的上下文切换信息

操作系统学习6进程和线程管理

操作系统学习6进程和线程管理

(6)进程控制

这里的进程控制,跟前面进程的生命周期和状态转换不同,这里进程的控制是对于两个很多个进程来说的,也就是哪个进程被哪个进程取代等。

1)一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。fork函数将运行着的程序分成2个(几乎)完全一样的进程,每个进程都启动一个从代码的同一位置开始执行的线程。这两个进程中的线程继续执行,就像是两个用户同时启动了该应用程序的两个副本。

2)系统调用exec()加载程序取代当前运行的进程

fork、vfork和clone三个函数主要是Linux用来创建新的进程(线程)而设计的,exec()系列函数则是用来用指定的程序替换当前进程的所有内容。所以exec()系列函数经常在前三个函数使用之后调用,来创建一个全新的程序运行环境。Linux用init进程启动其他进程的过程一般都是这样的。          

exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,主要是在fork()之后,它会诱导fork后的子进程另起炉灶,但是PID不变,自己开创一片新天地,清空原来的堆栈区、代码区和数据区,让新的可执行程序完全替代现有的资源。换句话说,就是在调用进程内部执行一个可执行文件,这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,颇有些神似"三十六计"中的"金蝉脱壳"。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行

操作系统学习6进程和线程管理

3)wait()

操作系统学习6进程和线程管理

4)exit()

操作系统学习6进程和线程管理

如果把僵尸态作为进程的其中一个状态,则状态之间的转移图:

操作系统学习6进程和线程管理


相关文章: