操做系统学习笔记

BIOS

存在内存的特定地址,启动执行,检查计算机硬件java

加载位于硬盘特定地址的BootLoader,管理权交给BootLoadernode

BootLoader加载os,管理权交给os算法

系统调用

应用程序主动向操做系统发起请求指令(syscall)。同步或异步chrome

异常

应用程序执行中被动触发操做系统执行指令。应用程序意想不到的行为。除以零、访问未知地址等。同步。应用能够感知shell

中断

外设向操做系统发出请求,须要操做系统提供支持。异步。对应用透明编程

陷阱

陷阱指的是当异常或者中断发生时,处理器捕捉到一个执行线程,而且将控制权转移到操做系统中某一个固定地址的机制。现代操做系统是由中断驱动的,中断分为硬件中断和软件中断。而陷阱属于一种软件中断。若是计算机没有进程要执行,没有用户响应请求,操做系统将等待某个事件的发生。而事件老是由中断或者陷阱引发的windows

双重模式操做

操做系统为保证系统正常运行,会提供两种操做模式:api

  • 用户模式:用户进程执行的模式,模式位1
  • 系统模式:系统调用的模式,模式位0

两种模式不会互相干扰。操做系统须要硬件支持来提供双重模式操做:当用户进程进行非法操做(非法指令或非法地址访问)的时候,硬件会向操做系统发出陷阱信号,致使控制权转移到操做系统,操做系统结束用户进程并给出错误信息数组

进程

运行中的程序。分为用户进程和系统进程浏览器

内存管理

内存是cpu和io设备能够共同访问的数据仓库。若是cpu须要访问磁盘数据,首先生成io调用将数据加载进内存,以后才能直接访问

虚拟地址

编译器会应用程序生成的地址

物理地址

实际内存条(主存)和硬盘提供的可用地址

cpu访问内存数据的过程

  1. cpu中ALU(算术逻辑单元)在运算的时候须要获取某个虚拟地址的数据
  2. 虚拟地址到物理地址的映射关系会保存在MMU(内存管理单元)和内存中。MMU首先查找本身的映射表中虚拟地址对应的物理地址,若是找不到就去内存查找,直到找到物理地址
  3. cpu向总线发起访问物理地址的请求
  4. 内存访问物理地址的数据并经过总线传递给cpu

操做系统在其中扮演者预先创建虚拟地址到物理地址映射关系的角色,而且还限制了每一个应用访问虚拟地址的空间。若是cpu执行某个应用程序时,访问了不属于它的空间,就会产生一个内存访问异常,操做系统会接管并执行相应的异常处理代码

内存碎片

内存中空闲的,未被使用的空间

外碎片

分配单元之间,没被使用的空闲空间

内碎片

已分配给该应用,可是该应用未利用到的空闲空间

内存管理

合理的内存管理就是减小这些内存碎片

  • 操做系统为了加载应用程序,会提早分配一段连续的内存空间
  • 应用程序为了存储、访问一些数据,须要向操做系统发起请求,操做系统为它分配一段连续的内存空间

简单的连续的内存分配算法

  • 首次适配:找到第一个足够大小的空闲块就立马分配
  • 最优适配:找到(空间块 - 需求块)非负数而且最小的空闲块。须要排序,容易致使不少小碎片
  • 最差适配:找到(空闲块 - 需求块)最大的空闲块。须要按空闲大小倒序排序,容易致使后面申请的大块都没法分配

碎片整理

  • 压缩式碎片整理:将已使用的内存块进行移动,压缩成连续的空间,最后留出大的空闲块。须要在应用暂停执行的时候才能移动,不然应用访问内存数据出错。涉及反复拷贝操做,开销较大
  • 交换式碎片整理:若是经过压缩也没有足够内存块使用,能够将不活跃应用申请的内存块换出到硬盘,留出足够的空间分配给活跃应用

非连续内存管理

  • 优势:一个程序的物理内存是非连续的,这样能够提升内存利用率,减小碎片。而且将程序分红多个模块分别进行管理,让有的模块能够被多个程序共享,还能够对这些模块采用不一样的管理方式。
  • 缺点:须要创建虚拟地址到物理地址的映射关系,采用软件的方式创建性能开销大,须要依赖硬件方式创建

两种硬件方案:分段、分页

分段

将应用分红多个段:堆、运行栈、程序数据、程序text段。在虚拟地址是连续的,可是映射到物理地址是分离的

分段寻址机制:在分段机制中,要将虚拟地址映射到物理地址,虚拟地址被划分红两个部分,第一段为segment number,第二段为segment offset。cpu根据segment number去segment table中定位到该number对应物理内存的base地址,及该segment长度limit。而后经过检查offset是否小于等于limit,若是不是就触发异常,若是是该虚拟地址就成功映射到物理地址base+offset

segment table是由操做系统预先创建,并存入硬件

分页

分段在cpu中使用较少,分页使用较多

分页相似分段,也是将内存分红多个部分

在虚拟内存中,这些部分称为page(页),在物理内存中,这些部分称为frame(帧)。虚拟地址由page number和page offset定位,物理地址由frame number和frame offset定位,page number与frame number大小能够不一致,可是二者的offset必须一致,而且page size和frame size大小一致。

硬件寻址的时候根据虚拟地址划分出page number和page offset,并经过page table和该table的base地址查询page number到frame number的映射关系,最后定位到frame number,因为page offset与frame offset一致,因此最终物理地址就是frame number + page offset

分段与分页惟一不一样的地方在于每一个segment的大小不一样,而每一个page大小相同(1k、4k等)。因此segment table须要记录[segment number -> 物理内存base地址,segment长度limit]映射关系。而page table只须要记录[page number -> frame number]映射关系

经过分段或分页中,虚拟内存是连续的,可是定位到物理内存能够是非连续的,这样提升了内存利用率,减小了碎片

page table由操做系统创建

页表

假设page size为1k(2^10),64位操做系统须要创建的page table大小达到2^(64-10)=2^54,这么大的page table cpu和内存都没法存放,这致使空间开销大。即便内存能够存下,每次寻址都要先查page table再查物理内存,涉及到两次内存查询,这致使时间开销大

  • 时间:经过TLB解决
  • 空间:二级/多级页表、反向页表

TLB

每次cpu的MMU(内存管理单元)寻址的时候都会查询page table,这样致使时间开销很大。TLB是MMU中包含的一个缓存,缓存了page table的数据,空间很小速度很快

为了减小TLB查询miss,应用须要保证访问局部性

二级/多级页表

以二级页表说明

将虚拟地址分红三部分:

  • p1:一级页表编号
  • p2:二级页表编号
  • page offset:页表offset

内存中创建两个page table,一级page table中存储二级page table的base地址,二级page table存储frame number

寻址方式:

  1. MMU已知一级page table的起始地址,加上p1,定位到一级page table中存储的二级page table的base地址
  2. 根据该base加上p2,定位到frame number
  3. 最终加上offset,定位到物理地址

这种方式之因此能减小空间,能够经过64位系统、1k page size状况来讲明:

只用一个page table会致使须要创建大小为2^54的page table,根据这个page table中的信息判断虚拟地址是否有对应的物理地址

采用二级page table会让一级page table成为二级page table的索引,若是索引不到二级page table无需创建,索引到才须要创建。当虚拟地址远远大于物理地址的时候,会极大减小二级page table的item

结合上述结果,可得若是更多级page table会大大减小所需空间,不过也会带来另一个问题:屡次访问内存page table导致时间开销大。这个能够经过TLB解决

须要为每一个进程分配多级页表,以保证进程隔离,进程不会越界访问其余进程的虚拟地址。隔离以后,每一个进程看到的虚拟地址可能一致,但映射的物理地址不会相同,这一层经过操做系统保证

反向页表

多级页表会致使管理页表更麻烦,而且大小受虚拟地址影响,并且多级页表的状况下须要为每一个进程分配页表,因此空间开销依然不小

