操作系统概念
操作系统是控制和管理计算机系统内各种硬件和软件资源,有效组织多道程序运行的系统软件(或程序集合),是用户与计算机之间的接口。
四大基本特征
- 并发指两个或多个活动在同一时间间隔内发生。
- 共享指有限的计算机系统资源被多个进程共享使用
- 异步,或称不确定性,指系统中各种事件发生顺序的不可预测性
- 虚拟指一个物理实体映射为若干个对应的逻辑实体
核心态、用户态
- 核心态:执行操作系统程序时所处的状态。此时有较高的特权,可执行一切指令。
- 用户态:执行用户程序时所处的状态。权限较低,只能执行指令集中的非特权指令。
系统调用
用户运行的程序基本都是运行在用户态,如果需要调用操作系统提供的系统态级别的子功就需要系统调用了。
也就是说在用户运行的用户程序中,凡是与系统态级别的资源有关的操作,都必须通过系统调用方式向操作系统提出服务请求,并由操作系统代为完成。
这些系统调用按功能大致可分为如下几类:
主要功能
- 存储管理
- 作业和进程管理
- 设备管理
- 文件管理
- 用户接口服务
进程/线程
- 进程:一个具有一定独立功能的程序在一个数据集合上的一次动态执行的过程。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程。
- 线程:进程中的一个控制单元,负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
- 区别:
- 根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。
- 内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的。
- 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响;但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
进程
进程的状态
- 新建态(new):进程正在被创建,尚未到就绪状态。
- 就绪态(ready):进程已处于准备运行状态,即进程获得了除了处理器之外的一切所需资源,一旦得到处理器资源(处理器分配的时间片)即可运行。
- 运行态(running):进程正在处理器上上运行(单核 CPU 下任意时刻只有一个进程处于运行状态)。
- 阻塞态(waiting):又称为等待状态,进程正在等待某一事件而暂停运行如等待某资源为可用或等待 IO 操作完成。即使处理器空闲,该进程也不能运行。
- 结束态(terminated):进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运行。
进程控制块PCB
进程控制块是进程组成中最关键的部分,其中含有进程的描述信息和控制信息,是进程动态特性的集中反映,是系统对进程施行识别和控制的依据。
进程控制块的作用:
- 每个进程有唯一的进程控制块。
- 操作系统根据PCB对进程实施控制和管理。
- 进程的动态、并发特性是利用PCB表现出来的。
- PCB是进程存在的唯一标志。
进程间通信方式
- 管道/匿名管道
Pipes
- 有名管道
Names Pipes
- 信号
Signal
- 消息队列
Message Queuing
- 信号量
Semaphores
- 共享内存
Shared memory
- 套接字
Sockets
进程调度算法
- 短作业优先SJF:当分配CPU时,选择所需处理时间最短的进程。短进程将越过长进程,跳到队列头。一个进程一旦分得处理机,便执行下去,直到该进程完成阻塞时,才释放处理机
- 高响应比HRRF:用户给出估计服务时间,为每个作业计算一个响应比,在作业调度时,挑选相应比最高的作业运行。
- 优先级法HPF:优先级调度算法是从就绪队列中选出优先级最高的进程,让它在CPU上运行。
- 先来先服务法FCFS:每次从就绪队列中选择一个最先进入该队列的进程,把CPU分给它,令其运行。该进程一直运行下去,直至完成或由于某些原因而阻塞,才放弃CPU。
- 最短剩余时间优先法SRTF:当新进程进入就绪队列时,如果它需要的运行时间比当前运行的进程所需的剩余时间还短,则执行切换,当前运行进程被剥夺CPU的控制权,调度新进程运行。
- 轮转法RR:每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
死锁
多个进程可以竞争有限数量的资源。当一个进程申请资源时,如果这时没有可用资源,那么这个进程进入等待状态。当所申请的资源被其他等待进程占有,那么该等待进程有可能再也无法改变状态。这种情况称为 死锁。
死锁产生的根本原因是资源有限和操作不当。
死锁产生的四个必要条件
- 互斥:资源必须处于非共享模式,即一次只有一个进程可以使用。如果另一进程申请该资源,那么必须等待直到该资源被释放为止。
- 占有并等待:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。
- 不可抢占:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。
- 循环等待:有一组等待进程
{P0, P1,..., Pn}
,P0
等待的资源被P1
占有,P1
等待的资源被P2
占有,......,Pn-1
等待的资源被Pn
占有,Pn
等待的资源被P0
占有。
预防死锁
- 破坏互斥条件 有些资源本身要求必须互斥访问,这是资源的固有属性,所以,用否定条件互斥条件的方法不能预防死锁。
- 破坏占有且等待条件
- “空手”申请资源策略:不占有资源的时候,才能申请。
- 预分资源战略:静态分配,执行之前申请到全部资源。
- 破坏不可抢占条件
- 隐式抢占方式(被抢):如果一个进程占有某些资源,它还要申请被别的进程占有的资源,该进程就一定处于等待状态,则进程当前所占有的全部资源可被抢占。
- 抢占等待者的资源(去抢):进程申请资源,如果没有可用,可以从等待进程那里抢占资源。 这些方法常用于资源状态易于保留和恢复的环境中,如CPU寄存器、内存,但不能用于打印机、磁带机等。
- 破坏循环等待条件
- 资源有序分配策略
- 先弃大,再取小
线程
线程同步
线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使用冲突。
操作系统一般有下面三种线程同步的方式:
- 互斥量(Mutex):采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如Java中的
synchronized
关键词和各种Lock
都是这种机制。 - 信号量(Semphares):它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
- 事件(Event):Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。
进程和线程
- 进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。
- 同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的。
- 一个进程崩溃后,在保护模式下不会对其他进程产生影响;但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
- 一个线程只能属于一个进程,而一个进程可以有多个线程。
- 进程编程调试简单可靠性高,但是创建销毁开销大;线程正相反,开销小,切换速度快,但是编程调试相对复杂。
协程 *
协程是一种用户态的轻量级线程, 协程的调度完全由用户控制。
协程(Coroutine)是计算机程序的一类组件,推广了协作式多任务的子例程,允许执行被挂起与被恢复。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。协程更适合于用来实现彼此熟悉的程序组件,如协作式多任务、异常处理、事件循环、迭代器、无限列表和管道。 --维基百科
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
协程和线程的区别 *
- 线程是被动挂起恢复,协程是主动挂起恢复
- 线程和进程都是同步机制,而协程则是异步机制。
- 线程是操作系统层面的概念,协程是语言层面的概念
- 线程是抢占式的,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
- 协程基于线程,但相对于线程轻量很多,可理解为在用户层模拟线程操作
内存管理
内存管理的功能
- 内存的分配与回收:当作业或进程创建后系统会为他们分配内存空间,当结束后内存空间也会被回收。
- 地址转换:将程序中的逻辑地址转换成内存中的物理地址。
- 内存空间的扩充:利用虚拟存储技术或自动覆盖技术,从逻辑上扩充内存。
- 存储保护:保证个个作业在自己的内存空间内运行,互不干扰。
内存管理机制
连续分配管理方式
连续分配管理方式是指为一个用户程序分配一个连续的内存空间,常见的有单一连续分配、固定分区分配、动态分区分配。
单一连续分配
在单一连续分配方式中,内存被分为系统区和用户区。系统区通常位于内存的低地址部分,用于存放操作系统相关数据;用户区用于存放用户进程相关数据。
内存中只能有一道用户程序,用户程序独占整个用户区空间。
- 优点:实现简单;无外部碎片;可以采用覆盖技术扩充内存;不一定需要采取内存保护。
- 缺点:只能用于单用户、单任务的操作系统中;有内部碎片;存储器利用率极低。
内部碎片:分配给某进程的内存区域中,如果有些部分没有用上。
外部碎片:是指内存中的某些空闲分区由于太小而难以利用。
固定分区分配
将整个用户空间划分为若干个固定大小的分区,在每个分区中只装入一道作业。分区大小相等:缺乏灵活性,但是很适合用于用一台计算机控制多个相同对象的场合。
操作系统需要建立一个数据结构——分区说明表,来实现各个分区的分配与回 收。每个表项对应一个分区,通常按分区大小排列。每个表项包括对应分区的 大小、起始地址、状态(是否已分配)。
当某用户程序要装入内存时,由操作系统内核程序根据用户程序大小检索该表, 从中找到一个能满足大小的、未分配的分区,将之分配给该程序,然后修改状 态为“已分配”。
- 优点:实现简单,无外部碎片。
- 缺点:
- 当用户程序太大时,可能所有的分区都不能满足需求,此时不得不采 用覆盖技术来解决,但这又会降低性能;
- 会产生内部碎片,内存利用率低。
动态分区分配
动态分区分配又称为可变分区分配。这种分配方式不会预先划分内存分区,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统分区的大小和数目是可变的。
动态分区分配没有内部碎片,但是有外部碎片。
动态分区分配算法
- 首次适应算法 First Fit:每次都从低地址开始查找,找到第一个能满足大小的空闲分区。
- 最佳适应算法 Best Fit:由于动态分区分配是一种连续分配方式,为各进程分配的空间必须是连续的一整片区域。因此为了保证当“大进程”到来时能有连续的大片空间,可以尽可能多地留下大片的空闲区,即,优先使用更小的空闲区。
- 最坏适应算法 Worst Fit:又称 最大适应算法(Largest Fit),为了解决最佳适应算法的问题——即留下太多难以利用的小碎片,可以在每次分配时 优先使用最大的连续空闲区,这样分配后剩余的空闲区就不会太小,更方便使用。
- 邻近适应算法 Next Fit:首次适应算法每次都从链头开始查找的。这可能会导致低地址部分出现很多小的空闲 分区,而每次分配查找时,都要经过这些分区,因此也增加了查找的开销。如果每次都从上次查 找结束的位置开始检索,就能解决上述问题。
非连续分配管理方式
为用户进程分配的可以是一些分散的内存空间。常见的如页式管理、段式管理和段页式管理。
- 逻辑地址:在有地址变换功能 的计算机中,访内指令给出的地址(操作数) 叫逻辑地址,也叫相对地址。
- 物理地址:物理地址指的是真实物理内存中地址,更具体一点来说就是内存地址寄存器中的地址。
分页式
将内存空间分为一个个大小相等的分区,每个分区就是一个“页框”(页框=页帧=内存块=物理块=物理页面)。每个页框有一个编号,即“页框号”(页框号=页帧号=内存块号=物理块号=物理页号),页框号从0开始。
将进程的逻辑地址空间也分为与页框大小相等的一个个部分,每个部分称为一个“页”或“页面” 。每个页面也有一个编号,即“页号”,页号也是从0开始。
操作系统以页框为单位为各个进程分配内存空间。进程的每个页面分别放入一个页框中。也就是说,进程的页面与内存的页框有一一对应的关系。各个页面不必连续存放,可以放到不相邻的各个页框中。
- 优点:内存空间利用率高,不会产生外部碎片,只会有少量的页内碎片
- 缺点:不方便按照逻辑模块实现信息的共享和保护
快表
为了解决虚拟地址到物理地址的转换速度,操作系统在 页表方案 基础之上引入了快表来加速虚拟地址到物理地址的转换。
快表,又称联想寄存器TLB: translation lookaside buffer
,是一种访问速度比内存快很多的 高速缓存(TLB不是内存!),用来存放最近访问的页表项的副本,可以加速地址变换的速度。
使用快表之后的地址转换流程是这样的:
- 根据虚拟地址中的页号查快表;
- 如果该页在快表中,直接从快表中读取相应的物理地址;
- 如果该页不在快表中,就访问内存中的页表,再从页表中得到物理地址,同时将页表中的该映射表项添加到快表中;
- 当快表填满后,又要登记新页时,就按照一定的淘汰策略淘汰掉快表中的一个页。
多级页表
引入多级页表的主要目的是为了避免把全部页表一直放在内存中占用过多空间,特别是那些根本就不需要的页表就不需要保留在内存中。多级页表属于时间换空间的典型场景。
不论是快表还是多级页表实际上都利用到了程序的局部性原理。
分段式
进程的地址空间按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名,每段从0开始编址。内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。
- 优点:很方便按照逻辑模块实现信息的共享和保护
- 缺点:如果段长过大,为其分配很大的连续空间会很不方便;段式管理会产生外部碎片
分页与分段
区别:
- 页是信息的物理单位;段是信息的逻辑单位。
- 页的大小是由系统确定的;段的长度因段而异。
- 页的地址空间是一维的;段的地址空间是二维的。
- 分页系统很难实现过程和数据的分离;分段系统却可以很容易实现过程和数据的分离。
- 分页会产生内部碎片;分段会产生外部碎片。
- 分段比分页更容易实现信息的共享和保护。
- 分页的主要目的是为了实现离散分配,提高内存利用率。分页仅仅是系统管理上的需要,完全是系统行为,对用户是不可见的;分段的主要目的是更好地满足用户需求。一个段通常包含着一组属于一个逻 辑模块的信息。分段对用户是可见的,用户编程时需要显式地给出段名。
共同点:
- 分页机制和分段机制都是为了提高内存利用率,减少内存碎片。
- 页和段都是离散存储的,所以两者都是离散分配内存的方式。但是,每个页和段中的内存是连续的。
段页式
将地址空间按照程序自身的逻辑划分为若干段,再将各段分为大小相等的页面。将内存空间分为与页面大小相等的一个个内存块,系统以块为单位为进程分配内存。
虚拟内存
是用户能作为可编制内存对待的虚拟存储空间,是操作系统给用户提供的一个比真实内存空间大得多的地址空间。虚拟存储技术建立了”内存+外存“的两级存储器结构,并将二者有机地结合在一起,从而得到一个容量相当于外存,速度接近于内存的存储体系。
与没有使用虚拟内存技术的系统相比,使用这种技术使得大型程序的编写变得更容易,对真正的物理内存(例如RAM)的使用也更有效率。此外,虚拟内存技术可以使多个进程共享同一个运行库,并通过分割不同进程的内存空间来提高系统的安全性。
特征:
- 虚拟扩充:扩大逻辑内存的容量,即用户编程时用到的地址空间可以远大于实际内存的容量。
- 部分装入
- 离散分配
- 多次对换:在一个进程运行期间,它所需要的全部程序和数据分成多次调入内存,而暂时不被使用的部分,可以换出到外存的对换区。
局部性原理
局部性原理是虚拟内存技术的基础,正是因为程序运行具有局部性原理,才可以只装入部分程序到内存就开始运行。
局部性原理表现在以下两个方面:
- 时间局部性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据 被访问过,不久之后该数据很可能再次被访问。(因为程序中存在大量的循环)
- 空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问。 (因为很多数据在内存中都是连续存放的,并且程序的指令也是顺序地在内存中存放的)
时间局部性是通过将近来使用的指令和数据保存到高速缓存存储器中,并使用高速缓存的层次结构实现。空间局部性通常是使用较大的高速缓存,并将预取机制集成到高速缓存控制逻辑中实现。虚拟内存技术实际上就是建立了 “内存一外存”的两级存储器的结构,利用局部性原理实现髙速缓存。
虚拟内存技术实现
- 请求分页存储管理 : 建立在分页管理之上,为了支持虚拟存储器功能而增加了请求调页功能和页面置换功能。请求分页是目前最常用的一种实现虚拟存储器的方法。请求分页存储管理系统中,在作业开始运行之前,仅装入当前要执行的部分段即可运行。假如在作业运行的过程中发现要访问的页面不在内存,则由处理器通知操作系统按照对应的页面置换算法将相应的页面调入到主存,同时操作系统也可以将暂时不用的页面置换到外存中。
- 请求分段存储管理 :建立在分段存储管理之上,增加了请求调段功能、分段置换功能。请求分段储存管理方式就如同请求分页储存管理方式一样,在作业开始运行之前,仅装入当前要执行的部分段即可运行;在执行过程中,可使用请求调入中断动态装入要访问但又不在内存的程序段;当内存空间已满,而又需要装入新的段时,根据置换功能适当调出某个段,以便腾出空间而装入新的段。
- 请求段页式存储管理
页面置换算法
- 最佳置换法OPT:为调入新页面而必须预先淘汰某个老页面时,所选择的老页面应在将来不被使用,或者是在最远的将来才被访问。以此保证获得最低的缺页率。
- 先进先出法FIFO:为调入新页面而必须预先淘汰某个老页面时,总是淘汰在内存中停留时间最长的一页,即先进入内存的页,先被换出。
- 最近最久未使用置换法LRU:当需要置换一页时,选择在最近一段时间里最久没有使用过的页面予以淘汰。这种算法认为过去一段时间内未被访问过的页面,在最近的将来可能也不会被访问。
- 最少使用页面置换算法LFU:为调入新页面而必须预先淘汰某个老页面时,该置换算法选择在之前时期使用最少的页面作为淘汰页。