允许多个程序同时进入内存并运行,提高CPU
的利用率,目的是提高系统效率
a图内存中有四个程序,串行执行,因为这里只有一个程序计数器。
当有了多道程序技术之后就得到了b图,每个程序各自独立的占用一个逻辑程序计数器,达到并发执行效果
从c图中可以看到多个程序是轮流执行的
并发环境指一段时间间隔内,单处理器上有两个或两个以上的程序同时处于开始运行但尚未结束的状态,并且次序不是事先确定的。在并发环境下执行的程序就是并发程序。
进程是具有独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的独立单位
CPU
的一个抽象CPU
变换成多个虚拟的CPU
CPU
调度给需要的进程,即将CPU
的控制权交给某个进程就称为调度###特征
PCB
是系统感知进程存在的唯一标志:进程与PCB
一一对应PCB
集合。
进程表的大小往往固定,这也决定了一个操作系统最多支持多少个进程,有时我们称为系统支持的并发度1.4.1 PCB中需要保存的信息process id
),这个标识是唯一的,通常是一个整数user id
)* 虚拟地址空间的使用状况
* 打开的文件列表4、CPU线程信息undefined这是当
* 寄存器值(通用寄存器、程序计数器`PC`、程序状态字`PSW`、栈指针)
* 指向该进程页表的指针
说明:从上图中可以看到第一列是和进程管理相关的字段,第二列是存储管理的字段,第三列是文件管理的字段。
CPU
,并在CPU
上运行CPU
,而暂时不能运行* 已完成创建一个进程所必要的工作,如`PID、PCB`
* 但尚未同意执行该进程,因为资源有限终止态
* 终止执行后,进程进入该状态
* 可完成一些数据统计工作
* 资源回收挂起态
* 用于调节负载
* 进程不占用内存空间,其进程映像交换到磁盘上
![](//upload-images.jianshu.io/upload_images/1925650-0c3172fd8b1a3825.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/70
+
0)
说明:这里使用fork()
创建一个进程。浅度睡眠和深度睡眠不同在于前者在睡眠时会接收信号,而后者则不会。正在运行的程序可能因为调试断点可能出现一个暂停的状态。
PCB
PCB
从一个队列进入另一个队列undefined进程控制操作完成进程各状态之间的转换,由具有特定功能的原语(其实就是程序,只是这些程序不许与被中断)完成。关于进程控制的原语如下:
原语:完成某种特定功能的一段程序,具有不可分割性或不可中断性,即原语的执行必须是连续的,在执行过程中不允许被中断。又称原子操作。
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
undefinedCPU
密集型进程/IO
密集型进程UNIX
中是一个进程家族树的概念:init
为根进程。于是如果某一个进程结束了,那么其子孙进程都必须结束。Windows
中的所有进程的地位都是相同的。操作系统为每个进程分配了一个地址空间。这里我们看一个例子:
这个程序我们从命令行中输入数据,比如:
myval 7
myval 8
此时我们会发现虽然进程不同,但是打印出来的地址确实一样的。这里我们从进程地址空间来分析:
说明:上面的两个进程都有这样一个地址空间,也就是说这两个进程是在不同的地址空间上的相同的位置,所以虽然地址是一样的,但是实际上在实际内存中的地址是不一样的。
对进程执行活动全过程的静态描述(快照)。由进程地址空间内容、硬件寄存器内容及与该进程相关的内核数据结构、内核栈组成
PCB
及各种资源数据结构CPU
硬件状态从一个进程换到另一个进程的过程称为上下文切换,其实就是运行环境的切换。CPU
上的寄存器中。寄存器有:程序计数器、程序状态寄存器、栈指针、通用寄存器、其他控制寄存器的值引入理由
我们看一个例子,一个web
服务器的工作方式
可以看到每次从磁盘读取的时候进程都是暂停的,这样会导致性能低下
那如何提高服务器的工作效率?通常情况下是使用网页缓存
在
没有线程情况下的两种解决方案
I/O
多线程的解决方式
说明:这是一个多线程的web
服务器的工作方式,首先读取客户端的请求,之后由分派线程将各个任务分派给工作线程,这里还是采用了网页缓存
于是我们可以看到一个web
服务器的实现有三种方式:
如果有多个处理器的话,一个进程就会有多个线程同时在执行了,这样可以极大的提高运行性能
线程的属性
ID
-->
需要提供一些操作一般有三种实现机制
UNIX
undefinedPOSIX线程库--PTHREAD Solaris
操作系统
##6.4 线程状态(Java)
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
去执行。存在一个进程等待队列`{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 + Allocationi ;
转(2)
(4)若对所有i,Finish[i] == true
,则系统处于安全状态,否则,系统处于不安全状态。
允许死锁发生,但是操作系统会不断监视系统进展情况,判断死锁是否真的发生。一旦死锁发生则采取专门的措施,解除死锁并以最小的代价恢复操作系统运行。
1、当进程由于资源请求不满足而等待时检测死锁。这里缺点是系统开销较大。
2、定时检测
3、系统资源利用率下降时检测死锁
发生死锁后重要的是以最小的代价恢复系统的运行。方法如下:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。