允许多个程序同时进入内存并运行,提高CPU
的利用率,目的是提高系统效率
a图内存中有四个程序,串行执行,因为这里只有一个程序计数器。 当有了多道程序技术之后就得到了b图,每个程序各自独立的占用一个逻辑程序计数器,达到并发执行效果 从c图中可以看到多个程序是轮流执行的
并发环境指一段时间间隔内,单处理器上有两个或两个以上的程序同时处于开始运行但尚未结束的状态,并且次序不是事先确定的。在并发环境下执行的程序就是并发程序。
进程是具有独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的独立单位
CPU
的一个抽象CPU
变换成多个虚拟的CPU
CPU
调度给需要的进程,即将CPU
的控制权交给某个进程就称为调度①程序 描述进程要完成的功能 ②数据集合 程序在执行时所需要的数据和工作区。 ③程序控制块PCB 包含进程的描述信息和控制信息。它是进程存在的唯一标志。
PCB
是系统感知进程存在的唯一标志:进程与PCB
一一对应PCB
集合。
进程表的大小往往固定,这也决定了一个操作系统最多支持多少个进程,有时我们称为系统支持的并发度process id
),这个标识是唯一的,通常是一个整数user id
)CPU
不运行的时候操作系统需要把一些重要的信息记录下来
PC
、程序状态字PSW
、栈指针)说明:从上图中可以看到第一列是和进程管理相关的字段,第二列是存储管理的字段,第三列是文件管理的字段。
CPU
,并在CPU
上运行CPU
,暂时不能运行PID、PCB
说明:这里使用fork()
创建一个进程。浅度睡眠和深度睡眠不同在于前者在睡眠时会接收信号,而后者则不会。正在运行的程序可能因为调试断点可能出现一个暂停的状态。
PCB
PCB
从一个队列进入另一个队列
说明:真实情况下,就绪态也是排多个队列。而等待态由于各自产生的原因(事件)不同而排不同的队列。
进程控制操作完成进程各状态之间的转换,由具有特定功能的原语(其实就是程序,只是这些程序不许与被中断)完成。
原语:完成某种特定功能的一段程序,具有不可分割性或不可中断性,即原语的执行必须是连续的,在执行过程中不允许被中断。又称原子操作。
New
...)UNIX
中:fork/exec
Windows
中:CreateProcess
结束进程
PCB
UNIX
中使用:exit
,Windows
中:TerminateProcess
处于运行状态的进程,在其运行过程中期待某一事件发生,如等待键盘输入、等待磁盘数据传输完成、等待其他进程发送消息。当被等待的事件未发生时,由进程自己执行阻塞原语,使自己由运行态变为阻塞态。在UNIX
中我们使用wait
,在Windows
中使用WaitForSingleObject
。
fork()
通过复制调用进程来建立新的进程,是最基本的进程建立过程。也就是通过复制父进程来创建子进程。exec()
包括一些列系统调用,它们都是通过用一段新的程序代码覆盖原来的地址空间,实现进程代码的转换wait()
提供初级进程同步操作,能使一个进程等待另一个进程的结束exit()
用来终止一个进程的运行UNIX
中fork()
实现:
PCB
,在UNIX
中又叫proc
结构pid
Linux
中采用了写复制技术COW
加快创建进程。0
pid
CPU
密集型进程/IO
密集型进程UNIX
中是一个进程家族树的概念:init
为根进程。于是如果某一个进程结束了,那么其子孙进程都必须结束。Windows
中的所有进程的地位都是相同的。操作系统为每个进程分配了一个地址空间
这个程序我们从命令行中输入数据,比如:
myval 7
myval 8
此时我们会发现虽然进程不同,但是打印出来的地址确实一样的。这里我们从进程地址空间来分析:
说明:上面的两个进程都有这样一个地址空间,也就是说这两个进程是在不同的地址空间上的相同的位置,所以虽然地址是一样的,但是实际上在实际内存中的地址是不一样的。
对进程执行活动全过程的静态描述(快照)。由进程地址空间内容、硬件寄存器内容及与该进程相关的内核数据结构、内核栈组成
PCB
及各种资源数据结构CPU
硬件状态从一个进程换到另一个进程的过程称为上下文切换,其实就是运行环境的切换。CPU
上的寄存器中。寄存器有:程序计数器、程序状态寄存器、栈指针、通用寄存器、其他控制寄存器的值引入理由
我们看一个例子,一个web
服务器的工作方式
可以看到每次从磁盘读取的时候进程都是暂停的,这样会导致性能低下,那如何提高服务器的工作效率?通常情况下是使用网页缓存,在没有线程情况下的两种解决方案
I/O
多线程的解决方式
说明:这是一个多线程的web
服务器的工作方式,首先读取客户端的请求,之后由分派线程将各个任务分派给工作线程,这里还是采用了网页缓存
于是我们可以看到一个web
服务器的实现有三种方式:
如果有多个处理器的话,一个进程就会有多个线程同时在执行了,这样可以极大的提高运行性能
属性
ID
-->
需要提供一些操作一般有三种实现机制
线程是由运行时系统管理的,在内核中只有进程表。典型例子就是UNIX
POSIX线程库--PTHREAD
Solaris
操作系统0)新建:创建后尚未启动 1)运行:包括了 OS 中 Running 和 Ready 状态,也就是处于此状态的线程可能正在运行,也可能正在等待 cpu 为它分配执行时间 2)无限期等待:处于这种状态的线程不会被分配 cpu 执行时间,要等待其他线程显示唤醒。以下方法会让线程进入无限期等待 :
3)有限期的等待:处于这种状态的线程也不会被分配 cpu 执行时间,不过无需等待被其他线程显示唤醒,而是在一定时间后,他们会由 OS 自动唤醒 1.设置了 timeout 的 object.wait() 方法 2. 设置了 timeout 参数的 Thread.join() 3.LockSupport.parkNanos() 4.LockSupport.parkUnit() 4) 阻塞:与"等待"的区别:
一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。 这些共享资源一般是硬件设备或一群变量。 管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。与那些通过修改数据结构实现互斥访问的并发程序设计相比,管程实现很大程度上简化了程序设计。
管程提供了一种机制,线程可以临时放弃互斥访问,等待某些条件得到满足后,重新获得执行权恢复它的互斥访问。
临界区管理的基本原则 ①如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。 ②任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待。 ③进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。 ④如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。
一组进程中,每个进程都无限等待被该组进程中另一进程所占用的资源,因而永远无法得到的资源,这种现象称为进程死锁,这一组进程就称为死锁进程
资源数量有限、锁和信号量错误使用。
I/O
部件、内存、文件、数据库、信号量等可抢占资源。说明:
1
和资源2
,有这样一种情况,比如这两个进程都上cpu
执行,但是进程A
执行到第二句的时候需要使用资源2
,而进程B
执行到第二句的时候需要资源1
,但是此时恰好都不能获得各自的资源,这样就进入忙等待(进入轮询看资源是否可用),这就是活锁,也就是先加锁再轮询,这样导致两个进程既无进展也没有阻塞。这和死锁的概念的区别在于死锁的时候进程不能进入cpu
去执行。image.png
{P1,P2,......,Pn}
,其中P1
等待P2
占有的资源,P2
等待P3
占有的资源,......,Pn
等待P1
占有的资源,形成一个进程等待环路。
用有向图描述系统资源和进程的状态
化简步骤:
SPooling
技术的引入,解决不允许任何进程直接占有打印机的问题。设计一个“守护进程/线程”负责管理打印机,进程需要打印时, 将请求发给该daemon
,由它完成打印任务。只适用于状态易于保存和恢复的对主存资源和处理器资源的分配适用于资源。如cpu
和内存等。
P1
申请了资源1、3、9
,而进程P2
需要资源1、2、5
,那么进程P2
在申请时必须按照1、2、5
的顺序来申请,这样就破坏了环路条件,因为在申请到资源1
之前,后面的资源是申请不到的。存在下述严重问题: 限制了新类型设备的增加。 造成对资源的浪费。 必然会限制用户简单、自主地编程。
P1,P2,......,Pn
,则称系统处于安全状态。安全状态表示系统一定没有发生死锁。{P1,P2,......,Pn}
是安全的,如果对于每个进程Pi(1<= i <= n)
:它以后还需要的资源数量不超过系统当前剩余资源量与所有进程Pj(j < i)
当前占有资源量只和。这是Dijkstra
在1965
年提出的,是仿照银行发放贷款时采取的控制方式而设计的一种死锁避免算法。
当进程Pi
提出资源申请时,系统执行下列步骤:
(1)若Request[i] <= Need[i]
,转(2);否则,报错返回。
(2)若Request[i] <= Available
,转(3);否则,报错返回。
(3)假设系统分配了资源,则有:
Available = Available - Request[i];
Allocation[i] = Allocation[i] + Request[i];
Need[i] = Need[i] = Request[i]
`</pre>
若系统新状态是安全的,则分配完成;若系统新状态是不安全的,则恢复原来状态,进程等待。
为了进行安全性检查,定义了数据结构:
安全性检查的步骤:
(1)`Work = Available; Finish = false;`
(2)寻找满足条件的`i`:
如果不存在,则转(4)
(3)
`Work = Work + Allocation[i] ; 转(2)
(4)若对所有i,Finish[i] == true
,则系统处于安全状态,否则,系统处于不安全状态。
发生死锁后重要的是以最小的代价恢复系统的运行。方法如下:
image.png
image.png
image.png
image.png
image.png
image.png