进程管理
进程的引入
- 进程的并发执行
提高计算机系统的资源利用率及系统吞吐量
- 进程并发执行的特征
- 间断性
- 失去封闭性,程序运行结果不可再现性
与时间有关的错误:没有实现互斥共享
- 静态程序结构不能支持并发运行的实现
- 进程管理功能
- 进程控制
- 进程互斥与同步
- 互斥方式
多个进程在共享临界资源时应采用互斥方式访问
- 同步方式
当多个进程互相合作完成一共同任务时,需要对它们的执行次序加以协调,满足它们的前驱后继关系
- 互斥方式
- 进程通信
进程之间进行信息交换
- 调度
进程概念
-
进程定义与特征
- 进程定义和特征
进程是具有一定独立功能的程序关于某个数据集合的一次运行过程,是系统进行资源分配和调度的一个独立单位
- 动态性
进程是程序的一次执行过程,它因创建而产生、由调度而执行、因得不到资源而暂停执行、最后因撤销而消亡
- 并发性
进程的并发性是指同在内存中的多个进程能在一段时间内同时运行,交替使用处理器
- 独立性
进程是一个能独立运行的基本单位,也是系统进行资源分配和调度的一个独立单位
- 异步性
各进程是按各自独立的、不可预知的速度向前推进的
- 动态性
- 进程映像
进程实体的组成
- 进程与程序的区别和联系
- 程序是静态概念,本身可以作为软件资源长期保存;而进程是程序的一次执行过程,是动态的,有一定的生命期
- 进程是一个能独立运行的单位,是系统进行资源分配和调度的基本单位,能与其它进程并发执行,而程序因其自身不能描述并发运行过程中的动态信息,所以无法参与并发运行
- 各进程在并发执行过程中存在异步性特征,而程序因为只能顺序执行,故没有这个特征
- 程序和进程并非是一一对应关系
- 进程定义和特征
-
进程状态与转换
- 进程的三种基本状态
-
就绪状态
-
运行状态
-
阻塞状态
就绪→进程调度→运行 运行→时间片到→就绪 运行→等待某事件发生→阻塞 阻塞→所等待事件发生→就绪
-
创建状态与终止状态
运行态→进行运行结束或遇到不可修复错误→终止态 创建态→系统完成创建进程的一系列工作→就绪态
-
- 进程的三种基本状态
-
linux进程状态解析
-
进程控制块
- 进程标识信息
- 进程标识符
- 用户标识符
- 家族关系
- 进程调度信息
- 进程状态
- 进程优先级
- 其他调度相关信息
- 事件
- 进程现场信息
- 通用寄存器内容
- 指令计数器的值
- 程序状态字PSW
- 栈指针
- 进程控制信息
- 进程的程序和数据在内存或外存的地址
- 进程同步信息
- 进程通信信息
- 资源管理信息
- 链接指针
- 进程标识信息
进程控制
- 进程创建
- 进程图
- 引起进程创建的典型事件
- 作业调度
- 用户登录
- 提供特定服务
- 应用请求
- 进程创建原语
- 为新进程申请一个尚未被使用的pid和一个空白PCB
- 为新进程分配必要的资源
- 初始化新进程的PCB
- 最后,将新进程状态置为就绪状态,并插入就绪队列,等待CPU调度
- 进程撤销
- 引起进程撤销的典型事件
- 正常结束而撤销
- 异常终止而撤销
- 应外界干预而撤销
- 操作员或操作系统干预
- 父进程请求
- 父进程被撤销
- 进程撤销原语
- 根据被撤销进程的标识符,从系统PCB集合中找到该进程的PCB,读出其状态,若处于运行状态,则立即终止其运行,并将系统调度标志置为"True",以便撤销该进程后系统立即重新进行CPU调度
- 若被撤销进程还有子进程,或者为这些子进程临时指定新的父进程
- 回收被撤销进程所拥有的全部系统资源
- 最后,将被撤销进程的PCB从所在队列移出,待以后父进程从中收集相关信息后,最后释放它,则改进程就永远从系统中消失了
- 引起进程撤销的典型事件
- 进程阻塞与唤醒
- 引起进程堵塞和唤醒的典型事件
- 请求资源失败
- 等待某种操作的完成
- 前驱进程尚未完成
- 进程无新工作可做
- 进程阻塞原语
- 进程唤醒
- 引起进程堵塞和唤醒的典型事件
- Linux进程管理
- 进程描述符task_struct
- 进程状态
state exit_state
- 相关标识符信息
- pid_t pid
- pid_t tgid
- pid_t pgrp
- session
- uid和gid
- euid和egid
- suid和sgid
- fsuid和fsgid
- 家族关系
- struct task_struct *group_leader
- struct list_head thread_group
- struct task_struct *real_parent
- struct task_struct *parent
- struct list_head children
- struct list_head sibling
- 进程调度相关信息
- 进程地址空间及文件系统信息
- 进程信号管理相关信息
- 进程状态
- 进程创建
- fork()
创建一个普通进程,调用一次返回两次,在子进程中返回0,在父进程中返回子进程的pid
- vfork()
子进程能共享父进程的地址空间,且父进程会一直阻塞,直到子进程调用exit()或调用exec()
- clone()
接受一个指向某函数的指针和该函数的参数,刚创建的子进程将马上执行该函数。clone()允许子进程有选择性的继承父进程的资源,因此通常用它来创建线程
- do_fork()
- fork()
- 进程终止
- 进程睡眠和唤醒
- 进程睡眠
- 唤醒进程
- 进程描述符task_struct
进程同步
- 进程同步的基本概念
- 并发进程间的间接制约关系与进程互斥
- 临界资源
- 临界区
- 同步机制应遵循的原则
- 空闲让进
- 忙则等待
- 有限等待
- 让权等待
- 并发进程间的直接制约关系与进程同步
- 并发进程间的间接制约关系与进程互斥
- 进程同步机制及应用
保证多个并发进程间的正确执行,使它们的执行结果具有可再现性
- 利用硬件方法解决进程互斥问题
- 禁止中断
使切换进程不可能发生
- 利用专用机器指令解决进程互斥问题
- TSL指令
读出指令地址中的值,并为其赋予一非零新值,最后返回该地址中的旧值
- Swap指令
全局布尔变量lock和局部布尔变量key
- TSL指令
- 禁止中断
- 利用软件方法解决进程互斥问题
- Peterson算法
- 设置两个共享变量:boolean数组flag[2],表示进程是否希望进入临界区,初始值均为false。整型变量turn,用于标识当前优先允许哪个进程进入
- 当P0希望进入临界区时,将其flag[0]置为true,同时置turn为1,将优先进入临界区的机会让给P1 然后判断:若P1的flag[1]为true且turn值为1,则等待P1退出临界区,否则直接进入。退出临界区时将flag[0]置为false
- 面包店算法
为每个进程设置一个唯一编号Pi,同时设置两个公用的数据结构:boolean choosing[N]表示进程是否正在取号,初始值为false,若Pi正在取号,改为True,int number[N]表示取到的号吗,初始值为0
- Peterson算法
- 利用锁机制解决进程互斥问题
为每个资源设置一把锁
- 利用信号量机制解决进程互斥问题
- 整形信号量机制
- 记录型信号量机制
- 信号量集机制
- 利用信号量机制实现进程的互斥
- 利用信号量机制实现进程的同步
- 利用硬件方法解决进程互斥问题
- 经典进程同步问题
- 生产者-消费者问题
- 哲学家进餐问题
- 读者-写者问题
- 理发师问题
- 管程机制
- 管程定义
一个管程定义了一个数据结构和能为并发进程所致行的一组操作
- 定义变量
- 利用管程机制解决生产者-消费者问题
- 管程定义
- linux同步机制解析
- 原子操作
- 自旋锁
- 读写自旋锁
- 内核信号量
- 读写信号量
- IPC信号量 (System V信号量)
- sem结构
- IPC信号量的相关系统调用
- int semget(key_t,int nsems,int semflag)
创建一个新的IPC信号量
- int semop(int semid,struct sembuf *opsptr,size_t nops)
操作指定的IPC信号量
- int semctl(int semid,int semnum,int cmd,union semun ag)
对IPC信号量实现控制操作
- int semget(key_t,int nsems,int semflag)
- Posix信号量
- 无名信号量
- int sem_init(sem_t *sem,int pshared,unsigned int value)
创建一个新的无名信号量,并进行初始化
- int sem_getvalue(sem_t *sem,int *sval)
获取指定信号量当前值,并保存在sval中
- int sem_wait(sem_t *sem)
阻塞型申请资源操作
- int sem_post(sem_t *sem)
释放资源操作
- int sem_destroy(sem_t *sem)
删除sem
- int sem_init(sem_t *sem,int pshared,unsigned int value)
- 有名信号量
-
sem_t *sem_open(const char *name,int oflag,mode_t mode,int value)
打开一个已存在的有名信号量或创建并初始化一个有名信号量,并将其引用计数+1
-
int sem_close(sem_t *sem)
关闭信号量
-
int sem_unlink(const char *name)
彻底删除信号量
-
- 无名信号量
进程调度
- 进程调度的基本概念
- 调度的层次
- 高级调度
从后备队列中选择一批合适的作业调入内存,并创建相应进程
- 低级调度
从就绪队列中选择一个合适的进程,令其投入运行
- 中级调度
负责进程在内存与外存之间的换入与换出
- 高级调度
- 进程调度功能
- 排队程序
- 分派程序
- 上下文切换程序
- 进程调度方式
- 非抢占方式
- 抢占方式
- 内核完全不可占
- 内核部分可抢占
- 内核完全可抢占
- 进程调度时机
- 选择进程调度方式及调度算法应考虑的因素
- 系统设计目标
- 调度的公平性
- 资源的均衡利用
- 合理的系统开销
- 调度性能的评价标准
- cpu的利用率
- 系统吞吐量
- 周转时间和带权周转时间
提交到完成,带权周转时间=周转时间/要求服务时间
- 响应时间
提交到响应
- 对截止时间的保证
- 调度的层次
- 进程调度算法
- 先来先服务调度算法
非抢占,有利于长作业不利于短作业,系统平均周转时间长,不能保证紧迫型任务得到及时处理
- 短作业优先调度算法
可抢占,最短平均周转时间,对长作业不利,不能保证紧迫型任务得到及时处理
- 高响应比优先调度算法
响应比=1+等待时间/要求服务时间,避免了饥饿,增加了系统开销,不能保证紧迫型任务得到及时处理
- 优先级调度算法
可抢占
- 时间片轮转调度算法
理想:响应时间=就绪进程数量*时间片
- 多级队列调度算法
抢占,实时进程:抢占,系统进程:非抢占,交互式进程:时间片轮转,批处理进程:先来先服务或短作业,实现简单 调度开销小 不够灵活 低优先级饥饿
- 多级反馈队列调度算法
抢占,时间片递增,长作业饥饿
- 先来先服务调度算法
- linux进程调度算法解析
进程通信
- 进程通信类型
- 共享存储器系统通信
在存储器中划出一块共享存储区,诸进程通过对共享存储区的读写操作来实现通信
- 消息传递系统通信
可实现不同主机间多个进程的通信
- 直接通信的方式
- 阻塞发送,阻塞接收
- 无阻塞发送,无阻塞接收
- 无阻塞发送,阻塞接收
- 间接通信方式
信箱头 信箱体
- 私用信箱
- 公用信箱
- 共享信箱
- 直接通信的方式
- 管道通信
管道文件:连接两个进程以实现它们之间通信的一个打开的共享文件 描述符fd[] FIFO 对管道的读写操作必须互斥进行、必须同步进行 只有确定通信双方都存在时才能进行管道通信
- 无名管道
存在于高速缓存中的临时文件,没有对应磁盘映像,常用于有亲缘关系的父子进程或兄弟进程之间的通信
- 有名管道
可用于系统中任意进程间的通信,可以在文件系统中长期存在具有路径名的文件
- 无名管道
- 客户-服务器系统通信
- Socket(套接字)
网络地址 连接类型 网络规程
- 远程过程调用(RPC)
允许客户机上的进程通过网络调用位于远程主机上的过程,不需要了解底层网络通信技术的实现细节,调用的实现过程对客户进程是透明的,采用客户/服务器结构实现
- Socket(套接字)
- 共享存储器系统通信
- 消息缓冲队列通信机制
- 消息缓冲队列通信机制中的数据结构
- 消息缓冲区
- 缓冲区队列
- PCB中与通信有关的数据项
- 发送原语send()
- 向系统申请一个空闲消息缓冲区
- 将发送区a中的信息复制到该空闲消息缓冲区中
- 将保存着待发送消息的消息缓冲区插入到接收进程的消息队列中
- 接收原语receive()
- 首先从自己的消息队列中取出第一个或指定的消息缓冲区
- 把该消息缓冲区中的信息复制到接收区B中
- 释放改消息缓冲区
- 消息缓冲队列通信机制中的数据结构
- linux进程通信机制
- linux管道通信机制
- 无名管道
- 有名管道 FIFO
- linux的IPC消息队列通信机制
- IPC消息队列通信机制中的相关数据结构
- 消息缓冲区struct msgbuf
- 消息结构 msg_msg
- 消息队列结构 msg_queue
- IPC消息队列相关的系统调用
- int msgget(key_t key,int msgflg);
- int msgsnd(int msqid,struct msgbuf *msgp,size_t msgsz,int msgflg);
- ssize_t msgrcv(int msqid,struct msgbuf *msgp,size_t msgsz,long msgtyp,int msgflg);
- int msgctl(int msqid,int cmd,struct msqid_ds *buf);
- IPC消息队列通信机制中的相关数据结构
- linux共享内存通信
- 共享内存通信机制中的数据结构
shmid_kernel
- 共享内存机制的相关系统调用
- int shmget(key_t key,int size,int shmfig);
- void *shmat(int shmid,const void *shmaddr,int shmgflg);
- int shmdt(const void *shmaddr);
- int shmctl(int shmid,int cmd,struct shmid_ds *buff);
- 共享内存通信机制中的数据结构
- linux管道通信机制
进程死锁
- 死锁的基本概念
- 死锁的概念
若系统中存在一组进程,它们中的每一个都无限等待被该组进程中另一进程所占用的且永远无法释放的资源
- 产生死锁的原因
- 竞争资源
若系统中有多个进程要共享某些资源,而这些资源的数量又不能同时满足各进程的需要,便会引起各进程对资源的竞争从而可能导致死锁
- 请求或释放资源的时机不当
异步性特征 资源请求和释放顺序不合理
- 竞争资源
- 产生死锁的必要条件
- 互斥条件
某个资源在一段时间内只能由一个进程占用,一旦将其分配给某进程后,必须等待改进程使用完成、主动释放它之后,才能再次分配给其它进程使用
- 占有且等待条件
进程已占有至少一个资源,又申请新的资源,由于该资源已被分配给别的进程,则该进程阻塞,但它在等待新资源时,仍继续占有已分到的资源
- 不可剥夺条件
一个程序所占有的不可剥夺资源在它使用完之前,系统不能强行剥夺,只能由该进程使用完之后主动释放
- 循环等待条件
系统中若干进程之间对资源的占有和请求形成了循环等待的关系,此时环路中的每个进程都已经占有一些资源,同时又在等待其相邻进程所占有的资源
- 互斥条件
- 处理死锁的基本方法
- 预防死锁
进程申请资源或系统分配资源时必须遵循某些预先设置的闲置条件,破坏产生死锁的条件 (资源利用率低)
- 避免死锁
在资源分配的过程中,用某些方法防止系统进入不安全状态(资源利用率和系统吞吐量高)
- 检测和解除死锁
设置死锁检测机构,及时地检测出系统是否出现死锁,并确定与死锁有关的进程和资源
- 预防死锁
- 死锁的概念
- 预防死锁
- 破坏占有且等待条件
- 静态分配资源
执行前申请所需全部资源
- 要求进程在不占有资源时才可申请资源
允许动态申请,申请前必须释放
- 静态分配资源
- 破坏不可剥夺条件
如果一个已经占有某些资源的进程请求新的资源而又不能立即得到满足时,它当前已占有的资源可以被其他进程剥夺,只有在它获得原有被剥夺资源和所申请的新资源时,才能重新继续运行
- 破坏循环等待条件
按序分配资源
- 破坏占有且等待条件
- 避免死锁
- 安全状态
安全序列
- 银行家算法
- 银行家算法中的数据结构
- 可利用资源Available[m]
- 最大需求矩阵Max[n][m]
- 分配矩阵Allocation[n][m]
- 需求矩阵Need[n][m]
Need[i][j] = Max[i][j] - Allocation[i][j]
- 安全性算法
- 设置两个向量 系统某时刻能够提供的各类资源的可用数量Work=Available,系统是否有足够资源分配给进程 Finish[i] = False
- 寻找Finish[i]=False且Need[i][j]<=Wrok[j] 若存在执行3 否则执行4
- 释放占据的资源 Work=Work+Allocation[i] Finish[i]=False 返回2
- 若所有Finish[i]=True 则系统处于安全状态 否则不安全
- 银行家算法
- 如果Request[i]<=Need[i]执行2,否则出错
- 如果Request[i]<=Available 执行3,否则等待
- Available -= Request Allocation += Request Need -= Request 执行4
- 执行安全性算法,若不满足,则分配资源无效,阻塞等待,恢复原来的资源分配状态
- 银行家算法中的数据结构
- 安全状态
- 死锁的检测与解除
- 资源分配图
- 死锁定理
可完全简化:无死锁
- 死锁检测算法
Coffman算法
- 死锁检测时机
- 每当进程请求资源且得不到满足时就做检测
- 周期性定时检测
- 依据cpu利用率确定是否进行检测
- 死锁的解除
- 撤销进程
- 撤销所有死锁进程
- 一次只撤销一个死锁进程直到解除死锁为止
- 抢占资源
逐步抢占部分死锁进程的资源给其他进程使用,并挂起被强占资源的进程,直至解除死锁为止
- 在选择被抢占资源的进程时要考虑代价问题
- “回滚问题”
- 避免饥饿现象
n个进程需要m个同类资源需要最少资源数n(m-1)+1
- 撤销进程
线程机制
- 线程基本概念
- 线程的引入
解决基于同数据区的同时多请求
- 什么是线程
- 一个线程ID
- 一组寄存器集合
- 两个栈
- 一个私有存储区
- 线程控制块TCB
- 进程和线程的比较
- 调度
传统的操作系统中,进程既是资源分配和拥有的基本单位,又是独立调度和执行的基本单位。而引入线程后,则把线程作为调度和执行的基本单位,把进程作为资源分配和拥有的基本单位,将传统进程的两个属性分开,使线程轻装运行,从而显著提高系统的并发程度,降低CPU切换开销。同一进程中两个线程的切换不会引起进程切换,但不同进程中的两个线程切换将会引起进程切换
- 并发性
引入线程后,不仅进程间可并发执行,而且一个进程中的多个线程间也能并发执行,系统并发度更高,能更有效地使用系统资源和提高体统吞吐量
- 拥有资源
线程基本上不拥有资源,但它可以访问所属进程的全部资源
- 系统开销
系统在创建进程时,必须为之分配资源,建立进程虚拟空间及相关数据结构,还要建立PCB,系统所付出的开销将显著大于创建线程的开销。进程切换时,需要切换进程的上下文,而线程切换只需要保存和设置少量寄存器内容,不涉及存储管理方面的操作,同一进程中多个线程间的数据共享及通讯是通过直接读写该进程空间的公用数据来完成,不需要写入操作系统的内核
- 调度
- 线程管理
- 线程状态
- 就绪状态
- 运行状态
- 阻塞状态
- 终止状态
- 线程控制
- 线程创建
- 线程终止
- 线程阻塞
- 线程唤醒
- 线程同步
- 线程调度
- 线程通信
- 线程状态
- 多线程的应用
- 线程的引入
- 线程的实现机制
- 用户级线程ULT
- 优点
线程切换速度快、调度算法可以是应用程序专用的、用户级线程可运行在任何操作系统上
- 问题
线程系统调用的阻塞问题、纯用户级线程机制不能利用多处理器的优势
- 优点
- 内核级线程KLT
在内核空间实现 所有管理工作都由内核完成
- 优点
线程执行系统调用时仅阻塞调用线程本身、在多处理器环境下一个进程的多个线程可以同时在多个cpu上并行执行、操作系统内核本身可以采用多线程机制
- 问题
开销较大
- 优点
- 组合方式
- 多对一模型
- 一对一模型
- 多对多模型
- 用户级线程ULT
- Linux线程机制