博客主页:🏆看看是李XX还是李歘歘 🏆
🌺每天不定期分享一些包括但不限于计算机基础、算法、后端开发相关的知识点,以及职场小菜鸡的生活。🌺
💗点关注不迷路,总有一些📖知识点📖是你想要的💗
⛽️今天的内容是6000字的进程相关的知识点,篇幅较长,建议先收藏后阅读⛽️💻💻💻
目录
进程
1.进程的概念
为了更好的描述和控制程序的并发,实现操作系统的并发性和共享性,引入了进程的概念,为每一个进程分配一个专门的数据结构:进程控制块PCB(常驻内存),保存运行期间进程的数据,PCB 是进程存在的唯一标志。
进程是系统进行资源分配的单位,且每个进程拥有独立的地址空间。(即进程是资源分配和调度的一个独立单元)
2.进程的特征
动态性(进程状态在动态变化),并发性(多个进程实体同时存在于内存中),独立性(有独立的资源和调度),异步性(进程按照各自独立的、不可预知的速度向前推进,异步会导致执行结果不可再现,所以需要配置相应的同步机制),结构性(进程由程序段、数据段和PCB组成)。
3.进程的组成部分
程序段
- 代码数据,能被进程调度程序调度到CPU执行的程序代码。注意,程序可被多个进程共享。
数据段
- 与程序逻辑本身相关的数据,可以是进程对应的程序加工处理的原始数括,也可以是程序执行时产生的中间或最终结果。
PCB
- 进程描述信息:进程标识符(PID)--标志各个进程;用户标识符(UID)--标志进程所属用户
- 进程控制和管理信息:进程当前状态,进程优先级,处理机占用时间等
- 资源分配清单:各种堆栈指针和代码段、数据段指针,
- 处理机相关信息:处理机各种寄存器的值
就绪队列、阻塞队列等队列组织方式:
- 链接方式将同一状态的PCB链接成一个队列,不同状态对应不同的队列,也可把处于阻塞态的进程的PCB,根据其阻塞原因的不同,排成多个阻塞队列。
- 索引方式将同一状态的进程组织在一个索引表中,索引表的表项指向相应的PCB,不同状态对应不同的索引表,如就绪索引表和阻塞索引表等。
4.进程的状态及其转化关系
进程具备五种状态,分别为:
-
创建状态:进程正在被创建,尚未转到就绪状态,创建进程需要申请一个空白的PCB,并向 PCB写一些控制和管理进程的信息,然后由系统分配资源,将进程转入就绪状态。
-
就绪状态:进程已处于准备执行的状态,获得了除处理机以外的一切所需资源。
-
执行状态:进程在处理机上运行。在单处理机环境下,每一时刻最多只有一个进程运行。
-
阻塞状态:进程正在等待某一事件(服务请求)而暂停运行,如等待某资源变为可用(不包括处理机)或等待输入输出 I/O 完成,即使处理机空闲,该进程也不能运行。
-
结束状态:进程正从系统中消失,这可能是进程正常结束或其他原因中断退出运行,当进程需要结束运行时,系统首先必须置该进程为结束状态,然后再进一步处理资源释放和回收。
注意:
- 就绪队列在内存中,阻塞队列在外存。
- 执行态变为阻塞态是主动的行为,自身进程需要的东西在忙,主动的去申请和退出(资源);阻塞态变为就绪态为被动的行为,需要其他相关进程的协助,被动等待资源的释放。
状态变化触发机制:
- 从运行态到阻塞态---进程的资源不够时
- 从运行态到就绪态---进程的时间片用完了,进程需要重新等待cpu的使用。
- 从就绪态到运行态---获得了处理机的资源
- 从阻塞态到就绪态---所需资源准备好了
5.进程的切换
进程的切换是指处理机从一个进程的运行转到另一个进程上运行,在这个过程中,进程的运行环境产生实质性的变化。过程如下:
- 保存处理机上下文,包括程序计数器和其他寄存器。
- 更新PCB信息。
- 把进程的PCB移入相应的队列,如就绪队列、等待某资源释放的阻塞队列等。
- 选择另一个程序执行,并更新其PCB。
- 更新内存管理的数据结构。
- 恢复处理机上下文。
"调度"和“切换”的区别:
调度是指决定资源分配给哪个进程的行为,是一种决策行为;切换是指实际分配的行为,是执行行为。一般来说,先有资源调度,然后才有进程的切换。
6.进程间的通信
根据进程间交换信息量的多少和效率的高低,进程通信分为如下低级通信和高级通信。
-
低级通信:只能传递状态和整数值(控制信息)。(如 同步互斥工具:PV 操作)
由于进程的互斥和同步,需要在进程间交换一定的信息,故不少学者将它们也归为进程通信。
-
特点:传送信息量小,效率低,每次通信传递的信息量固定,若传递较多信息则需要进行多次通信。
-
编程复杂:用户直接实现通信的细节,容易出错。
-
-
高级通信:提高信号通信的效率,传递大量数据,减轻程序编制的复杂度,包括三种方式:
-
共享内存
-
消息传递
-
管道通信
-
共享内存
在通信进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行写/读操作,实现进程之间的信息交换。
在对共享空间进行写/读操作时,需要同步互斥工具(如 P操作、V操作),对共享空间的写/读进行控制。
共享存储又分为两种:
- 低级方式的共享是基于效据结构的共享;
- 高级方式的共享则是基于存储区的共享。
操作系统只负贵为通信进程提供可共享使用的存储空问和同步互斥工具,而数据交换则由用户自己安排读/写指令完成。
消息传递
进程间的数据交换是以格式化的消息( Message)为单位的,进程通过系统提供的发送消息和接收消息两个原语进行数据交换。
- 直接通信方式:发送进程直接把消息发送给接收进程,并将它挂在接受进程的消息缓冲队列上,接收进程从消息缓冲队列中取出消息。
- 间接通信方式:发送进程把消息发送到一个中间实体,接收进程从中间实体,接收进程从中间实体取得消息。这个中间实体一般称为信箱,这种通信方式又称为信箱通信方式,是电子邮件系统的实现方式。
消息队列就是使用消息传递的典型示例,就是一个消息的链表,是一系列保存在内核中消息的列表。用户进程可以向消息队列尾部添加消息,也可以向消息队列头部读取消息,消息一旦被接收,就会从队列中删除
主要包括:创建或打开消息队列,添加消息,读取消息和控制消息队列。
管道通信
所谓的”管道”,是指用于连接一个读进程和一个写进程以实现它们之间的通信的一个共享文件,又名pipe文件。
- 向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入(写)管道。
- 而接收管道输出的接收进程(即读进程)则从管道中接收(读)数据。
为了确保通信的正常进行,管道机制必须提供互斥、同步和确定对方存在三方面的协调能力;而且从管道读数据是一次性操作,数据一旦被读取,它就从管道中被抛弃,释放空间以便写更多的数据。
管道只能釆用半双工通信,即某一时刻只能单向传输。要实现父子进程双方互动通信,需要定义两个管道。
linux中使用的方式就是管道通信,同时,由unix兴起的socket也使用的是管道。
套接字Socket
主要用在不同机器上的进程间通信,参考我的另一篇文章:
注意:共享内存比管道通信和消息队列效率高。
因为所有进程共享同一块内存,共享内存在各种进程间通信方式中具有最高的效率。访问共享内存区域和访问进程独有的内存区域一样快,并不需要通过通过系统调用或者需要切入内核的过程来完成。同时它也避免了对数据的各种不必要的复制。
消息队列和管道通信的消息传递方式过程如下:
- 写进程将数据写入管道/消息队列
- 数据由写进程拷贝到内核。
- 读进程将数据从内核拷贝到读进程中
- 读进程将数据读出管道/消息队列
上述过程通常要经过4次拷贝,才能完成文件的传递。
共享内存消息传递方式过程如下:
- 写入数据到共享内存区
- 从共享内存区读出数据
共享内存消息传递过程不涉及到内核的拷贝,所以花的时间较少,但是进程需要提供同步策略(常用方法是通过使用信号量进行同步),消息队列和管道通信由内核负责同步。
线程
线程的概念
线程是“轻量级进程”,它是基本的CPU执行单元,也是程序执行流的最小单元,由线程ID、程序寄存器、寄存器集合和堆栈组成(即 CPU 调度的基本单元)。线程控制块(Thread Control Block, TCB):保存运行期间线程的数据,TCB 是线程存在的唯一标志。
线程的属性
-
线程属于进程,是进程的一个实体,是被系统独立和分配的基本单位。
-
线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,同一进程中的各个线程共享该进程的所有资源。
-
一个进程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
线程的分类
用户级线程:
优点:用户线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高。
缺点:当一个用户级线程被阻塞后,整个进程都被阻塞,并发能力较弱,多个线程不可在多核处理机上并行运行。
内核级线程:
优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
缺点:一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。
多线程模型
- 多对一模型:将多个用户级线程映射到一个内核级线程,线程管理在用户空间完成。
- 一对一模型:将每个用户级线程映射到一个内核线程。
- 多对多模型:将n个用户级线程映射到m个内核级线程上,要求m <= n;
在多对一的线程模型中,当一个多线程进程中的某个线程中的某个线程被阻塞后,整个进程都将被阻塞。
为什么进程间的通信必须要借助OS内核功能?
因为进程都有自己的独立地址空间,进程无法访问其他进程的地址空间,所以必须借助于操作系统调用函数实现。
进程和线程的关系
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
- 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
- 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
- 处理机分给线程,即真正在处理机上运行的是线程。
- 线程是进程内的一个执行单元,也是进程内的可调度实体。
线程与进程的区别
- 调度:进程是资源分配和调度的一个独立单元;而线程是 CPU 调度的基本单元。
- 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。线程并发执行过程中要进行同步和互斥,因为它们共享同一进程的所有资源。
- 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。并且线程共享整个进程的资源(寄存器、堆栈、上下文)
- 系统开销:线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多,所以进程在切换和创建销毁时,耗费的资源较大,系统开销较大。
- 健全性:进程有独立的地址空间,进程崩溃后,在保护模式下不会对其他的进程产生影响;线程有自己的堆栈和局部变量,但没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮。
- 创建和销毁:进程的创建调用 fork 或者 vfork,而线程的创建调用 pthread_create;进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束。
处理机的调度
调度分级
- 作业调度:又称为高级调度,主要指的是从外存上的后备状态的作业中挑选一个作业,给它们分配内存、输入/输出设备等必要的资源,并建立相应的进程,以使它获得竞争处理机的权利。
- 中级调度:又称为内存调度,按照某种策略决定将那个处于挂起状态的进程重新调入内存。(从外存到内存的)
- 进程调度:又称为低级调度,其主要任务是按照某种方法和策略从就绪队列中选取一个进程,将处理机分配给它。(处理机的使用)
发生频率:高级调度 < 中级调度 < 低级调度
调度方式
非抢占方式/非剥夺调度方式:(适用于批处理系统)
抢占方式/剥夺调度方式:适用于大多数分时或者实时系统
调度算法
先来先服务(FCFS):选择最先进入队列的
短作业优先:选择完成时间最短的
优先级调度:选择优先级别最高的
高响应比优先:选择响应比最高的
时间片轮转:总是选择就绪队列中第一个进程,但仅能运行一个时间片
多级反馈队列:时间片轮转调度算法和优先级调度算法的综合和发展
进程同步
临界资源:一次仅允许一个进程使用的资源
同步遵循的准则
- 空闲让进:临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区。
- 忙则等待:当已有进程进入临界区时,其他试图进入临界区的进程必须等待。
- 有限等待:对请求访问的进程,应保证能在有限时间内进入临界区。
- 让权等待:当进程不能进入临界区时,应立即释放处理器,防止进程忙等待。
实现临界区互斥的基本方法
软件实现
- 单标志法:该算法设置一个公用整型变量turn。用于指示被允许进入临界区的进程编号,若turn = 0,则允许P0进程进入临界区。只能交替使用临界资源,不遵循”空闲让进“原则,可以”实现同一时刻最多只允许一个进程访问临界区”。
- 双标志法先检查:该算法的基本思想是在每个进程访问临界区资源之前,先查看临界资源是含正被访问,若正被访问,该进程等待;否则,进程才进入临界区。设置一个数据flag[i],如第i个元素值为FALSE,表示Pi进程未进入临界区,若值为TRUE,表示Pi进程进入临界区。不遵循“忙则等待”的原则。
- 双标志法后检查:其将自己的标志设置为TRUE,在检测对方的状态标志,若对方标志为TRUE,则进程等待;否则进入临界区。不遵循空闲让进,有限等待。
- 皮特森算法:为了防止两个进程进入临界区而无限期等待。又设置了变量tum,每个进程在先设置自己的标志后再设置turn标志。这时,再同时检测另一个进程状态标志和不允许进入标志,以便保证两个进程同时要求进入临界区时,只允许一进程进入临界区。不遵循让权等待。
除了软件实现方式还有硬件实现和信号量(PV)
死锁
死锁概念
死锁:是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程将无法向前推进。
饥饿:由长期得不到想要的资源,某进程无法向前推进的现象。
死循环:某进程执行过程中一直跳不出某个循环的现象。
相同点
都是进程无法顺利向前推进的现象(故意设计的死循环除外)。
区别
死锁的发生至少要有两个或两个以上的进程
饥饿的发生可能只有一个进程
死锁和饥饿是管理者(OS)的问题,死循环是被管理者的问题
死锁产生的必要条件
-
互斥条件:一段时间进程独占一个资源。
-
不剥夺条件:已获资源不能被其他进程强行夺走。
-
请求并保持条件:进程已经获得资源,又发出新的请求,但是另一些资源已经被其他进程占用,则当前已经获得的资源保持住,不释放。
-
循环等待条件:存在一个循环等待链,链中进程已获资源同时为链中下一进程所请求。
死锁的处理逻辑
- 死锁预防:设置某些限制条件,破坏产生死锁的四个必要条件中的一个或几个,以防止发生死锁。(有一些资源必须互斥使用,无法剥夺互斥条件)
- 避免死锁:找到一个资源分配的序列,能使得所有进程都顺利完成,用某种方法防止系统进入不安全状态,从而避免死锁,最出名的是银行家算法。
- 死锁的检测与解除,无须采取任何限制性措施,允许进程在运行过程中发生死锁。通过系统的检测机构及时地检测出死锁的发生,然后采取某种措施解除死锁。