反向页表不一样,它根据物理地址索引虚拟地址,大小受物理地址限制,而且全部进程共享同一张页表,因此空间开销很小。须要区分每一个物理内存对应的应用,就须要引入pid(进程编号)

因为cpu只能拿到虚拟地址,利用反向页表的时候,须要遍历页表,匹配指定pid和虚拟地址,最终肯定物理地址。至关于遍历数组的值,肯定索引,时间开销较大

优化方案

基于hash函数方案:能够经过传递pid和虚拟地址,执行一段hash函数,定位到索引,即物理地址。缺点是设计高效少碰撞的hash算法难度较大,而且访问hash表会致使多出一次内存访问,这个能够经过TLB缓解

虚存技术

内存不够的状况下的解决方案之一。将应用使用的内存分红多个页管理,当内存不够用的时候,将应用没有使用到的页交换到磁盘,当内存足够的时候,将应用须要的页交换到内存。经过操做系统内核和MMU完成

内存置换的时候,须要一些信息,这些信息存在页表项中

页表由页表项组成,每一个页表项包含

  • 虚拟页编号
  • 访问位:表示该页是否被访问过(读写)。1表示是,0表示否。置换算法优先置换没有被访问过的页
  • 修改位:表示该页在内存中是否被修改过。1表示是,0表示否。若是被修改过,交换该页的时候,须要从新写到磁盘。若是没被修改过,直接释放该页便可,磁盘已经保存该页内容
  • 保护位:表示该页的访问权限。可读、可读写、可执行等
  • 驻留位:该页在内存仍是磁盘。1表示该页在内存;0表示该页在磁盘,访问到该项将致使缺页中断。
  • 物理页编号

程序局部性

虚存技术将内存置换交给了操做系统,但一样提出了对应用程序的要求。它但愿程序可以保证局部性

  • 时间局部性:一个数据的一次访问与下一次访问间隔时间很短
  • 空间局部性:一条指令与下一条指令访问的数据集中在一个很小的区域

当程序达到时间局部性和空间局部性,就说程序的局部性很好,它能让内存置换次数更少,使得访问虚存就像访问内存同样简单、快速

缺页中断

当访问虚拟内存没法映射到物理内存时,会产生缺页中断,流程为:

  1. 若还有空闲物理页面,则分配物理页帧f,则进入步骤4;若没有空闲物理页面,则进入步骤2
  2. 经过页面置换算法,选出物理页f与其对应的虚拟页q,若是该页的修改位为1,则将该页写回磁盘
  3. 修改q对应的页表项(将驻留位改成0)
  4. 将硬盘中的数据读到物理页帧f,并修改对应的页表项(将驻留位改成1,将物理页号改成f)
  5. 从新运行被中断的指令

最优置换算法

局部页面置换算法

当出现缺页中断而且没有足够的物理页面时,须要置换算法保证将一部分页面写到磁盘,并腾出相应的空间。

最优置换算法但愿保证将要置换的页面在后面很长一段时间都不会访问到

不过这种状况较理想,由于没法预测将来应用会访问到的页面,因此这种方式没法实现。不过这种算法能够做为一个理想评价标准

FIFO置换算法

局部页面置换算法

操做系统维护一个链表,维护使用过的页信息。链表采用先进先出原则,在缺页中断发生时,链表表头的页会被淘汰。效果较差

LRU置换算法

局部页面置换算法

最久未被使用的页优先被淘汰

采用栈或链表实现:每次访问页面,若是以前访问过,则替换到表头或栈顶,并淘汰表尾或栈底的元素。每次访问页面都要遍历一次链表或者栈,时间开销较大。若是采用hash表,空间开销大

CLOCK置换算法

局部页面置换算法

将页组织成一个环形链表,当出现缺页中断时,指针沿着环形链表不断向后转动,若是发现访问位为1的则将它置为0,若是碰到访问位为0的则淘汰该页

访问位是在cpu访问该页以后置为1的,表明该页最近被访问过。这个算法至关于给了两次机会,当须要置换时,会检查该页,若是为1,则置为0,表示只有最后一次机会留在内存。若是一个页常常被访问,操做系统置换检查时,该位应该老是1

加强CLOCL置换算法

局部页面置换算法

上述CLOCK算法没有考虑须要置换的页面是否被修改过。

  • 若是被修改过,则置换该页成本较大,须要将页数据写出
  • 若是没被修改过,说明该页内容已经存在于磁盘,置换成本很小,直接释放该页便可

加强CLOCK算法思想是优先淘汰没有访问而且没有写的页。它引入修改位,将访问位与修改位视为二元组,在环形链表中:

  • 若是指针指向页的二元组为11,则置为01(访问为置为0)
  • 若是指针指向页的二元组为10或01,则置为00
  • 若是指针指向页的二元组为00,则直接淘汰

LFO置换算法

局部页面置换算法

淘汰使用最少的页

这种置换算法有两个问题:

  1. 须要一个计数器来统计使用次数,这个计数器不断增加,容易变得很大,须要专门的硬件(内存或寄存器)来存储,开销大
  2. 有些应用初始化操做会频繁加载某些页,可是以后再也不使用。致使这些页面计数不少,可是之后不会再用
  3. 比较计数大小的时间开销较大

第二个问题的解决方案是,定时将计数寄存器右移,保证一直未被使用的数据,最后会指数级衰减

belady现象

给每一个应用分配的物理页面越多,缺页率反而越高的异常现象

工做集

一个进程当前正使用的逻辑页面集合。由二元组W(t,△)

  • t表示当前时间
  • △表示一个时间窗口
  • W(t,△)表示当前时间t以前的△时间窗口中的全部页面集合
  • |W(t,△)|表示工做集大小,即页面数

|W(t,△)|能够量化应用局部性,若是页面数越大,局部性越差

常驻集

当前时刻,进程实际驻留在内存中的页面集合,大小等于操做系统分配给该进程的物理页数,内容取决于操做系统采用的页面置换算法。

工做集与常驻集:进程不断访问内存页,造成工做集集合,访问页的时候会查询常驻集中页,若是不存在则引起缺页中断

局部与全局页面置换算法

以前介绍的FIFO,LRU,CLOCk,加强CLOCK都是局部页面置换算法,只考虑某个进程内页面置换的状况。全局页面置换算法会考虑全部进程进行页置换的状况。

为何须要全局置换算法?

每一个进程工做集不断变化,程序局部性也在不断变化,操做系统能够根据这些信息动态调整常驻集大小,避免浪费或者资源不够。因此,全局页面置换算法相比局部页面置换算法,更加合理

为何介绍局部置换算法?

既然全局置换算法更合理,为何介绍局部置换算法呢?由于有的局部算法如FIFO,LRU,也能够用在全局;有的系统更加依赖局部置换算法来保证部分程序出现抖动不会影响全局系统

工做集页置换算法

全局页面置换算法

常驻集只保存工做集中的页面,全部不在工做集中的页面都要置换出去。每次应用访问内存页的时候,都会进行置换算法

缺页率置换算法

全局页面置换算法

思想是根据缺页率动态调整常驻集大小。当缺页率太高时,增大常驻集(物理页帧数);当缺页率太小时,减少常驻集。保证缺页率在一个合适的范围内

实现:

  • t1表示当前缺页中断时间,t2表示上次缺页中断时间,T表示缺页率阈值,高于该阈值表示太高,低于等于该阈值表示太低
  • 当t2 - t1 > T,从常驻集中移除不在[t1,t2]时间内的页
  • 当t2 - t1 <= T,在常驻集中添加缺失的页

相比较工做集页置换算法,缺页率置换算法以缺页率为参考依据,工做集置换算法须要在每一个时刻执行算法,缺页率算法只是在缺页的时候才会执行算法

抖动

当进程数愈来愈多,分配给每一个进程的物理页数愈来愈少,致使常驻集永远没法包含工做集,导致大量缺页中断,操做系统频繁进行内存置换,cpu利用率大大下降。这种现象称为抖动

