一、定位
在计算机系统中,操作系统是其中的重要一环。对上,给软件提供稳定的运行环境;对下,管理着各种硬件设备。总的来说,操作系统是一个非常复杂的软件,本章我们只讨论其中一个非常重要的模块——进程管理。
二、什么是进程?
进程(process)又叫做任务(task)。其中在操作系统一书中提出这样的概念:每个应用程序运行于现代操作系统之上时,操作系统会提供一种抽象,好像系统上只有这个程序在运行,所有的硬件资源都被这个程序在使用。进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程;
看完定义,是不是更懵了呢?其实也没那么复杂,我们将上述定义简化,其实进程就是操作系统对运行程序的一种抽象,一个运行起来的程序就是进程(process)/任务(task)。在我们的计算机上有各种各样的应用程序,当我们运行程序,就会在系统中形成一个进程,我们可以通过任务管理器查看当前系统里的进程:
注意:在操作系统内部,进程又是操作系统进行资源分配的基本单位。
三、进程管理
正如上图,我们的系统中运行着很多进程,为了帮助操作系统实现资源的分配、调度和管理,并保持计算机系统的稳定性和安全性,操作系统需要进行进程管理。
在操作系统中,将进程抽象为了一个结构体——进程控制块抽象(PCB Process Control Block),每一个 PCB 对象都是一个进程。操作系统再通过数据结构将PCB对象组织起来,方便进行管理。
1、PCB相关属性(了解)
有关系统资源的属性:
(1)进程标识符pid: 用于标识一个进程的唯一编号。
(2)内存指针:标记当前进程使用的内存的位置(进程跑起来就需要消耗一定的资源,内存指针标识着进程运行的时候使用了哪些内存上的资源)
(3)文件描述表:硬盘上存储的数据往往以文件为单位进行整理。进程每次打开一个文件,就会产生一个文件描述符(标识这个被打开的文件)一个进程可能会打开多个文件,对应一组文件描述符。把这些文件描述符放到一个顺序表这样的结构里就构成了文件描述表。
进程调度的属性:
(1)进程状态
就绪状态(Ready):当进程已经完成了初始化,并且等待分配CPU时间片时,进程处于就绪状态。此时进程已经处于可运行状态,只需要等待CPU的调度即可执行。
运行状态(Running):当进程正在执行指令并占用CPU资源时,进程处于运行状态。此时进程处于执行态,正在执行程序代码。
阻塞状态(Blocked):当进程因等待某个事件的发生而被挂起时,进程处于阻塞状态。例如,等待IO请求完成或者等待某些资源释放,此时进程无法占用CPU资源而被挂起。
创建状态(New):当操作系统为进程分配所需的资源并执行初始化时,进程处于创建状态。在该状态下,进程尚未分配到CPU时间片,也还未被加载到内存中执行。
终止状态(Terminated):当进程执行完毕或者出现致命错误而被强制终止时,进程处于终止状态。在该状态下,操作系统将释放该进程占用的所有资源,包括内存、文件句柄等等。
(2)进程的优先级
进程之间的调度就有一定的优先级。优先级高的先调度。
(3)进程的上下文
上下文就是描述了当前进程执行到哪里这样的存档记录,进程在离开CPU的时候就要把当前运行的中间结果存档。等到下次进程回到cpu上,再恢复之前的存档,从上次结果继续往后执行。
进程的上下文就是指进程运行过程中,CPU内部一系列寄存器的值。 寄存器有很多种,其中最典型的作用就是保存当前进程执行的中间结果,包括进程运行到那一条指令,进程离开CPU就需要把这些寄存器的值保存到PCB的上下文字段中,进程下次回来,再把PCB中的值恢复到寄存器中。
(4)进程的记账信息
统计了每个进程在CPU上执行了多久,可以作为调度的参考依据。
2、多任务的处理方式
概念补充:这里的CPU核心数指的是逻辑核心。例如我的电脑是8内核和16线程处理器,即8个物理核心,16个逻辑核心。
并行
同一时刻,多个核心,同时执行多个进程,此时这这多个进程就是并行执行。
并发
对于单核CPU来说,在同一时间只能执行一个进程的代码,所以在单核CPU上实现多进程,是通过CPU快速的切换不同进程,看上去就像是多个进程在同时进行。例如一个核心,先执行进程1,执行一会儿后,再去执行进程2,再执行一会儿后,再去执行进程3,此时只要这里的切换速度足够快,看起来1、2、3就是同时执行的。
这就是为什么虽然我的电脑CPU只有16个核心,却可以同时执行这么多的任务,原因就是通过并发+并行的方式来完成的。当操作系统对进程的调度足够快,看起来就是同时的。
为了实现多个任务能够同时执行,所以系统一般采用并发+并行的方式进行,这个过程是系统自身控制的,我们一般感知不到。很多时候我们将并行+并发统称为并发。
四、什么是线程?
定义:
线程是“更轻量”的进程,一个进程中可以包含多个线程,每个线程都是一个独立可以调度执行的“执行流”,这些执行流之间本身就是并发的。
单核CPU的发展遇到了瓶颈,要想提高算力, 就需要多核 CPU,而并发编程能更充分利用多核 CPU资源。虽然多进程编程和多线程编程都能满足“并发编程”的需求,但是我们更鼓励使用多线程编程,至于为什么?下面我们来详细讨论讨论:
在定义中已经提到,线程是“轻量级”的进程。也就是说相比于线程,进程更“重量”,那么这个重量具体体现在哪里呢?
(1)创建进程比创建线程更慢。
(2)销毁进程比销毁线程更慢。
(3)调度进程比调度线程更慢。
造成以上问题的原因主要体现在资源分配上,我们上面提到,进程是系统进行资源分配的基本单位,而资源分配往往又是一个非常耗时的操作,就单拿内存分配为例,系统要为进程分配一块内存,就需要遍历空闲的内存,找到一块合适的内存进行分配。如果多个进程都在申请内存,那么系统进行内存分配的时候就得一个一个来。而对于线程而言,进程包含线程,意味着在创建线程的时侯,系统资源是已经分配好了的,这也就为线程省下了分配资源的开销,因此多线程编程才是更优的选择。
我们可以将上述过程形象化为工厂和车间,如果一个工厂要扩大生产,可以采用如下两种方式:
- 通过增开分工厂的方式
- 通过增加新车间的方式
线程特点:
- 线程是操作系统调度执行的基本单位。
- 每个线程都是一个独立的执行流,多个线程之间,也是并发执行的。
- 多个线程可能是在多个CPU核心上同时运行(并行),也可能是在一个CPU核心上,通过快速切换进行运行(并发)。
总结
进程和线程的区别:
- 进程包含线程,一个进程里可以有一个或多个线程。
- 进程有自己独立的系统资源(内存空间和文件描述符表),同一个进程中的多个线程之间共享一份系统资源(地址空间和文件描述符表)。
- 进程是操作系统资源分配的基本单位,线程是操作系统调度执行的基本单位。
- 进程之间具有独立性,一个进程挂了,不会影响到别的进程。同一个进程里的多个线程之间一个线程挂了,可能会把整个进程带走,影响到其他线程。
线程的优势:
进程比较重量,即如果频繁地创建/销毁进程(资源分配),成本较高。而对于多线程,只有在进程启动,创建第一个线程的时候,需要花成本去申请系统资源,一旦进程创建完毕,此时后续再创建的线程就不必在申请资源了。创建/销毁的效率就提高了不少。所以鼓励采用多线程编程。