2.1.1 操作系统之进程的定义、组成、特征、组织
2.1.1.1 进程的定义
先了解一下什么是程序: 程序是
静态的,存在磁盘里的可执行文件,是一系列的指令集合。
进程是动态的,是程序的一次执行过程。同一个程序多次执行会对应多个进程。
思考一下:操作系统是怎么区分这些进程的呢?
当进程被创建时,操作系统会为该进程分配一个
唯一的、不重复的PID(进程ID)。
2.1.1.2 进程的组成
程序段、数据段、PCB三部分组成了进程实体(进程映像),进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。一般情况下,我们把进程实体简称为进程。例如,所谓创建进程,实质上是创建进程实体中的PCB;而撤销进程,实质上是撤销进程实体中的PCB。
PCB是进程存在的唯一标识。
进程运行过程
PCB是给操作系统用的,程序段、数据段是给进程自己用的。
2.1.1.3 重点了解一下进程控制块PCB
2.1.1.4 进程的特征
2.1.1.5 进程的组织
在一个系统中,通常有数十、数百乃至数千个PCB。为了能对他们加以有效的管理,应该用适当的方式把这些PCB组织起来。
注: 进程的组成讨论的是一个进程内部由哪些部分构成的问题,而进程的组织讨论的是多个进程之间的组织方式问题。
- 链接方式
- 索引方式
2.1.2 操作系统之进程的状态及转换
2.1.2.1 进程的状态
在一个程序运行之前,操作系统需要完成创建进程。操作系统为该进程分配所需的内存空间等系统资源,并为其创建、初始化PCB (如为进程分配 PID)
当内存空间不够的时候,暂时调到外存等待的进程状态为挂起状态(挂起态),挂起态又进一步细分为就绪挂起、阻塞挂起两种状态。
2.1.2.2 进程5种状态之间的转换
2.1.3 进程控制
2.1.3.1 什么是进程控制以及如何用原语实现对进程的控制
进程控制是实现进程状态的转换。
原语的执行具有原子性,即执行过程只能一气呵成,期间不允许被中断。可以用“关中断指令”和“开中断指令”这两个特权指令实现原子性。
2.1.3.2 进程控制相关的5种原语
(1)进程的创建原语
(2)进程的终止原语
(3)进程的唤醒和阻塞原语
- 进程的阻塞和唤醒原语是
成对存在的,必须成对使用。 阻塞原语是由被阻塞进程自我调用实现的唤醒原语是由一个被唤醒进程合作或被其他相关的进程调用实现的
(4)进程的切换原语
我们需要知道一点:
无论哪个进程控制原语,要做的无非三类事情:
- 更新PCB中的信息
- 将PCB插入合适的队列
- 分配/回收资源
2.1.4 进程之间的通信
进程间通信(Inter-Process Communication)是指两个进程之间产生数据交互。
进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有内存地址空间相互独立;为了保证安全,一个进程不能直接访问另一个进程的地址空间。
1. 共享存储
共享存储又分为基于数据结构的共享和基于存储区的共享。
基于数据结构的共享:比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢、限制多,是一种低级通信方式基于存储区的共享:操作系统在内存中划出一块共享存储区,数据的形式、存放位置都由通信进程控制,而不是操作系统。这种共享方式速度很快,是一种高级通信方式。 注:通过“增加页表项/段表项”即可将同一片共享内存区映射到各个进程的地址空间中。
Linux中,如何实现共享内存:
int shm_open(....); //通过shm_open系统调用,申请一片内存共享区
void * mmap(....); //通过mmap系统调用,将共享内存区映射到进程自己的地址空间
2. 管道通信
- 管道只能采用
半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道。 - 各进程要
互斥地访问管道。 - 数据以字符流的形式写入管道,当管道
写满时,写进程将阻塞,直到读进程将管道中的数据取走,即可唤醒写进程;当管道读空时,读进程将阻塞,直到写进程往管道中写入数据,即可唤醒读进程。 - 数据一旦被读出,就从管道中被抛弃,这就意味着读进程最多只能有一个,否则可能会有读错数据的情况。
- 只要管道没空,读进程就可以从管道读数据,只要管道没满,写进程就可以往管道写数据。
3. 消息传递
进程间的数据交换以格式化的消息 (Message)为单位。进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换。
格式化的消息由消息头和消息体组成,消息头包括发送进程ID、接收进程ID、消息长度等格式化的信息。
- 直接通信方式
- 间接通信方式
2.1.5 操作系统之线程概念与多线程模型
2.1.5.1 线程的概念
思考一下什么是线程,有进程为什么还要引入线程?
进程是程序的一次执行。有的进程可能需要“同时”做很多事,而传统的进程只能串行地执行一系列程序。为此,引入了“线程”,来增加并发度。可以把线程理解为“轻量级进程”。线程是一个基本的CPU执行单元,也是程序执行流的最小单位。一个进程中可以有多个线程,它们共享这个进程的资源。
引入线程之后,不仅是进程之间可以并发,进程内的各线程之间也可以并发,从而进一步提升了系统的并发度,使得一个进程内也可以并发处理各种任务 (如QQ视频、文字聊天、传文件)
由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,需要较大的时空开销,限制了并发程度的进一步提高。为减少进程切换的开销,把进程作为资源分配单位和调度单位这两个属性分开处理,即进程仅作为资源分配的基本单位(如打印机、内存地址空间等都是分配给进程的),把调度执行与切换的责任交给线程,即线程成为独立调度的基本单位,它比进程更容易(更快)创建,也更容易撤销。
记住这句话!引入线程前,进程是资源分配和独立调度的基本单位。引入线程后,进程是资源分配的基本单位,线程是独立调度的基本单位。
2.1.5.2 线程的属性
2.1.5.3 线程的状态与转换
线程的状态与转换与进程的状态与转换基本上是一样的
2.1.5.4 线程的组织与控制
2.1.5.5 线程的实现方式
线程的实现分为两类:用户级线程(User-Level Thread,ULT)和内核级线程(Kernel-Level Thread, KLT)。内核级线程又称内核支持的线程。
- 用户级线程
- 内核级线程
- 将用户级线程和内核级线程组合起来
在同时支持用户级线程和内核级线程的系统中,可采用两者组合的方式:将n个用户级线程映射到m个内核级线程上(n>=m)
重点:操作系统只“看得见”内核级线程,因此只有内核级线程才是处理机分配的单位。
例如:左边这个模型中,该进程由两个内核级线程,三个用户级线程,在用户看来,这个进程中有三个线程。但即使该进程在一个4核处理机的计算机上运行,也最多只能被分配到两个核,最多只能有两个用户线程并行执行。
2.1.5.6 多线程模型
前面我们提到了线程的实现方式,有用户级和内核级。那么这两种模式的交叉组合就会产生几种不一样的组织结构,即不一样的模型。