抖动出现表明操做系统的负载太高,这种负载每每是过多的进程数或不合理的物理页数致使的。为了缓解这种状况,操做系统须要保证:

  1. 全部进程的工做集总和 = 物理内存大小。若是前者大于后者,会致使物理内存不够用,常常会出现缺页中断;若是前者小于等于后者,说明物理内存足够空闲,每一个进程能分配到足够的工做集。
  2. 平均缺页间隔时间(MTBF) = 缺页中断处理时间(PFST)。若是MTBF >= PFST,表示缺页间隔时间较长,不算很频繁,有足够的时间处理缺页中断;若是MTBF < PFST,表示缺页间隔较短,很频繁,没有足够的时间处理缺页中断

实际状况,第一个方案较难实现,每每采起第二个方案

进程与程序

关联:

  • 进程是正在运行的程序。它拥有独立的堆、栈、代码段、数据段
  • 程序是可执行文件,只有被操做系统加载进内存以后才能成为进程
  • 一个程序能够衍生出多个进程,每一个进程使用的数据内容都不相同。一个进程可能由多个程序组成。二者是多对多映射关系

区别:

  • 进程是动态的,程序是静态的。程序是有序代码的集合;而进程是运行的程序,它拥有用户态和系统态之分
  • 进程是暂时的,程序是永久的。进程结束以后,它就不存在了;只要硬盘不坏,它存储的程序就一直存在
  • 进程和程序的组成不一样。进程不光包含程序,还包含运行中的输入输出的数据,以及进程的状态信息

进程组成

进程包含以下信息:

  • 代码
  • 数据
  • PC寄存器,存储下一条执行指令
  • 一组通用寄存器
  • 一组系统资源,如内存、文件系统、网络等资源

进程控制块

操做系统用来表明每一个进程的数据结构,简称PCB。PCB能够描述进程的基本状况和状态变化的过程,是每一个进程的惟一标识

包含三类信息:

  1. 进程标识信息
  2. 状态信息的保存区。每一个进程都会利用cpu对通用寄存器、PC寄存器、状态寄存器、栈指针寄存器作一些修改,这些修改在进程切换以前须要保存下来,以便恢复使用。PCB必须能存储这些状态信息
  3. 进程控制信息。 进程调度状况、进程间通讯信息、进程对内存的使用信息、进程使用的资源、进程与其余进程的父子关系都存在这里

通用操做系统会采用链表而不是数组来组织PCB,由于进程会不断建立和销毁,因此须要不断添加和删除操做,采用链表开销更小

进程的生命周期

进程生命周期能够分为:

  1. 进程建立。进程建立的三种状况:操做系统初始化、用户发起建立进程的请求、进程发起建立进程的系统调用
  2. 进程就绪。一旦进程建立完成,而且初始化完成,它就处于就绪状态,表示能够被执行
  3. 进程运行。操做系统会从多个就绪状态的进程中选择一个给cpu执行
  4. 进程等待。进程须要等待一些事件完成,会阻塞本身
  5. 进程唤醒。当处于等待状态的进程所须要的事件完成时,就会被(其余进程或操做系统)唤醒。一旦唤醒,进程进入就绪状态
  6. 进程结束。进程会由于这几种状况结束:正常退出(自愿)、异常退出(自愿)、致命退出(强制:访问非法地址)、被其余进程杀死(强制:kill)

进程状态变化

其中须要说明的是Running与Ready之间的状态转换:内存中有多个就绪的进程须要执行,当进程A执行时间片用完以后,操做系统会切换到进程B,让进程A进入就绪状态,进程B进入运行状态

进程挂起

进程挂起就是当内存不够用的时候,将进程换出到磁盘。挂起分为两种状态:

  • 阻塞挂起:进程在磁盘而且等待事件的发生
  • 就绪挂起:进程在磁盘,一旦进入内存就是就绪状态

挂起涉及到的状态转换:

  • 阻塞 -> 阻塞挂起:进程须要更多内存资源时,优先将阻塞状态的进程挂起,变成阻塞挂起状态
  • 就绪 -> 就绪挂起:在高优先级阻塞进程和低优先级就绪进程中,优先挂起后者,变成就绪挂起状态
  • 运行 -> 就绪挂起:当出现高优先级阻塞进程进入就绪状态,操做系统可能会挂起运行进程,变成就绪挂起状态
  • 阻塞挂起 -> 就绪挂起:当阻塞挂起的进程等待的事件出现时,操做系统会将它转变为就绪挂起状态

进程激活

进程激活就是将进程从磁盘换入到内存,涉及到的状态转换:

  • 就绪挂起 -> 就绪:没有就绪进程,或者被就绪挂起的进程优先级较高时,会执行这种转换
  • 阻塞挂起 -> 阻塞:当一个进程释放了足够的内存,操做系统会将优先级较高的阻塞挂起进程激活,变成阻塞状态

进程状态队列

操做系统根据进程状态,来分开管理进程:

  • 就绪队列管理就绪状态的进程
  • 阻塞队列管理阻塞状态的进程

操做系统根据进程的状态,决定将它插入哪一个队列中。当进程状态变化时,操做系统会从原队列取出,插入新队列

  • 根据优先级将就绪队列分红多种,优先级高的队列优先被处理
  • 根据等待的事件类型,将阻塞队列分红多种,每一个队列对应一个事件

线程

轻量级进程,是进程中的一条执行流程。一个进程中的不一样线程,共享相同的数据区、代码区和打开的系统资源,可是每一个线程有本身独立的PC、栈、通用寄存器,以便维护各自的执行状态。进程是资源分配的单位,线程是cpu调度的单位

操做系统采用数据结构TCB来表示线程,线程与进程同样,也拥有就绪、阻塞、运行三种状态,以及状态之间的转换关系

优势:

  • 轻量级,打开、释放、切换更快
  • 共享数据模型,比进程间消息传递更快
  • 共享相同资源,更加节省空间

缺点:

  • 安全性差,同一进程不一样线程会对共享的数据产生污染
  • 稳定性差,同一进程多个线程,其中一个奔溃会引发其余全部线程崩溃

例子:

  • 高性能计算每每采用线程
  • 浏览器为了稳定性和安全性,每每每一个标签页一个进程(chrome)

线程所需的资源:

用户线程

由库函数实现的线程是用户线程。采用库来支持线程的建立、销毁、调度,操做系统并不知道用户线程的存在

优势:

  • 线程的操做都在用户态,开销小

缺点:

  • 线程执行阻塞操做会block整个进程,由于操做系统只知道进程而不知道线程
  • 进程中一个线程只要不让出cpu,其余线程都没法执行,由于操做系统没法对这些线程作调度
  • 操做系统只能以进程为单位分配时间片,最终分到每一个线程的时间片就很是有限

内核线程

由内核维护PCB和TCB

优势:

  • 不会由于个别线程阻塞其余线程,由于操做系统会执行线程切换
  • 操做系统可以以线程为单位分配时间片,更灵活

缺点:

  • 每次线程操做涉及系统调用,须要从用户态切换到内核态,开销大

案例:

  • windows

轻量级线程

内核支持的用户线程,结合了用户线程管理开销小和内核线程稳定性高的优势。

案例:

  • Linux方案:每一个用户线程对应一个轻量级线程,每一个轻量级线程对应一个内核线程,最终用户线程与内核线程一对一。这种方案简单,高效,使用更多
  • Solaris方案:用户线程和轻量级线程多对多,轻量级线程和内核线程多对一,最终用户线程和内核线程多对多。这种方案复杂度高,更灵活,可是收益较小
  • 其余方案:用户线程和轻量级线程一对一,轻量级线程和内核线程多对一,最终用户线程和内核线程多对一

用户线程与内核线程映射关系:

进程切换

进程切换涉及的几个关键操做:

  • 保存进程上下文
  • 恢复进程上下文
  • 快速切换。为保证速度,通常采用汇编实现

其中上下文主要指进程使用的寄存器,如PC寄存器、栈寄存器、通用寄存器。这些信息在切换以前必须保存,以便以后恢复执行

