概念:进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位
组成:
1. PCB(进程描述信息、及进城控制和管理信息、资源分配清单、处理机相关信息)
2. 程序段(程序的代码)
3. 数据段(运行过程中产生的各种数据)
1. **运行状态:**在CPU上运行
2. **就绪状态:**进程获得除处理机外的一切资源,准备进入运行状态
3. **阻塞状态:**即使CPU空闲也不能运行,需要等待其他资源
4. 创建状态
5. 终止状态
原因:进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址空间相互独立。为了保证安全,一个进程不能直接访问另 一个进程的地址空间。
然而进程之间的信息交换又是必须实现的。 为了保证进程间的安全通信,操作系统提供了一些方法。
共享存储
两个进程对同一片共享空间进行访问,访问必须互斥进行。
基于数据结构的共享:
比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢、 限制多,是一种低级通信方式
基于存储区的共享:
在内存中画出一块共享存 储区,数据的形式、存放位置都由进程控制, 而不是操作系统。相比之下,这种共享方式速 度更快,是一种高级通信方式。
管道通信
“管道”是指用于连接读写进程的一个共享文件,又名pipe 文件。其实就是在内存中开辟一个大小固定的缓冲区
进程间的数据交换以格式化的消息(Message)为单位。进程通过操作系统提供的“发送消息/接收 消息”两个原语进行数据交换。
两种方式:
(1) 直接通信方式:消息直接挂到接收方的消息队列里
(2) 间接通信方式:消息发送到中间体(信箱)
进程是资源分配的基本单位,当进程切换时,需要保存/恢复进程的运行环境,还需要切换内存地址空间(更新块表、更新缓存)开销很大。
引入线程后,线程成为了调度的基本单位,进程仍然是资源分配的基本单位。线程几乎不拥有资源,只拥有极少量的资源 (线程控制块TCB、寄存器信息、堆栈等)。在多CPU环境下,各个线程还可以分派到不同的CPU上并行地执行。
Java内存模型:
非剥夺调度方式,又称非抢占方式。只允许进程主动放弃处理机。
剥夺调度方式,又称抢占方式。
当一个进程正在处理机上执行时,如果有一个更重要或更紧迫的进 程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给更重要紧迫的那个进程。
**先来先服务:**按照作业/进程到达的先后顺序进行服务。
**短作业优先:**最短的作业/进程优先得到服务(所谓“最短”,是指要求服务时间最短)。分为抢占式和非抢占式两种。
**高响应比优先:**在每次调度时先计算各个作业/进程的响应比,选择响应 比最高的作业/进程为其服务。
计算公式:响应比 = (等待时间 + 要求服务的时间) / 要求服务的时间
调度算法对比:
算法 | 是否可抢占? | 优点 | 缺点 | 导致饥饿? |
---|---|---|---|---|
先来先服务 | 否 | 公平;实现简单 | 对短作业不利 | 不会 |
短作业优先 | 两个版本(抢占式的叫最短剩余时间 优先算法 SRTN) | 平均等待时间短 | 对长作业不利,可 能导致饥饿;难以 做到真正的短作业 优先 | 会 |
高响应比优先 | 否 | 上述两种算法的权衡折中,综合考虑的等 待时间和运行时间 | 不会 |
并发性带来异步性,有时需要进程同步解决这种异步问题。有的进程之间需要相互配合地完成工作,各进程的工作需要遵循一定的先后顺序。
对临界资源的访问、需要互斥的进行。即同一时间段内只能允许一个进程访问该资源。比如多台电脑访问同一台打印机。
互斥需要遵循的规则:
在早期的操作系统中实现进程的同步与互斥是用硬件实现的,但是始终不能完全满足互斥需要遵循的四条规则。直到1965年,荷兰学者Dijkstra提出了一种卓有成效的实现进程互斥、同步的方法——信号量机制。
用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便的实现了进程互 斥、进程同步。
**一对原语:**wait(S) 原语和 signal(S) 原语。wait、signal 原语常简称为 P、V操作(来自荷兰语 proberen 和 verhogen)。wait即申请资源,若不能满足则进入阻塞队列,signal即释放资源并通知因该资源而阻塞的进程。
semaphore mutex = 1; //打印机数量
//p1进程
wait (mutex);
使用打印机...
signal(mutex);
//p2程
wait (mutex);
使用打印机...
signal(mutex);
死锁情况:生产者或消费者占用缓冲区却没有东西放入缓冲区或从缓冲区消费,生产者或消费者又不释放缓冲区资源,导致死锁。
semophore mutex = 1; //互斥信号量,实现对缓冲区的互斥访问
semophore empty = n; //同步信号量,表示空闲缓冲区的数量
semophore full = 0; //同步信号量,表示产品的数量,也即非空缓冲区的数量
producer() {
while(1) {
生产一个产品;
P(empty);
P(mutex);
把产品放入缓冲区;
V(mutex);
V(full);
}
}
consumer() {
while(1) {
P(full);
P(mutex);
从缓冲区中取出一个产品;
V(mutex);
V(empty);
使用产品;
}
}
哲学家进餐问题
死锁情况:所有哲学家拿起相同方向的筷子,又不主动放下筷子,造成死锁。
semaphore chopstick[5]={1,1,1,1,1};
semaphore mutex = 1; //互斥地取筷子
Pi (){ //i号哲学家的进程
while(1){
P(mutex);
P(chopstick[i]); //拿左
P(chopstick[(i+1)%5]); //拿右
V(mutex);
吃饭…
V(chopstick[i]); //放左
V(chopstick[(i+1)%5]); //放右
思考…
}
}
破坏互斥条件
将临界资源改造为可共享资源(如SPOOLing技术)
破坏不剥夺条件
方案一:申请的资源得不到满足时,立即释放拥有的所有资源
方案二:申请的资源被其他进程占用时,由操作系统协助剥夺
破坏请求保持条件
运行前分配好需要的资源,之后一直保持
破坏循环等待条件
给资源编号,必须按照一定的顺序申请资源
核心思想:在进程提出资源申请时,先预判此次分配是否会导致系统进入不安全状态。如果会进入不安全状态,就暂时不答应这次请求,让该进程先阻塞等待。
资源剥夺法
挂起(暂时放到外存上)某些死锁进程,并抢占它的资源,将这些资源分配给 其他的死锁进程。但是应防止被挂起的进程长时间得不到资源而饥饿。
撤销进程法(或称终止进程法)
强制撤销部分、甚至全部死锁进程,并剥夺这些进程的资 源。这种方式的优点是实现简单,但所付出的代价可能会很大。因为有些进程可能已经运行 了很长时间,已经接近结束了,一旦被终止可谓功亏一篑,以后还得从头再来。
进程回退法
让一个或多个死锁进程回退到足以避免死锁的地步。这就要求系统要记录进程的历史信息,设置还原点。