文章目录
基本概念
- 原子操作:一个函数(原语)或动作的指令序列不可分割,要么作为一个整体执行(不可中断),要么都不执行。
- 临界资源:一次仅允许一个进程独自占有使用的不可剥夺资源。
- 临界区:进程访问临界资源的那一段代码。
为了互斥访问临界资源,每个进程在进入临界区之前,需要先进行检查。 - 同步:多个进程因为合作产生的直接制约关系,使得进程有一定的先后执行关系
- 互斥:多个进程在同一时刻只有一个进程能进入临界区
- 饥饿:一个就绪进程被调度程序长期忽视、不被调度执行。
- 信号量:用于进程间传递信号的一个整数。在信号两上只可以进行三种操作,即初始化-- 递增- -递减(P和V操作)。这三种操作分别都是原子操作。递减作用于阻塞一个进程,递增作用于解除一个进程的阻塞。
操作系统的基本特征
- 并发
并发是指宏观上在一段时间内能同时运行多个程序(秒杀,多个人做同一件事),而并行则指同一时刻能运行多个指令(多个人同时做不同的事)
并行需要硬件支持,如多流水线、多核处理器或者分布式计算系统。
操作系统通过引入进程和线程,使得程序能够并发运行。 - 共享
共享是指系统中的资源可以被多个并发进程共同使用。
有两种共享方式:互斥共享和同时共享。
互斥共享的资源称为临界资源,例如打印机等,在同一时刻只允许一个进程访问,需要用同步机制来实现互斥访问。 - 虚拟
虚拟技术把一个物理实体转换为多个逻辑实体。
主要有两种虚拟技术:时(时间)分复用技术和空(空间)分复用技术。
多个进程能在同一个处理器上并发执行使用了时分复用技术,让每个进程轮流占用处理器,每次只执行一小个时间片并快速切换。
虚拟内存使用了空分复用技术,它将物理内存抽象为地址空间,每个进程都有各自的地址空间。地址空间的页被映射到物理内存,地址空间的页并不需要全部在物理内存中,当使用到一个没有在物理内存的页时,执行页面置换算法,将该页置换到内存中。 - 异步
异步指进程不是一次性执行完毕,而是走走停停,以不可知的速度向前推进。
进程和线程以及它们的区别
- 进程是对运行时程序的封装,是一个动态的过程:有它自身的产生、存在和消亡的过程(生命周期)。作为资源分配的的基本单位,系统在运行时会为每个进程分配不同的内存区域
- 线程是进程的子任务,作为调度和执行的基本单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
进程让操作系统的并发性成为可能,而线程让进程的内部并发成为可能
区别
Ⅰ 拥有资源
进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源。
Ⅱ 调度
线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程
中的线程时,会引起进程切换。
Ⅲ 系统开销
由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销
线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而
线程切换时只需保存和设置少量寄存器内容,开销很小。
Ⅳ 通信方面
线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC(进程间通信 InterProcess Communication)
进程同步有哪几种机制
原子操作、信号量机制、自旋锁管程、会合、分布式系统
进程间通信(IPC)的几种方式
-
管道(pipe)及有名管道(named pipe):管道可用于具有亲缘关系的父子进程间的通信;有名管克服了管道没有名字的限制:因此,除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
-
消息队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列:具有写权限的进程可以按照一定的规则向消息队列中添加新信息;对消息队列有读权限的进程则可以从消息队列中读取信息;
它克服了上两种通信方式中信号量有限的缺点:信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限; -
共享内存(shared memory):允许多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程 对共享内存中的数据的更新。
(1)由于所有进程对共享内存的访问就和访问自己的内存空间一样,不需要进行额外系统调用或内核操作,还避免了多余的内存拷贝,所以,这种方式是效率最高、速度最快的进程间通信方式
(2)由于内核并不提供任何对共享内存访问的同步机制,所以使用共享内存一般需要依靠某种同步操作,如互斥锁和信号量等,来达到进程间的同步及互斥; -
信号量(semaphore):它是一个计数器,用于为多个进程提供对共享数据对象的访问。
-
套接字(Socket):可用于网络中不同机器之间的进程间通信,应用非常广泛
-
信号(signal):信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;除了用于进程间通信,进程还可以发送信号给进程本身;
线程同步的方式
-
互斥量 Synchronized/Lock:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问
-
信号量 Semphare:它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量
-
事件(信号),Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便地实现多线程优先级的比较操作
进程有哪几种状态
- 就绪状态(ready)等待被调度:进程已获得除处理机以外的所需资源,等待分配处理机资源
- 运行状态(running):占用处理机资源运行,处于此状态的进程数小于等于CPU数
- 阻塞状态(waiting)等待资源:进程等待某种条件,在条件满足之前无法执行;
线程有几种状态?
在 Java虚拟机 中,线程从最初的创建到最终的消亡,要经历若干个状态:创建(new)、就绪(runnable/start)、运行(running)、阻塞(blocked)、等待(waiting)、时间等待(time waiting) 和 消亡(dead/terminated)。在给定的时间点上,一个线程只能处于一种状态,各状态的含义如下图所示:
线程各状态之间的转换如下:
死锁
如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。
1). 死锁的概念
在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。
通俗的讲,就是两个或多个进程无限期的阻塞、相互等待的一种状态。
2). 死锁产生的四个必要条件
(a->b 充分;b->a 必要;由死锁可以推出以下条件发生)
- 互斥:至少有一个资源必须属于非共享模式,即一次只能被一个进程使用;若其他申请使用该资源,那么申请进程必须等到该资源被释放为止;
- 占有并等待:一个进程必须占有至少一个资源,并等待另一个资源,而该资源为其他进程所占有;
- 非抢占:进程不能被抢占,即资源只能被进程在完成任务后自愿释放
- 循环等待:若干进程之间形成一种头尾相接的环形等待资源关系
3). 死锁的处理基本策略和常用方法
解决死锁的基本方法主要有 预防死锁、避免死锁、检测死锁、解除死锁 、鸵鸟策略 等。
a. 死锁预防
在程序运行之前预防发生死锁
死锁预防的基本思想是 只要确保死锁发生的四个必要条件中至少有一个不成立,就能预防死锁的发生,具体方法包括:
-
打破互斥条件:允许进程同时访问某些资源。
但是,有些资源是不能被多个进程所共享的,这是由资源本身属性所决定的,因此,这种办法通常并无实用价值。 -
打破占有并等待条件:可以实行资源预先分配策略
(进程在运行前一次性向系统申请它所需要的全部资源,若所需全部资源得不到满足,则不分配任何资源,此进程暂不运行;只有当系统能满足当前进程所需的全部资源时,才一次性将所申请资源全部分配给该线程)或者只允许进程在没有占用资源时才可以申请资源(一个进程可申请一些资源并使用它们,但是在当前进程申请更多资源之前,它必须全部释放当前所占有的资源)。
但是这种策略也存在一些缺点:在很多情况下,无法预知一个进程执行前所需的全部资源,因为进程是动态执行的,不可预知的;同时,会降低资源利用率,导致降低了进程的并发性。 -
打破非抢占条件:允许进程强行从占有者那里夺取某些资源。也就是说,但一个进程占有了一部分资源,在其申请新的资源且得不到满足时,它必须释放所有占有的资源以便让其它线程使用。
这种预防死锁的方式实现起来困难,会降低系统性能。 -
打破循环等待条件:实行资源有序分配策略。对所有资源排序编号,所有进程对资源的请求必须严格按资源序号递增的顺序提出,即只有占用了小号资源才能申请大号资源,这样就不回产生环路,预防死锁的发生。
b. 死锁避免
在程序运行时避免发生死锁。
死锁避免的基本思想是动态地检测资源分配状态,以确保循环等待条件不成立,从而确保系统处于安全状态。
所谓安全状态是指:如果系统能按某个顺序为每个进程分配资源(不超过其最大值),那么系统状态是安全的,换句话说就是,如果存在一个安全序列,那么系统处于安全状态。
资源分配图算法和银行家算法是两种经典的死锁避免的算法,可以确保系统始终处于安全状态。
- 资源分配图算法应用场景为每种资源类型只有一个实例(申请边,分配边,需求边,不形成环才允许分配),
- 银行家算法应用于每种资源类型可以有多个实例的场景。
c. 死锁解除
死锁解除的常用两种方法为进程终止和资源抢占。
进程终止是指简单地终止一个或多个进程以打破循环等待,包括两种方式:终止所有死锁进程;一次只终止一个进程,直到取消死锁循环为止;
资源抢占是指 从一个或多个死锁进程那里抢占一个或多个资源,此时必须考虑三个问题:
(I) 选择一个牺牲品
(II) 回滚:回滚到安全状态
(III) 饥饿(在代价因素中加上回滚次数,回滚的越多则越不可能继续被作为牺牲品,避免一个进程总是被回滚)
4)产生死锁的原因可归结为如下两点:
a. 竞争资源
系统中的资源可以分为两类:
可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺,CPU和主存均属于可剥夺性资源;
另一类资源是不可剥夺资源,当系统把这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等。
产生死锁中的竞争资源之一指的是竞争不可剥夺资源(例如:系统中只有一台打印机,可供进程P1使用,假定P1已占用了打印机,若P2继续要求打印机打印将阻塞)
产生死锁中的竞争资源另外一种资源指的是竞争临时资源(临时资源包括硬件中断、信号、消息、缓冲区内的消息等),通常消息通信顺序进行不当,则会产生死锁
b. 进程间推进顺序非法
若P1保持了资源R1, P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁
例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁
分页和分段有什么区别(内存管理)?
段式存储管理是一种符合用户视角的内存分配管理方案。在段式存储管理中,将程序的地址空间划分为若干段(segment),如代码段,数据段,堆栈段;这样每个进程有一个二维地址空间,相互独立,互不干扰。段式管理的优点是:没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)
页式存储管理方案是一种用户视角内存与物理内存相分离的内存分配管理方案。在页式存储管理中,将程序的逻辑地址划分为固定大小的页(page),而物理内存划分为同样大小的帧,程序加载时,可以将任意一页放入内存中任意一个帧,这些帧不必连续,从而实现了离散分离。页式存储管理的优点是:没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满)。
两者的不同点:
-
目的不同:分页是由于系统管理的需要而不是用户的需要,它是信息的物理单位;分段的目的是为了能更好地满足用户的需要,它是信息的逻辑单位,它含有一组其意义相对完整的信息;
-
大小不同:页的大小固定且由系统决定,而段的长度却不固定,由其所完成的功能决定;
-
地址空间不同: 段向用户提供二维地址空间;页向用户提供的是一维地址空间;
-
信息共享:段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制;
-
内存碎片:页式存储管理的优点是没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满);而段式管理的优点是没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)。
操作系统中进程调度策略有哪几种?
-
FCFS(先来先服务,队列实现,非抢占的):先请求CPU的进程先分配到CPU
-
SJF(最短作业优先调度算法):平均等待时间最短,但难以知道下一个CPU区间长度
-
优先级调度算法(可以是抢占的,也可以是非抢占的):优先级越高越先分配到CPU,相同优先级先到先服务,存在的主要问题是:低优先级进程无穷等待CPU,会导致无穷阻塞或饥饿;解决方案:老化
-
时间片轮转调度算法(可抢占的):队列中没有进程被分配超过一个时间片的CPU时间,除非它是唯一可运行的进程。如果进程的CPU区间超过了一个时间片,那么该进程就被抢占并放回就绪队列。
-
多级队列调度算法:将就绪队列分成多个独立的队列,每个队列都有自己的调度算法,队列之间采用固定优先级抢占调度。其中,一个进程根据自身属性被永久地分配到一个队列中。
-
多级反馈队列调度算法:与多级队列调度算法相比,其允许进程在队列之间移动:若进程使用过多CPU时间,那么它会被转移到更低的优先级队列;在较低优先级队列等待时间过长的进程会被转移到更高优先级队列,以防止饥饿发生。
虚拟内存?
1).内存的发展历程
没有内存抽象(单进程,除去操作系统所用的内存之外,全部给用户程序使用) —> 有内存抽象(多进程,进程独立的地址空间,交换技术(内存大小不可能容纳下所有并发执行的进程)
)—> 连续内存分配(固定大小分区(多道程序的程度受限),可变分区(首次适应,最佳适应,最差适应),碎片) —> 不连续内存分配(分段,分页,段页式,虚拟内存)
2).虚拟内存
虚拟内存允许执行进程不必完全在内存中。虚拟内存的基本思想是:每个进程拥有独立的地址空间,这个空间被分为大小相等的多个块,称为页(Page),每个页都是一段连续的地址。这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻进行必要的映射;当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的命令。这样,对于进程而言,逻辑上似乎有很大的内存空间,实际上其中一部分对应物理内存上的一块(称为帧,通常页和帧大小相等),还有一些没加载在内存中的对应在硬盘上
注意,请求分页系统、请求分段系统和请求段页式系统都是针对虚拟内存的,通过请求实现内存与外存的信息置换。
可以看出,虚拟内存实际上可以比物理内存大。当访问虚拟内存时,会访问MMU(内存管理单元)去匹配对应的物理地址(比如图5的0,1,2)。如果虚拟内存的页并不存在于物理内存中(如图5的3,4),会产生缺页中断,从磁盘中取得缺的页放入内存,如果内存已满,还会根据某种算法将磁盘中的页换出。
3). 页面置换算法
FIFO先进先出算法:在操作系统中经常被用到,比如作业调度(主要实现简单,很容易想到);
LRU(Least recently use)最近最少使用算法:根据使用时间到现在的长短来判断;
LFU(Least frequently use)最少使用次数算法:根据使用次数来判断;
OPT(Optimal replacement)最优置换算法:理论的最优,理论;就是要保证置换出去的是不再被使用的页,或者是在实际内存中最晚使用的算法。
4). 虚拟内存的应用与优点
虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。虚拟内存的使用可以带来以下好处:
在内存中可以保留多个进程,系统并发度提高
解除了用户与内存之间的紧密约束,进程可以比内存的全部空间还大
颠簸
颠簸本质上是指频繁的页调度行为,具体来讲,进程发生缺页中断,这时,必须置换某一页。然而,其他所有的页都在使用,它置换一个页,但又立刻再次需要这个页。因此,会不断产生缺页中断,导致整个系统的效率急剧下降,这种现象称为颠簸(抖动)。
内存颠簸的解决策略包括:
-
如果是因为页面替换策略失误,可以修改替换算法来解决这个问题;
-
如果是因为运行的程序太多,造成程序无法同时将所有频繁访问的页面调入内存,则要降低多道程序的数量;
-
否则,还剩下两个办法:终止该进程或增加物理内存容量。
局部性原理
(1). 时间上的局部性:最近被访问的页在不久的将来还会被访问;
(2). 空间上的局部性:内存中被访问的页周围的页也很可能被访问。