操做系统为存储这些进程,提供了多种队列:

  • 就绪队列
  • 阻塞队列
  • 僵尸队列

进程建立

  • Windows进程建立:CreateProcess
  • Unix/Linux进程建立:fork/exec,经过fork复制出一个子进程,并在程序中划分为不一样的处理流程,在子进程流程中执行exec重写当前进程,可是pid不变
int pid = fork();//建立一个进程,并返回子进程id

if(pid == 0){
	//子进程返回值为0
	exec("program",argc,argv0,argv1,...)
}else if(pid > 0){
	//父进程返回值为子进程id
	...
}else{
	//错误
	...
	wait(pid);//等待子进程结束
}

在fork复制进程的时候,会复制全部进程信息,只有其中各自子进程id不一样,使得fork返回值不一样

进程加载

Unix/Linux采用exec加载进程,绝大部分状况下fork后会使用exec,保证拷贝的子进程可以加载新程序。如上一节代码,exec会将当前进程全部信息替换为program进程信息

  • 简单的fork实现就是拷贝父进程全部数据,可是当子进程执行exec时,这些信息会被替换掉,以前全部拷贝都失去意义,并且开销大
  • 高效的fork实现会考虑exec带来的影响。通常只用拷贝所需的基本元信息和页表项,而非全量拷贝,保证子进程在读操做的时候不受影响,而且开销很小。以后exec操做替换全部信息也不会感到惋惜。若是子进程没有执行exec,也没有单纯只读,而是作了写操做,操做系统会采用copy on write技术,在写以前全量拷贝,保证父子进程有独立的数据空间

进程等待

wait()用于父进程等待子进程结束

  1. 子进程exit()返回一个值给父进程
  2. 父进程wait()等待子进程返回结果

不一样次序会有不一样结果:

  • 1先2后:子进程成为僵尸进程,父进程wait以后直接返回
  • 2先1后:父进程阻塞,等着子进程完成exit,并唤醒父进程,将exit code返回给父进程

进程退出

exit()用户进程资源回收

  • 它会返回结果给父进程
  • 释放资源
  • 若是父进程还存在,当前进程进入僵尸状态
  • 清理僵尸进程

其中exec的时候可能出现两种状况:

  • 进程处于Running
  • 可能须要加载硬盘上的程序,进程会从Running转为Blocked

进程调度

从就绪进程中挑选一个占用cpu的过程,若是有多cpu,还须要提早选出一个可用的cpu

进程调度涉及到上下文切换

进程调度时机,首先须要知足这两个条件其中一个才能产生调度:

  • 进程由运行状态切换到等待状态
  • 进程结束

其次,还须要分状况讨论具体调度时机:

  • 非抢占系统:

    • 进程主动让出cpu
  • 可抢占系统:

    • 进程时间分片用完
    • 进程由等待切换到就绪状态(表示立刻会抢占cpu)

长进程:执行时间较长的进程

短进程:执行时间较短的进程

先来先服务算法

进程调度算法之一。优先服务先入队列的进程

优势:

  • 简单

缺点:

  • 若是长进程在就绪队列前排位置,容易致使平均等待时间过长
  • 若是cpu密集型排在io密集型以前,容易致使io长时间得不到利用

短进程优先算法

进程调度算法之一。优先服务短进程。涉及到两个技术点:

  • 排序
  • 预测进程执行时长:相似牛顿法求根号的方式,不断预测并矫正,保证最后逼近真实值

优势:

  • 平均等待时间较短

缺点:

  • 容易致使进程饥饿(长进程一直没法执行)

最高响应比优先算法

进程调度算法之一。根据就绪队列中进程列出以下公式:

R=(w+s)/s

  • w:进程等待时间
  • s:进程执行时间
  • R:响应比

等待时间越长,R越大;执行时间时间越长,R越小。该算法优先服务R最大的进程。可见,它至关于短进程优先算法的改进版

优势:

  • 平均等待时间较短
  • 防止个别进程无限期等待

缺点:

  • 不支持抢占

时间片轮转算法

调度算法之一。指定时间片大小为N,按前后顺序取出就绪队列中的进程执行,时长不超过N,执行完成以后,取下一个进程继续执行。取进程的顺序与先来先服务一致,可是每一个进程执行时间不得超过N

优势:

  • 保证每一个进程都能执行

缺点:

  • 若是时间片设置过小,会有大量上下文切换,开销大,而且平均等待时间较长
  • 若是时间片设置太大,会退化成先来先服务算法

经验规则:一般设置成10ms,能够保证上下文切换开销在1%之内

多级队列算法

调度算法之一。将就绪队列分为多级子队列,根据每一个队列具体状况的不一样,采用不一样的算法,是一种综合使用全部算法的一种算法

多个子队列之间采用时间片轮转算法来调度,每一个队列保证必定的cpu执行时间。

如:两个队列,前台交互式,占80%时间,后台批处理,占20%时间。则每一个执行周期会执行4次前台进程和1次后台进程

多级反馈队列算法

多级队列算法改进版。多级队列算法没有考虑到实际队列中进程执行时间,有的较长但放在了前台队列,致使真正响应快速的进程没有获得更高的优先级

将多级子队列按优先级从高到低划分,优先级越低,分得的时间片越大,适合执行批处理任务,优先级越高,分得的时间片越小,适合执行交互式任务。执行顺序从高到底,高优先级队列执行完了才执行低优先级队列,在规定时间片内每执行完的进程,被下放到低一级的队列,但获取到了更多时间片

优势:

  • 优先执行了交互式任务
  • 高优先级的总时间较小,不会致使低优先级队列饥饿
  • 灵活,自动调整队列中的进程优先级,每一个队列能够采用适合本身的算法

缺点:

  • 实现复杂度较高

这是实际系统会采用的算法

公平共享调度

按用户组重要程度,给他们使用的系统资源划分优先级,没用完的资源用比例划分

不作详细介绍

实时调度

有些系统不只要保证功能性,更要保证明时性,但愿能再可预测的时间以内完成某个功能。它对吞吐量要求并不高,可是对实时响应要求很是高。根据实时程度,分为两种:

  • 硬实时:必须在指定时间内完成功能
  • 软实时:尽可能在指定时间内完成功能

一个实时任务中涉及的重要概念

系统每每由多个实时任务组成,多个实时任务一般是周期性请求,如图周期为5

其中假设周期为5,最大执行时间为1,则使用率为1/5

为保证调度系统实时可控,须要确立众多任务调度顺序,有两种方法

  • 静态优先级调度:事先肯定调度优先级
  • 动态优先级调度:动态调整调度优先级

速率单调调度

任务周期越短(速率越快)的优先级越高

由于任务的周期(速率)是调度以前就能肯定好的,因此它是静态优先级调度算法

最先截止时间调度

请求的任务中,截止时间越早,优先级越高

任务不一样,截止时间也不一样,因此只有处理任务请求的时候才能知道截止时间,因此它是动态优先级调度算法

非对称多处理器调度

选取其中一个cpu做为主,进行调度和资源访问,无需同步,可是没法利用多处理器

对称多处理器调度

简称SMP,每一个cpu运行本身的调度程序,访问共享资源的时候会须要同步,同步开销较大,可是更加通用。多处理器调度实际上是考虑如何将进程分配给cpu,有两种方案:

  • 静态进程分配:为每一个cpu维护一个就绪队列,进程一旦分配到这些队列,会一直属于该队列,进程一旦执行就不会被切换,至关于将进程绑定给cpu

    • 优势:调度开销小
    • 缺点:进程执行时间不一样会致使cpu负载不均衡
  • 动态进程分配:为全部cpu维护一个共享就绪队列,进程会分配给空闲可用的cpu执行,中途会被切换

    • 优势:cpu负载均衡
    • 缺点:调度开销(同步、上下文切换)

优先级反置

低优先级进程阻塞高优先级进程的现象

