进程(process)管理
定义:进程是一个具有一定独立功能的程序在一个数据集合是的一次动态执行过程
组成
包含了程序晕行的所有状态信息
- 程序的代码
- 程序处理的数据
- 程序计数器中的值,指示下一条将运行的程序
- 一组通用的寄存器的当前值,堆、栈
- 一组系统资源(内存网络文件系统等)
进程与程序的联系:
- 程序是产生进程的基础
- 程序的每次运行将产生不同的进程
- 进程是程序功能的体现
- 通过多次执行,一个程序可对应多个进程;通过调用关系,一个进程可包括多个程序
进程与程序的区别:
- 进程是动态的,程序是静态的: 程序是有序代码的集合;进程是程序的执行, 进程有用户态、核心态
- 进程是暂时的,程序是永久的:进程是一个状态变化的过程,程序可长久保存
- 进程与程序组成不同:进程由程序、数据、进程控制块(即进程状态信息)组成
进程的特点:
- 动态性:可动态创建、结束进程
- 并发性:进程可以被独立调度并占用处理机运行
- 独立性:不同进程的工作不互相影响
- 制约性:因访问共享数据、资源或进程间同步而产生制约
进程控制块(PCB):
- 进程的创建:为该进程生成一个PCB
- 进程的终止:回收他的PCB
- 进程的组织管理:通过对PCB的组织管理实现的
PCB包含的信息:
进程的标识信息
:如进程的标识,本进程的产生着的标识(父进程标识),用户标识
处理机状态信息保存区
:保存进程运行现场信息
- 用户可见寄存器:用户可以使用的数据,地址等寄存器
- 控制和状态寄存器:如程序计数器(PC),程序状态字(PSW)
- 栈指针:过程调用、系统调用、中断处理和返回时需要用到它
进程的控制信息
:
- 调度和状态信息:用于操作系统调度进程并占用处理机的使用
- 进程间通信信息:为支持进程间的与通信相关的各种标识、信号、信件等,这些信息存在接收方的PCB中
- 存储管理信息:包含有指向本进程影响存储空间的数据结构,以及父进程等进程间的关系
- 进程所用的资源:说明由进程打开、使用的系统资源,如打开的文件等
- 有关数据结构连接信息:进程可以连接到一个进程队列中,或连接到相关的其他进程的PCB
PCB的组织方式:
- 链表 :
较多使用
,因为进程是动态的插入删除 - 索引表
进程状态
进程的生命期管理:
- 进程创建
- 进程运行
- 进程等待
- 进程唤醒
- 进程结束
进程创建:
- 引起进程创建的主要事件*
- 系统初始化时,init进程
- 用户请求创建一个新的进程
- 正在运行的进程执行了创建进程的系统调用
进程等待(阻塞):
- 请求并等待系统服务,无法马上完成
- 启动某种操作,无法马上完成
- 需要的数据没有到达
进程只能自己阻塞自己,因为只有进程自身才能知道何时需要等待某种事件的发生,这是其他处于准备的进程可以由操作系统调度运行
进程唤醒:
- 被阻塞的进程需要的资源可被满足
- 被阻塞进程等待事件到达
- 然后将该进程的PCB插入到就绪队列
阻塞进程只能被其他进程或操作系统唤醒
进程结束:
- 正常退出(自愿的)
- 错误退出(自愿的)
- 致命错误(强制性的)
- 被其他进程所杀(强制性的)
进程状态变化模型:
进程挂起:
进程在挂起时,进程不占用内存空间,进程的映像在硬盘上
- 阻塞挂起状态:进程在外存并等待某事件的出现
- 就绪挂起状态:进程在外存,只要进入内存即可运行
从内存到外存,可能有以下几种情况:
- 塞到阻塞寒挂起:没有进程处于就绪状态或就绪进程要求更多内存资源时,会进行这种转换,以提交新进程或运行就绪进程
- 就绪到就绪挂起:当有高优先级阻塞(系统认为会很快就绪的) 进程和低优先就绪进程时,系统会选择挂起低优先级就绪进程
- 运行到就绪挂起:对抢先式分时系统,当有高优先级阻塞挂起进程因事件出现而进入就绪挂起时,系统可能会把运行进程转到就绪挂起状态
在外存时的状态转换:
- 阻塞挂起到就绪挂起:当有阻塞挂起进程因相关事件出现时,系统会把阻塞挂起进程转换为就绪挂起进程。
解挂/激活:把一个进程从外存转到内存; 可能有以下几种情况:
- 就绪挂起到就绪:没有就绪进程或挂起就绪进程优先级高于就绪进程时,会进行这种转换
- 阻塞挂起到阻塞:当一个进程释放足够内存时,系统会把一个高优先级阻塞挂起(系统认为会很快出现所等待的事件) 进程转换为阻塞进程
状态队列
- 由操作系统来维护一组队列,用来表示系统当中所有进程的当前状态
- 不同的状态分别用不同的队列来表示(就绪队列、各种类型的阻塞队列)
- 每个进程的PCB都根据它的状态加入到相应的队列当中,当一个进程的状态发生变化时,它的PCB从一个状态队列中脱离出来,加入到另外一个队列
线程:进程当中的一条执行流程
进程管理系统资源,线程作为进程的重要组成部分,由线程完成执行的过程。一个进程可以有多个线程,各线程共享进程提供的资源平台
线程的优点:
- 一个进程中可以存在多个进程
- 各线程之间可以并发的执行
- 各个线程之间可以共享地址空间和文件等资源
线程的缺点:
- 一个线程崩溃,会导致其所属进程的所有线程崩溃
单线程与多线程资源对比图
- 多线程共享代码、数据、文件等资源
- 多线程有各自自己的寄存器堆栈
进程与线程的比较:
- 进程是资源分配单位,线程是CPU调度单位
- 进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈
- 线程同样具有就绪、等待、执行三种基本状态,同样具有状态之间的转换关系
- 线程能减少并发执行的时间和空间的开销:
- 线程的创建时间比进程短
- 线程的终止时间比进程短
- 统一进程内的线程切换时间比进程短
- 由于同一进程的个线程间共享内存和文件资源,可直接进行不通过内核的通信
线程的实现:
- 用户线程:操作系统看不到的线程,由用户线程库管理
- 内核线程:是操作系统管理的线程
用户线程:
在用户空间实现的线程机制,它不依赖于操作系统的内核,由一组用户级的线程库函数来完成线程的管理,包括进程的创建、终止、同步和调度等
- 由于用户线程的维护由相应进程来完成(通过线程库函数),不需要操作系统内核了解用户线程的存在,可用于不支持线程技术的多进程操作系统
- 每个进程都需要它自己私有的线程控制块(TCB) 列表,用来跟踪记录它的各个线程的状态信息(PC、栈指针、寄存器),TCB由线程库函数来维护;
- 用户线程的切换也是由线程库函数来完成,无需用户态/核心)态切换,所以速度特别快*
- 允许每个进程拥有自定义的线程调度算法。
用户线程缺点:
- 阻塞性的系统调用如何实现? 如果一个线程发起系统调用而阻塞,则整个进程在等待
- 当一个线程开始运行后,除非它主动地交出CPU的使用权,否则它所在的进程当中的其他线程将无法运行
- 由于时间片分配给进程,故与其它进程比,在多线程执行时,每个线程得到的时间片较少,执行会较慢
内核线程:
是指在操作系统的内核当中实现的一种线程机制,由操作系统的内核来完成线程的创建、终止和管理
- 在支持内核线程的操作系统中,由内核来维护进程和线程的上下文信息(PCB和TCB)
- 线程的创建、终止和切换都是通过系统调用/内核函数的方式来进行,由内核来完成,因此系统开销较大
- 在一个进程当中,如果某个内核线程发走2系统调用而被阻塞,并不会影响其他内核线程的运行
- 时间片分配给线程,多线程的进程获得更多CPU时间
- Windows NT和Windows 2000/XP支持内核线程
轻量级进程:
它是内核支持的用户线程。一个进程可有一个或多个轻量级进程,每个量级进程由一个单独的内核线程来支持。(Solaris/Linux)
上下文切换
停止当前运行进程(从运行太转为其他状态)并且调度其他进程(转为运行状态),操作系统将PCB放入相应队列中, 就绪队列、等待I/O队列(分为每个设备的队列 )、僵尸队列
- 必须在切换之前存储许多部分的进程上下文
- 必须能够在之后恢复他们,所以进程不能显示他曾经被暂停过
- 必须快速(上下文切换时非常频繁)
需要储存:
- 寄存器(PC等)、CPU状态
- 一些时候可能会费时,所以我们应该尽量避免
进程控制:
- 创建进程,fork() 父进程创建子进程
- 加载和执行进程,EXEC()加载程序取代当前运行的进程
- 等待和终止进程 ,wait()
Fork()的简单实现:
对子进程分配内存,复制父进程的内存和CPU寄存器到子进程中,开销大
99%调用fork是为了接下来调用exec:fork中内存复制没用,子进程将可能关闭打开的文件和连接,开销高
优化:即省略复制父进程到子进程的过程
使用Vfork(),早期LINUX采用虚拟fork
- 一个创建进程的系统调用,不用创建一个同样的内存映像
- 一些时候是轻量级fork,只是复制了一小部分父进程的内容
- 子进程应该几乎立即调用exec
- 现在不再使用,如果采用了copy on write COW技术(通过OS的虚存管理,只复制了父进程meta元数据即页表,指向的是同一地址空间,对某个地址单元进行写操作时触发异常,父子各把要用的页复制成两份,使父进程和子进程拥有不同的地址。按需,光读不用复制,写才用)
等待:
wait():父进程用来等待子进程的结束,一个子进程向父进程返回一个值,父进程必须接受这个值并处理。
为什么要让父进程等?而不是直接结束? :
当进程执行完毕退出后,几乎所有资源都回收到OS中。但有个资源很难回收,就是PCB,PCB是代表进程存在的唯一标识,OS要依据PCB执行回收。这个功能由父进程完成。
Wait()使父进程睡眠,当子进程调用exit时操作系统解锁父进程,将通过exit传递得到的返回值作为wait调用的一个结果(连同子进程的pid一起)。关闭所有打开的文件和连接,释放内存,释放大部分支持进程的OS结构,检查父进程是否存活。
- 如果父进程存活,它保留exit结果的值直到父进程需要它,进入僵尸(zombie/defunct)状态。
- 如果父进程先于子进程死掉了,它释放所有的数据结构,这个进程死亡。
- 最后清理所有等待的僵尸进程。
最早的进程ROOT进程/主动进程/RIT进程会定期扫描PCB列表,找到僵尸状态的进程并清理,使OS中不会僵尸越积越多。
如果这里没有子进程存活,wait立刻返回。如果有父进程的僵尸等待,WAIT立即返回其中一个值并解除僵尸状态。
僵尸状态:
僵尸状态就是子进程调用了EXIT但父进程还没有wait执行完毕的时候。此时子进程无法正常工作,只是等待被父进程回收
状态转换图:
执行EXEC()时,进程可以处于不同的状态。首先是runnig, 然后加载(加载进内存)、运行,加载时间长要有可能running->blocked。