进程的概念伴随着多道程序设计技术,即并发的出现而出现
进程实质上就是一个执行中的程序。教材中的定义是:进程是程序的一次执行,该进程可以和其它进程并发执行;它是一个动态的实体,是资源的基本分配单元
PCB | Process Control Block,进程控制块。包含着进程的描述和控制信息,是进程存在的唯一标志 |
---|---|
程序 | “纯代码”部分,也称为“文本段”,描述了进程要完成的功能,是进程执行时不可修改的部分 |
数据 | 进程执行时用到的数据(全局变量,静态变量,常量) |
工作区 | 即堆栈区,用于内存的动态分配,保存局部变量,传递参数、函数调用地址等 |
是操作系统用来记录进程详细状态和相关信息的基本数据结构,它和进程是一一对应的,是进程存在的唯一标识
PCB结构:
类型 | 内容 | 作用 |
---|---|---|
标识信息 | - 进程标识符(ID) - 父进程标识符(ID) - 用户标识(User ID) |
用于唯一标识一个进程 |
状态信息 | - 控制和状态寄存器:程序计数器(PC),CPU状态寄存器 - 用户可见寄存器 - 栈指针 |
用于描述进程的状态 |
控制信息 | - 调度和状态信息:进程状态,优先级,调度相关信息; - 数据结构:进程的组织方式(队列,环…) - 进程间通信:进程通信的信息,如消息队列,互斥、同步; - 进程特权; - 存储管理;虚存段表/页表指针; - 资源使用权和使用情况:进程控制资源,如打开文件等 |
用于进程的调度管理 |
- 就绪状态:进程已经具备运行的条件,等待系统分配CPU
- 执行状态:进程正在使用CPU
- 阻塞状态:进程等待某个事件发生,如等待I/O操作完成、等待某资源可用等
- 就绪状态 -> 运行状态:分配到CPU
- 运行状态 -> 就绪状态:时间片用完,或被更高优先级的进程抢占CPU
- 运行状态 -> 阻塞状态:等待某事件发生,例如需要分配资源,申请I/O操作,但系统暂时不能进行分配
- 阻塞状态 -> 就绪状态:等待的事件发生
-
系统对进程的控制通过操作系统内核中的原语实现
-
原语:由若干条指令构成的可完成特定功能的程序段,必须在管态下运行,它是一个“原子操作(atomic operation)”过程,执行过程不能被中断
-
进程控制的基本原语
- 创建进程:创建一个新进程,为其分配资源,初始化PCB,将其插入就绪队列
- 撤销进程:终止一个进程,释放其占有的资源,回收其PCB
- 阻塞进程:将一个进程由运行状态转换为阻塞状态
- 唤醒进程:将一个进程由阻塞状态转换为就绪状态
- 挂起进程:内存不足时使用
- 激活进程:解除挂起状态
面向系统性能,要满足以下条件:
- 公平性
- 较大的吞吐量
面向用户性能,满足以下条件:
- 响应时间较短
- 周转时间较短
-
先来先服务(FCFS):按照进程到达就绪队列的先后顺序进行调度,是一种非抢占式调度算法
-
时间片轮转法(RR):系统规定一个时间长度(时间片)作为允许一个进程运行的时间,如果在这段时间该进程没有执行完,则必须让出CPU给下一个进程使用,自己则排到就绪队列末尾,等待下一次调度;如果时间片结束之前被阻塞或结束,则CPU立即切换
需要考虑其时间片的设置长度,不能太长也不能太短。
-
基于优先调度算法:为系统中的每个进程规定一个优先数,就绪队列中具有最高优先数的进程有优先获得处理机的权利;如果几个进程的优先数相同,可则对它们实行RR调度策略
-
静态优先数法:进程的优先数在进程创建时就确定下来,不再改变
-
动态优先数法:进程的优先数随着时间的推移而改变
-
-
多级反馈队列调度算法(Multilevel Feedback Queue Scheduling):
- 系统中维持多个不同优先级的就绪队列。
- 每个就绪队列具有不同长度的时间片。优先级高的就绪队列里的进程,获得的时间片短;优先级低的就绪队列里的进程,获得的时间片长。
- 新进程进入时加入优先级最高的就绪队列的末尾
- 同步:需要相互合作,协同工作的进程间的关系
- 互斥:多个进程因争用临界资源而互斥执行
- 临界资源:一段时间内只允许一个进程访问的资源
解决进程互斥的软件方法:
-
Dekker算法
-
Peterson算法
-
加锁法-自旋锁(spinlock)
设置一个共享变量W (锁) ,初值为0。当一个进程想进入其临界区(使用临界资源的程序段)时,它首先测试这把锁:如果锁的值为0,则进程将其置为1并进入临界区。若锁已经为1,则进程等待直到其变成0
- 信号量(semapore):一个整形变量,通过初始化以及P、V操作来访问
- 公有信号量:用于进程间的互斥,初值通常为1
- 私有信号量:用于进程间的同步,初值通常为0或n
- P操作:荷兰语中的"proberen",意为"测试",用于申请临界资源
- 若申请成功,信号量
$s=s-1$ ,进程继续执行 - 若申请失败,进程进入阻塞状态,进入S的等待队列等待信号量变为非0
- 若申请成功,信号量
- V操作:荷兰语“verhogen”——“增量/升高”之意,意味着释放/增加一个单位资源
P、V操作通常被设置为原语操作,不可分割,不可中断
使用一个公有信号量
// 信号量初始化
semaphore S = 1;
// 进程P0
{
P(S);
outputData();
V(S);
}
// 进程P1
{
P(mutex);
outputData();
V(S);
}
使用两个私有信号量
// 信号量初始化
semaphore S0 = 0;
semaphore S1 = 0;
// 进程P0
{
inputData();
V(S1);
P(S0);
outputData();
}
// 进程P1
{
P(S1);
processData();
V(S0);
}
可以辅助练习几个经典IPC问题,加深理解
- 易读性差
- 不利于修改和维护
- 正确性难以保证
管程(monitor)是关于共享资源的一组数据结构和在这组数据结构上的一组相关操作。管程将共享变量以及对共享变量能够进行的所有操作集中在一个模块中,以过程调用的形式提供给进程使用。
实际上我觉得这个可以参考面向对象程序设计
在进程之间传输数据/交换信息,根据交换信息量的多少和效率的高低分为:
- 低级通信:只能传输状态和整数值,传输信息量小。如PV、管程
- 高级通信:能传输大批量数据,通信效率高。
系统提供了以下几种通信方式
使用一段共享内存区来实现进程间的通信,是最高效的通信方式。
发送方把一组消息通过消息缓存区发送给接收方
- 直接通信方式:进程间一对一通信
- 间接通讯方式:利用一个信箱作为媒介进行通信
管道是一种信息流缓冲机构(线性字节数组),使用文件读写方式进行访问,但管道并不是文件。通过FIFO方式传输数据。
需要注意的是:
- 通过系统调用write和read进行管道的写和读
- 管道是半双工的,数据只能在一个方向上流动。如果需要双向通信,需要建立起两个管道
- 只适用于父子进程间的通信。管道能够把信息从一个进程的地址空间拷贝到另一个进程的地址空间
这块是翻转课堂内容,直接抄当时我做的PPT了。随便看看
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务,但是每个进程至少要有一个线程
- 进程有自己独立的地址空间和资源,进程之间的切换和通信开销较大
- 进程的并发执行使得进程调度的工作量日益增大,系统效率得不到有效的提升
- 进程间的并行度达不到人们预期效果
- 提高程序的并发性,充分利用多核CPU和外围设备
- 开销小,创建和切换线程比进程快很多
- 数据通信和共享数据方便,线程之间可以直接访问共享内存,不需要调用内核
线程是为了提高程序的并发性和效率而引入的。线程可以让一个进程内的多个任务同时执行,充分利用多核CPU和外围设备,提高系统的吞吐量和响应速度。线程也可以减少进程间的切换和通信开销,提高系统的资源利用率和运行效率。线程还可以简化程序的设计和实现,方便模块化和结构化
- 二者都用于实现程序的并发执行
- 进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。
- 进程有自己独立的地址空间和资源;线程共享所属进程的地址空间和资源,但有自己的栈和寄存器,线程之间的切换和通信开销较小。
- 进程的并发性较差,一个进程崩溃可能导致整个系统崩溃;线程的并发性较好,一个线程崩溃只会影响该线程所属的进程。
- 进程可以拥有多个线程,一个进程至少有一个线程;线程不能拥有其他线程,但可以创建和销毁其他线程。
用户级线程(User-Level Thread) | 核心级线程(Kernel-Level Thread) |
---|---|
完全建立在用户空间的线程库,不需要内核的参与 | 由操作系统负责创建和销毁的线程,内核维护进程及线程的上下文信息以及线程切换 |
效率高,创建和切换速度快,资源消耗少 | 调度开销较大,从用户态到内核态的转变有代价,数量有限 |
不能利用多核 CPU 的优势,也不能避免阻塞整个进程 | 能参与全局的多核处理器资源分配,也能在一个线程阻塞时切换到另一个线程运行 |
可以为应用程序定制调度算法 | 不能定制算法 |
也有混合型的线程实现方法