如三个进程A,B,C,优先级A<B<C

  1. A执行,须要访问资源,加锁
  2. 切换到C执行,须要访问资源,等待A释放锁,进入阻塞
  3. B开始执行,优先级比A高,抢占cpu执行,而且长时间执行致使A没法释放锁,C也没法获取锁

这样B就阻塞了C的执行

优先级继承

优先级反置的解决方案之一

当低优先级占有资源而且高优先级进程申请资源时,一旦低优先级进程被阻塞(如A被B阻塞),就让它的优先级继承高优先级进程(让A继承C的优先级),这样就能解除阻塞顺利抢占cpu并执行

优先级天花板协议

优先级反置的解决方案之一

当进程占用资源时,将它的优先级提高为M(M=全部可能占有该资源进程的最高优先级),保证让该进程不会被阻塞

缺点:该方案的缺点明显,它的滥用优先级提升的功能,当全部进程优先级同样的时候,这种方案也没用了

进程并发

优势:

  • 资源共享,节约成本
  • 速度提高
  • 提升资源利用率
  • 程序模块化,一个进程能够分红多个模块,并发执行

缺点:

  • 并发会致使数据正确性、一致性受到影响

原子操做

之因此进程并发会出现问题,是由于不少进程操做都不是原子操做,致使执行一半被中断,并切换到另外一个进程,使得数据正确性和一致性受到影响

原子操做是一组不可中断的操做,里面可能涉及多个步骤,要么所有成功,要么所有失败,不可能出现部分红功部分失败的状况

进程交互

进程之间有三种交互状况:

  • 进程隔离:各进程资源独立,并发不会致使问题

  • 共享资源:访问共享资源,会带来三个问题

    • 互斥:一个进程访问共享资源,另外一个进程就不能访问
    • 死锁:每一个进程拥有一部分资源,须要另外一部分资源才能执行后续的操做,致使互相等待,而没法继续
    • 饥饿:部分进程轮流占用资源,致使其余进程没法占用

    操做系统要知足共享资源的同时又能提升效率,就必须提供一种高效的同步互斥机制

  • 通讯协做:经过互相通讯来传递信息

临界区

将访问共享资源的一片代码称为临界区

进入区
临界区
退出区
剩余区

临界区访问规则:

  • 空闲则入:没有其余进程进入临界区的时候,能够直接进入
  • 忙则等待:临界区被其余进程访问的时候,等待
  • 有限等待:不会一直等待,能够设置超时时间
  • 让权等待:没法进入临界区而等待的时候,须要放弃cpu使用权(sleep和while空转会一直占用cpu,wait不会)

禁用硬件中断

经过禁用硬件中断能够很好的实现对临界区访问的同步,它能够保证进程不会被中断,也没有进程上下文切换,也没有并发执行。它的思想是经过限制并发、并行,保证进程执行的正确性和一致性

优势:

  • 基于硬件方案,实现简单

缺点:

  • 系统低效
  • 临界区执行时间长会致使后续的进程饥饿
  • 没有中断,也就没法及时响应进程的紧急事件
  • 只适用于单个cpu。能够保证单个cpu的状况下进程不会并发执行,可是没法保证多个cpu的状况下进程并行执行

现代操做系统提供了禁用中断及恢复中断的指令,可是不多使用

Peterson算法

基于软件方式的实现两个线程同步的经典算法

思想是提供两个共享变量,并组合使用,来表明是否能够访问临界区。能够很好的协调多个线程访问,而且符合临界区访问规则

优势:

  • 不依赖硬件,彻底基于软件方法实现。虽然涉及多个共享变量的访问并不是原子操做,可是依然能覆盖多线程访问的各类可能性

缺点:

  • 实现复杂,须要考虑多个共享变量被并发使用的各类可能性,容易出错
  • 只适用于两个线程同步,更多线程同步须要将代码改形成更加复杂的模式

经过编程抽象的一种同步机制,底层依赖于硬件支持,保证一个时间点只能有一个线程访问共享资源。涉及到一个变量和两个操做

  • value:锁占用状态,1为占用,0为空闲
  • 获取锁:获取锁的进程才能访问共享资源
  • 释放锁:释放锁后,其余进程才能继续获取锁
  • 等待队列:自旋锁不须要等待队列,引入等待队列能够防止cpu空转

锁操做必须保证是原子的。cpu提供了两个原子性操做,库函数经过使用这两个原子操做,来实现锁语义

cpu提供的原子操做

  • testAndSet(value):简称ts。返回value的原值,并将value设成1
  • exchange(a,b):交换a,b内存中的值
class Lock{
	int value=0;
	WaitQueue q;
	
	void lock(){
		while(testAndSet(value))
			block(currentThread);//将当前线程放入阻塞队列,放弃cpu。同时调度其余线程;若是不作任何操做,就是忙等待
	}
	
	void unlock(){
		value=0;
		wakeup(otherThread);//将阻塞队列中的线程唤醒到就绪队列
	}
}

优势:

  • 适用单cpu或多cpu
  • 适用任意多线程
  • 能够对多个资源进行同步控制
  • 简单易用

缺点:

  • 非公平,容易致使线程饥饿。将线程唤醒到就绪队列,操做系统会按线程优先级来调度,而非等待时间,会导致部分低优先级线程一直没法执行
  • 有死锁风险。由于会有阻塞操做,当多个线程有多个资源占用时,可能出现死锁风险

基于锁方案的同步机制更加经常使用

信号量

经过编程抽象的一种同步机制。由一个整数、两个方法、一个队列组成

  • sem:整数。表明资源数量
  • P():获取资源的方法。实现细节:sem减一,判断是否小于0,是就阻塞,不然继续
  • V():释放资源的方法。实现细节:sem加一,判断是否大于等于0,是就唤醒阻塞线程
  • q:等待队列

其中信号量提供给用户用,但P()和V()方法由操做系统来实现,优先级比应用进程的优先级,不会被中断,保证了两个方法的原子性

信号量是一种公平的同步方法,先阻塞的线程先进等待队列,而且优先被唤醒。相较而言,自旋锁没法保证公平

用信号量解决生产者消费者问题

在早期操做系统中使用较多,如今不多使用,由于使用复杂,容易出错。而且信号量中P是操做系统来实现,优先级高,可能会保持阻塞,造成死锁

管程

不少现代编程语言(java)使用的一种同步模型,是一种高级同步机制

由如下成分组成:

  • 锁:保护共享资源,持有锁的线程才能访问共享资源
  • N个条件变量:相似信号量中的sem,区别在管程有多个变量。能够做为同步、唤醒操做的前提条件。经过这些条件变量,管程不只能实现二元互斥,还能限制线程数量
  • 等待队列:没拿到锁的线程都进入等待队列
  • Wait:持有锁的线程进入等待队列,释放锁
  • Signal:唤醒等待队列中的一个线程

管程解决生产者消费者问题

其中count就是条件变量

其中Wait操做会释放锁,避免出现信号量中的死锁问题,使用相对容易

Henson管程

在T2唤醒T1以后,为了高效不切换,优先继续让T2线程执行(没法肯定到底哪一个执行),直到T2释放锁,T1才恢复执行。java中采用此方案

while(count==n){
	wait()
}

因此上述代码中,wait的线程被唤醒后,可能仍是没法执行,致使count的值可能遭到修改,因而必须使用while循环从新判断一次

Hoare管程

在T2唤醒T1后,T2释放缩,T1抢占并执行(肯定T1抢占)

if(count==n){
	wait()
}

因此上述代码,wait的线程被唤醒后,会抢占执行,期间不会有其余线程修改count的值,因此无需重复判断

hoare侧重于做为原理讲解管程执行流程,而henson才适合用在真正的系统

哲学家就餐

五个哲学家,五根筷子,每一个人两边都放了筷子,组成环路。哲学家有两个操做:

  • 就餐:须要拿到两边的筷子,进入就餐状态
  • 思考:放下两边的筷子,进入思考状态

