操作系统核心功能(二)

发布于 2021-09-21  63 次阅读


进程管理

啥是进程

操作系统必须全方位地管理计算机系统中运行的程序。因此,操作系统为正在运行的程序建立了一个管理实体——进程

进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是操作系统进行资源分配和调度的一个独立单位

OS应能管理与控制进程的执行、协调管理处理器、主存储器等各类资源在进程间的使用

一个进程包括五个实体部分,分别是:

  • (OS管理运行程序的)数据结构P
  • (运行程序的)内存代码C
  • (运行程序的)内存数据D
  • (运行程序的)通用寄存器信息R
  • (OS控制程序执行的)程序状态字信息PSW

为啥会有进程

如果一个计算机上面只跑一个程序,那么就不需要进程了。但是如果出现多个程序,并且想要同时跑,这时候就需要通过进程来进行管理。

计算机的资源是有限的。操作系统就需要在有限的资源上进行分配,以满足多个程序的运行。

何为进程管理

进程的管理就是操作系统动态分配系统的运行时资源,包括CPU,内存,外设等等。

主要可以分为两大类:

  • CPU资源:在一个单核CPU上,同时只能运行一个程序,如果有多个程序,就需要OS出面,进行切换操作。也叫做进程的调度。
  • 其他资源:包括文件、内存、外设,这部分涉及到进程的死锁。

进程的状态机制

进程既然要实现切换,必然有一个切换的流程,这里引入了进程状态来对进程的切换做了描述:

一个进程程获取了CPU资源时就进入执行态,让出cpu资源就进入就绪态。当进程因为IO操作挂起时,就进入阻塞态。

系统如何管理进程

Linux下使用了PCB来描述一个进程(Process Control Block),其中存了如下信息:

其中第一部分就是进程标识,OS会对每一个进程分配一个id,即pid(process id)

第二部分时进程的现场信息,在线程切换的时候,需要把当前的栈指针,处理器状态(状态字、寄存器)保存到内存。之后切换回来之后再进行恢复。

第三部分就是进程的其他信息,有用于指导调度器的,有关于进程资源的,还有很重要的进程的段页表等等。

进程切换的过程

进程切换必须在操作系统内核模式下完成,这就需要模式切换(处理器状态切换),模式切换包括:

(1)正向模式,即用户模式到内核模式,由中断/异常/系统调用中断用户进程执行而触发,由中断装置完成正向模式切换,包括:

  • 处理器模式转为内核模式;
  • 保存当前进程的PC/PSW值到核心栈;
  • 转向中断/异常/系统调用处理程序

(2)逆向模式,即内核模式到用户模式,OS执行中断返回指令将控制权交还用户进程而触发,由中断返回指令完成逆向模式转换,包括:

  • 从待运行进程核心栈中弹出PSW/PC值;
  • 处理器模式转为用户模式

完整的进程切换过程为

  1. (中断/异常等触发)正向模式切换并压入PSW/PC
  2. 保存被中断进程的现场信息
  3. 处理具体中断/异常
  4. 把被中断进程的系统堆栈指针SP值保存到PCB
  5. 调整被中断进程的PCB信息,如进程状态
  6. 把被中断进程的PCB加入相关队列
  7. 选择下一个占用CPU运行的进程
  8. 修改被选中进程的PCB信息,如进程状态
  9. 设置被选中进程的地址空间,恢复存储管理信息
  10. 恢复被选中进程的SP值到处理器寄存器SP
  11. 恢复被选中进程的现场信息进入处理器
  12. 中断返回指令触发逆向模式转换并弹出PSW/P

一些中断/异常不会引起进程状态转换,不会引起进程切换,只是在处理完成后把控制权交回给被中断进程,处理流程为

  1. 中断/异常触发正向模式切换压入PSW/PC
  2. 保存被中断进程的现场信息
  3. 处理中断/异常
  4. 恢复被中断进程的现场信息
  5. 中断返回指令触发逆向模式转换弹出PSW/PC

进程的调度机制

主要有以下几种:

  • 最短工作优先(SJF);
  • 最短剩余时间优先(SRTF);
  • 最高响应比优先(HRRF);
  • 优先级调度(Priority);
  • 轮转调度(RR)。

具体的实现不再赘述。

轻量化的进程--线程

前面提到,创建一个进程,OS会为其分配内存资源,文件资源,CPU资源等等,但很多情况下,我们只是需要更多的CPU资源来进行计算,而操作的数据却是同一块。这时如果我们创建了多个进程,就需要其他方式共享这么一个操作数据,而且OS会对每一个进程均分配内存资源,重复装载代码等。这是很浪费的。

所以OS引入了线程的概念,线程依附于进程,一个进程可以有多个线程,每一个进程都有自己的寄存器指针和堆栈指针。而诸如代码段,数据段和打开的文件等都是共享的和透明的。

这样的设计就是专为CPU资源调度而生,每一个线程都可以独立保存自己的状态,可以被独立调度。

线程根据实现的不同分为内核线程和用户线程,其中用户线程对OS不可见,OS无法直接调度,仅由相关的线程库进行内部调度,好处是减少的内核空间的占用和状态切换开销,缺点就是这样的线程只能运行在一个CPU核心上,因为OS只对进程进行调度,一个进程只分配一个核心。但总体来说线程间的切换比进程间切换要小得多,包括不用从新加载内存页,以及CPU的缓存依然有效。

而内核态进程对OS是可见的,可以利用OS的调度算法,从进程的视角上看,一个进程就可以被多个CPU处理。

目前linux使用的是内核态进程,描述结构与PCB类似,多了一个pgid,即进程组id,一个进程中的线程有不同的pid,但是有着相同的gid。

资源分配--进程的死锁

其实死锁不仅仅发生在线程的调度上,只要满足以下条件就会发生死锁:

  • 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
  • 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
  • 环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。

所以解决死锁就是打破这四个条件其一即可

1、设置资源可共享 -- 打破互斥条件

2、资源不全部满足就释放所有资源 -- 打破资源保持条件

3、OS可以强行剥夺资源 -- 打破不剥夺条件

4、计算资源分配顺序,避免循环依赖 -- 打破环路等待(银行家算法)

事实上,Linux内核并不会去预防死锁,而是通过超时机制来检测死锁。如果某个线程长时间不让出CPU资源,则内核认为其陷入死锁,超时之后触发中断,内核直接介入并终止进程。

原因就是预防死锁对性能的损失太大了,不如设计一个检测机制并let it chrash。


当其他人都认为你要鸽的时候,你鸽了,亦是一种不鸽