可能出现一种状况,全部哲学家拿起左边的筷子,等着右边的筷子,最后都没法进食,活活饿死。这就是所谓的死锁状况

解决方案:

  1. 提供服务员,服务员只能给一个哲学家受权,被受权的哲学家才能拿两边的筷子,而后就餐。这样不会出现死锁,可是同一时刻只有一我的就餐,只须要两根筷子,另外两根浪费了
  2. 之因此出现死锁是由于它们按照相同顺序拿筷子,只须要让偶数编号的哲学家先拿左边再拿右边,奇数编号的哲学家先拿右边再拿左边便可。这样不但不会出现死锁,不相领的哲学家还可同时就餐,保证四根筷子可以同时利用

读者-写者问题

有四种状况:

  • 容许多个读者同时读。多个读者能够同时进入临界区
  • 在读的时候不能写。读者进入临界区后,后面的写者必须等待
  • 在写的时候不能读。写者进入临界区后,后面的读者必须等待
  • 在写的时候不能写。写者进入临界区后,后面的写者必须等待

根据读者、写者的实现机制不一样,可能采用不一样的优先策略:

  • 读者优先策略:优先让就绪的读者立刻执行,可能致使写者饥饿
  • 写者优先策略:优先让就绪的写者立刻执行,可能致使读者饥饿

java的读写锁就是利用管程思想实现了读者-写者机制

死锁

多个进程之间互相等待对方释放资源,而且永远没法结束的一种现象

死锁出现的必要条件:

  1. 资源互斥:至少有一个资源是互斥类型的,不能容许两个或两个以上的进程同时访问

  2. 占有并等待:至少有一个进程占有资源并等待另外一部分资源才能完成任务

  3. 非抢占:资源是不可抢占的,必须由占用进程自愿释放

  4. 循环等待:必须存在相似以下现象

    • A,B,C三个进程和一组资源
    • A申请B占用的资源
    • B申请C占用的资源
    • C申请A占用的资源
    • 三者造成循环等待

只有知足这四个条件,才会出现死锁

针对死锁的处理方法:

  • 死锁预防
  • 死锁避免
  • 死锁检测和恢复
  • 忽略死锁

死锁预防

提早预防死锁,死锁的必要条件有四个,只要其中任何一个不知足,死锁就不会出现

  • 资源共享:有些资源没法共享
  • 避免占用并等待:避免使用部分资源的同时等待另外一部分资源,须要的时候提早申请全部资源,这样致使资源利用率降低
  • 抢占:要保证资源必须是可存储可恢复的才能抢占
  • 避免循环等待:没法避免用户使用资源的顺序,因此没法避免循环等待

死锁避免

在进程申请资源的时候动态判断有没有出现死锁的可能,若是有就不容许进程使用资源

算法:当进程请求资源时,系统判断资源分配后是否处于安全状态,若是是则分配,不然暂停分配

安全状态指:

一系列进程P1到Pn,保证Pi申请的资源<=当前可用资源+全部Pj占有的资源,j<i。若是Pi申请的资源不能当即分配,暂停Pi,等到全部Pj完成

安全状态与死锁的关系

银行家算法

是一种基于安全状态判断的死锁算法

核心思想:每一个客户会申请须要的最大资金量,银行会根据你已经占有的资金,力所能及的将剩余部分借给客户。客户在借完资金后会及时归还,保证银行有充足的储备继续借款

  • 银行:操做系统
  • 资金:资源
  • 客户:进程

数据结构:

  • n:线程数量
  • m:资源类型数量
  • Max:n*m矩阵,表明每一个进程所需的最大资源数量,Max[i,j]表明进程i须要的资源j的最大数量
  • Allocation:n*m矩阵,表明每一个进程已占用的资源数量,Allocation[i,j]表明进程i占有的资源j的数量
  • Need:n*m矩阵,表明每一个进程须要的资源数量,Need=Max-Allocation
  • Available:m维数组,表明系统中每种资源的可用数量
  • Finished:n维数组,表明进程是否执行完成(释放资源)

算法:

  1. Work=Available,将可用资源拷贝到工做空间Work,将Finished元素初始化为false
  2. 找到进程i,知足Finished[i]=false,Need[i] < Allocable的进程,表示i能够申请资源,不然执行步骤4
  3. Work += Allocation[i],Finished[i]=true,由于i申请的资源最后会被回收,因此忽略分配过程,直接跳到回收环节。执行步骤2
  4. 当全部Finished元素都是true时,说明发现了安全分配的进程序列,系统处于安全状态;不然会出现死锁,不会为进程分配资源

死锁检测

基于银行家算法的思想,判断会不会出现死锁。在完成算法后,存在Finished[i]=false,表明进程i会致使死锁状态,称i为死锁进程

死锁恢复

经过结束进程来恢复死锁:

  1. 一次终止全部死锁进程
  2. 一次终止一个,直到消除死锁

显而后者更好,可是会根据以下条件考虑该优先终止哪一个进程:

  • 进程优先级:优先低
  • 交互进程仍是批处理进程:优先批处理
  • 进程已执行时间和还须要的执行时间:优先时间短
  • 进程已占用资源和还须要的资源:优先资源少
  • 终止进程数目:优先关联进程少

经过抢占进程资源来恢复死锁:

将死锁进程回退到安全状态,并抢占它的资源,开销小,可是会出现饥饿现象——进程恢复后又进入死锁状态,继续被抢占

进程通讯

简称IPC,进程间交互信息

  • 直接通讯:进程A、B,A直接将消息发给B,二者必须都在线
  • 间接通讯:进程A、B,A将消息发给内核提供的消息队列C,B从C取消息,二者可离线

能够分为阻塞与非阻塞

  • 阻塞:同步,A发消息给B,必须等待B收到A才能执行后面的操做
  • 非阻塞:异步,A发消息给B,不用等待也能执行后面的操做

根据通讯链路的缓冲容量可分为

  • 0容量:发送方必须等待接收方
  • 有限容量:发送方能够一直发,直到容量充满
  • 无限容量:发送方能够一直发,没有限制

信号

进程间基于中断传递消息的一种机制。如经过ctrl+c终止运行的程序

原理:应用进程注册信号处理函数,当出现中断时,操做系统会根据信号回调信号处理函数。ctrl+c是操做系统默认给每一个进程添加的信号处理函数

缺点是局限性大,只能传递信号类型的消息

管道

进程基于内存文件的通讯方式

在命令ls | more程序中

  1. shell首先建立管道,管道拥有读写两端
  2. 建立ls进程,将它的的stdout指向管道写端
  3. 建立more进程,将它的stdin指向管道读端

这样就在内存中创建了两个进程的消息通道

消息队列

内核提供一个FIFO队列,提供给不一样进程通讯

优势:

  • 使用简单

缺点:

  • 将消息从用户态传递给内核,须要进行用户态内核态切换,并且传递消息的过程就是数据拷贝的过程,开销大

共享内存

  • 不一样线程之间能够直接共享内存
  • 不一样进程之间共享内存,须要明确设置共享内存段

优势:

  • 不须要从用户态切换到内核态,也不须要数据拷贝,开销小

缺点:

  • 须要用户层处理数据同步的问题

实现机制:

基于页表来实现。不一样进程有本身的页表,操做系统将它们的页表映射到同一个物理地址空间,就完成了进程间共享内存

文件系统

操做系统中将磁盘数据组织成文件系统,提供对这些持久化数据的操做

功能:

  • 为数据分配和管理磁盘空间
  • 管理文件集合
  • 保障数据可靠性和安全性

文件系统须要被挂载才能被访问,它被挂载的目录称为挂载点

根据文件系统的类型分为:

  • 本地磁盘文件系统:ext四、NTFS等
  • 网络/分布式磁盘文件系统:NFS、GFS等

分布式文件系统会产生安全性、一致性等问题,比本地磁盘文件系统更复杂

文件

文件系统组织磁盘上数据的方式,并抽象了一些属性用于简化这些持久化数据的管理

文件属性:

  • 文件名
  • 建立日期
  • 类型
  • 位置
  • 大小
  • 建立者
  • ...

将文件分为两部分:

  • 文件头:文件属性、文件存储位置和顺序,在文件系统中管理。这是文件系统管理文件的基本依据
  • 文件数据:磁盘上的字节数据

目录

目录是一种特殊的文件,其内容是文件索引表,表的每一项是二元组<文件名,指向文件的指针>。操做目录就是操做文件索引表

应用程序都是经过系统调用来访问目录

文件别名

让多个文件名指向同一个文件,看起来就像给文件起了别名

有两种起别名的方式:

  • 硬连接:A为B的硬连接,A和B地位对等,都指向文件实体。文件有一个计数,记录多少个文件名指向它。每增长一个硬连接计数加一,删除一个硬连接计数减一,当计数为零时,文件才会真正被删除。操做方式:ln src dest
  • 软连接:A为B的软连接,A是一个指向B的“快捷方式”,A存储了B的路径。删除B,A成为死连接;删除A,B不受影响。操做方式:ln -s src dest

文件目录循环

可能出现文件子目录指向父目录,致使出现循环目录的状况,这种状况会导致目录遍历永远没法结束

常见的处理方式是设置一个最大遍历层数

名字解析

文件系统中的名字解析就是根据文件路径,读取文件信息

/bin/ls

解析过程为:

  1. 读取根目录的文件头
  2. 读取根目录的数据块,搜索bin项
  3. 读取bin目录的文件头
  4. 读取bin目录的数据块,搜索ls项
  5. 读取ls目录的文件头

当前工做目录

进程每读取文件的时候,都要从根目录进行名字解析,会致使效率很低。因此操做系统为每一个进程设置了一个目录PWD,表明当前工做目录。这样当访问一些当前目录存在的文件时,不须要再从根目录开始遍历

这种机制提供了一种基于相对路径来访问文件的方式

虚拟文件系统

简称VFS,是对底层各个不一样文件系统的一个抽象(各个磁盘文件系统和网络文件系统),对上层提供一个统一的api(open、close、read、write)

全部文件系统实现不一样,但有些公共数据结构仍是一致的

  • 超级控制块
  • 文件控制块
  • 目录项控制块

超级控制块

简称superblock,每一个文件系统有一个,记录了文件系统相关信息:文件系统的类型、数据块个数与大小、空闲块个个数与大小等信息

在文件系统挂载时加载到内存

文件控制块

简称inode,每一个文件有一个,记录了文件相关信息:文件大小、数据块位置、访问权限、拥有者等

在访问文件时加载到内存

目录项控制块

简称dentry,每一个目录项(文件、目录)有一个,记录了目录相关的信息:文件控制块的位置、父目录、子目录等

与文件控制块不一样的是,它侧重于对目录信息的描述,并且VFS利用目录项控制块将文件系统组织成树状结构

在遍历目录时会加载到内存

文件系统视图结构

文件系统组织视图

文件系统存储视图

其中

  • vol:superblock
  • dir:dentry
  • file:inode
  • datablock:数据块

数据块缓存

内存中缓存磁盘数据块的一部分区域

在读的时候提供预读取:预先读取后面的数据块

在写的时候延迟写:写到缓存就返回,操做系统定时flush

页缓存

简称page cache。因为虚拟内存是基于页来管理内存,文件系统基于块来管理文件数据,页和块大小不一样,须要一个机制统一二者的管理方式

页缓存实现中屏蔽了底层数据块的管理,上层统一提供基于页的管理方式,供虚拟内存使用

在应用进程进行内存映射mmap(将文件映射到内存)或者文件读写时,虚拟内存会先访问页缓存,找不到就会出现缺页中断,触发对文件系统的查询,而后再把数据加载到页缓存

文件描述符

不少文件操做都须要搜索文件,为了保证只在第一次读取的时候搜索,操做系统须要在第一次访问的时候打开文件,返回一个文件描述符,文件描述符就是一个索引,指向打开文件表中的打开文件,操做系统在该打开文件中维护了一些状态信息,保证之后在操做该文件的时候无需再次搜索

打开文件表

操做系统会建立一个全局打开文件表,同时也会为每一个进程建立一个打开文件表,其中维护了这些信息:

  • 文件指针:每一个进程都会维护一个读写指针,互不干扰
  • 打开的次数:指向全局表的该文件,记录了该文件被打开的次数,当次数为0时才事该文件才能被删除
  • 文件的磁盘位置:指向全局表的该文件,之后每次文件操做都只要从该表(内存)读取位置便可,无需搜索磁盘
  • 访问权限:每一个进程维护一个访问权限信息,如只读、读写等

读取一个文件的流程:

  • 打开文件,返回文件描述符
  • 根据文件描述符定位进程的打开文件表
  • 根据进程打开文件表定位全局的系统打开文件表
  • 根据全局打开表定位文件控制块
  • 根据文件控制块定位文件的数据块地址
  • 将数据块地址转换为磁盘扇区物理地址
  • 将扇区地址读到内存buffer
  • 将内存buffer数据返回给应用进程

打开文件锁

多个进程打开文件的时候,可能会出现并发安全问题

操做系统提供了两种文件锁:

  • 强制:打开文件时能够指定该文件被打开,其余进程不能打开
  • 劝告:进程根据锁状态决定怎么作

文件分配

当文件须要磁盘空间的时候,文件系统须要为该文件分配数据块

经常使用分配算法:

  • 连续分配
  • 链式分配
  • 索引分配

分配的时候主要考虑两点:

  • 碎片多少:因为数据块大小固定,因此内碎片已经肯定,这里考虑碎片主要是外碎片。即数据块分配给文件后,若是磁盘中有大量较小的空闲块,就说明外碎片较多
  • 访问性能:访问文件数据块的速度

连续分配

根据文件须要的块大小,查找磁盘中空闲块,发现有足够大小的,就分配给文件

优势:

  • 实现简单
  • 访问性能高

缺点:

  • 碎片多

链式分配

根据文件须要的块大小,找到磁盘中各个空闲块进行分配,并经过链表指针联系到一块

优势:

  • 没有碎片
  • 实现简单

缺点:

  • 访问性能差

索引分配

根据文件须要的块大小,找到磁盘中的空闲块进行分配,并单独利用一个块存储索引这些数据块

优势:

  • 没有碎片
  • 访问性能高

缺点:

  • 须要单独的索引块,若是文件较大,可能须要更多索引块,开销较大

实际unix文件系统(UFS)会结合索引分配和链式分配组成多级索引分配算法

空闲块管理

要为文件分配数据块,首先得知道全部空闲块在哪。先来看几种空闲块管理机制:

  • 位图:用位图表示全部块,0表示空闲,1表示已分配

    • 优势:查询快
    • 缺点:磁盘越大,位图存储开销越大
  • 链表:在一个空闲块中加入指针指向下一个空闲块,造成链表。

    • 优势:存储开销小
    • 缺点:查找快
  • 索引:经过一个块存储索引,记录这些空闲块的位置

    • 优势:查找快
    • 缺点:若是表大,须要更多的索引块,开销较大

实际系统是组合这几种方式

磁盘分区

机械硬盘都是经过磁头进行磁盘寻址,若是磁盘越大,寻址时间越大。因此将磁盘划分红多个分区能够减小寻址时间,提高磁盘访问速度,可是若是同时在多个分区之间切换也会带来性能的降低

分区就是一组柱面的集合,每一个分区能够视为一个独立的磁盘

分区组织方式:

  1. 在一个磁盘有多个分区,每一个分区一个独立的文件系统,提升磁盘访问速度
  2. 在一个分区有多个磁盘,提升存储空间

其中第二种方式,在一个分区使用多块磁盘,能够基于此方案提高文件系统的可靠性与吞吐量,利用到的就是磁盘冗余技术

磁盘冗余阵列

基于上述第二种方案,能够利用冗余磁盘提升吞吐量(并行)与可靠性(冗余)

磁盘冗余阵列简称RAID,是一种基于冗余磁盘的思想实现的磁盘管理技术,经常使用的有RAID-0,RAID-1,RAID-5等

能够经过文件系统或者RAID硬件控制器来实现

RAID-0

将数据块分红多个小块,并行存储在独立的磁盘上,这种方案能够提高吞吐量

RAID-1

向两个盘写,从任意一个读,利用其中一个作镜像盘来提升可靠性。这种基于冗余盘的思想同时缓解了一个盘的读压力,至关于也会提高读性能

RAID-4

利用一个盘作校验盘,其余盘基于RAID-0的方案拆分小块的基础上并行访问,校验盘的数据是其余盘数据的校验和。这种方案能够保证即便其中一个数据盘坏了,也能基于校验和和剩余磁盘来恢复它的数据。同时由于利用到了RAID-0的并行方案,也提高了读写性能

RAID-5

与RAID-4思想一致,可是为避免校验盘称为瓶颈,取消惟一的校验盘,在每次数据写入时,将计算好的校验和轮流存放在全部磁盘上,造成一个分布式系统的负载均衡算法

RAID-3

RAID-4和RAID-5都是基于块计算校验和,而RAID-3是基于位来计算校验和

RAID-6

RAID-5每次写都会计算出一个校验块,保证最终能够容忍一个磁盘坏掉。RAID-6则是计算出两个校验块,保证能够容忍两个盘坏掉

RAID 0+1

RAID嵌套技术之一

让两个磁盘采用RAID-0方案并行读写,再利用两个盘做为镜像盘同步数据。安全性、性能较好,成本较高

RAID 1+0

RAID嵌套技术之一

让两个盘采用RAID-1方案一个主一个备,再利用两个盘一主一备,先后二者采用RAID-0的方案并行读写。安全性、性能较好,成本较高

I/O设备

能够分为三类:

  • 字符设备:如键盘鼠标等
  • 块设备:如磁盘驱动器等
  • 网络设备:如无线、以太网、蓝牙等

各设备有不一样的I/O实现。根据cpu与这些设备的交互,将I/O分为阻塞I/O、非阻塞I/O、异步I/O

阻塞I/O

用户进行读写操做后,须要等待设备完成读写操做,并返回结果

非阻塞I/O

用户进行读写操做后,不须要等待返回结果

异步I/O

用户进行读写操做后,不须要等待返回结果,可是操做系统完成读写操做后会通知用户

I/O设备控制器

cpu经过北桥与内存、显卡等高速设备链接,经过南桥与I/O等设备链接

cpu依赖I/O设备控制器与I/O设备交互,设备控制器结构为:

  • 总线接口:cpu经过总线访问总线控制器,总线控制器访问总线接口;I/O设备产生中断,响应给中断控制器,控制器反馈给cpu
  • 硬件控制器:与I/O设备交互
  • 内存映射:内存映射后,存储映射的内存地址,cpu访问该内存地址就至关于访问I/O设备
  • 寄存器:存储I/O状态信息,如I/O操做是否执行完成等

cpu与I/O设备交互的三种方式:

  • 轮询:cpu不断轮询I/O设备
  • 中断:I/O设备经过中断将事件通知给cpu
  • DMA:I/O设备能够不经过cpu,直接经过DMA访问内存,避免cpu忙碌

I/O指令

cpu经过I/O端口访问I/O设备

内存映射

MMU将I/O设置的存储地址映射到内存,cpu经过访问内存来实现对I/O设备的访问

I/O结构

I/O访问流程就是从用户进程上到下的访问流程,当完成访问后设备经过中断,从下到上通知用户进程

I/O数据传输

cpu和I/O设备之间基于两种方式交换数据:

  • 程序I/O:直接经过cpu指令控制I/O设备交互数据

    • 优势:简单
    • 缺点:数据量大的时候会致使cpu忙碌
  • DMA:cpu让I/O设备经过DMA直接读写内存,cpu访问内存便可获得这些数据

    • 优势:传输数据的时候不阻塞cpu
    • 缺点:复杂

完成数据传输后,设备须要通知cpu,有两种机制:

  • 轮询:cpu轮询控制器寄存器,获取设备状态信息,根据状态进行操做

    • 优势:简单
    • 缺点:轮询间隔短会致使cpu忙碌,间隔长会致使响应延迟高
  • 中断:设备经过中断将事件反应给cpu。cpu每执行一条指令后都会进行中断检查,发现中断后立马执行中断请求,而后再恢复执行原来的进程

    • 优势:实时性高,延迟最多就一条指令
    • 缺点:复杂,而且当中断请求特别多的时候,cpu会花费大量时间响应中断,开销大

DMA

设备控制器直接访问内存的一种机制

基于DMA和中断的方式读取磁盘数据的流程:

  1. 应用发起I/O请求
  2. 磁盘驱动请求磁盘控制器
  3. 磁盘控制器读取数据,并通知DMA控制器
  4. DMA控制器将数据发给内存,并产生中断
  5. 中断会通知到cpu,cpu将数据传递给应用进程

磁盘I/O传输时间

磁盘工做机制

读取数据其实就是磁头定位到指定的磁道,而后从磁道读取指定扇区的内容

寻道时间:磁头定位到磁道所花的时间

旋转延迟:在磁头定位到磁道后,磁盘不断旋转,直到磁头发现数据所在扇区,这个时间开销就是旋转延迟

磁盘I/O传输时间=等待设备可用+等待传输通道可用+寻道时间+旋转延迟+数据传输时间

磁盘调度算法

是一种优化寻道时间的算法,基于如下前提,该算法才有意义:

  • 寻道是最耗时的环节
  • 同一个磁盘有多个请求。若是每一个磁盘只有一个请求,那么没法优化
  • 大量随机请求致使性能差

当大量随机I/O请求到来时,磁头会前后指向不一样的磁道,致使寻道开销较大,使用一种优化的顺序减小磁头须要“走动的距离”,是调度算法须要考虑的

FIFO磁盘调度算法

优先服务先来的I/O请求

优势:

  • 公平

缺点:

  • 实际使用接近随机I/O,性能差

SSTF磁盘调度算法

优先处理从当前磁臂移动距离最短的I/O请求

优势:

  • 总寻道时间短,性能高

缺点:

  • 容易致使请求饥饿,致使磁道距离较远的I/O请求一直得不到执行

SCAN磁盘调度算法

扫描算法,也成电梯算法,先从一个方向处理全部I/O请求,到头以后从另外一个方向处理剩余I/O请求

这种算法缓解了SSTF中的饥饿状况,可是仍是会出现某个方向磁道边缘的I/O请求饥饿的状况

C-SCAN磁盘调度算法

循环算法,从一个方向处理全部I/O请求直到磁道尽头,磁头立刻移动到另外一端(中间不处理请求),沿着这个方向继续处理全部请求

相比较SCAN,缓解了饥饿现象。保证磁道边缘的请求也能很快获得处理

C-LOOK磁盘调度算法

C-SCAN会沿着一个方向处理I/O请求直到磁道尽头,C-LOOK的优化是沿着一个方向处理I/O请求直到该方向没有I/O请求,其余都同样

现代操做系统每每会结合这些算法进行使用,保证寻道时间短的同时,又不会出现请求饥饿的状况

磁盘缓存

因为访问磁盘的速度和访问内存的速度查了几个量级,因此须要使用磁盘缓存来缓解这个差别致使的瓶颈

这个缓存能够采用单缓存和双缓存

  • 单缓存:有一个缓存区,I/O设备和cpu同时只能有一个进行访问
  • 双缓存:有两个缓存区,I/O设备和cpu能够同时操做各自的缓存区

访问频率置换算法

磁盘缓存须要一套置换算法来保证,常常用的扇区数据被缓存,少用的被置换到磁盘

这种算法结合了LRU和LFO的优势进行处理,短周期的以LRU为准,长周期的以LFO为准

相关文章
相关标签/搜索