操作系统学习总结-杭电

操作系统

发展与分类

​ 批处理操作系统

单道批处理

多道批处理—资源利用率高,系统吞吐量大,缺点-作业平均周转时间长,无交互能力

​ 分时系统

交互性(主要),多路性,独立性,及时性,CPU工作分成时间片,让每个终端用户感觉自己独占一个计算机,

​ 实时系统

及时和可靠性都有很好的特点,在交互性比分时弱

常用于机票订购系统,银行财务系统,情报检索系统

​ 微机系统
​ 网络系统

网络通信管理,网络资源管理,网络安全管理,提供网络服务

​ 分布式系统

一切功能集中在一台主机称为集中式处理系统,均匀把控制单元分在各个站点称为分布式系统

​ 嵌入式

机器人,掌上电脑,车载系统等等都是嵌入式系统,

规模小,硬件配置低

应用领域差别大

微小,实时,专业,可靠

特征与功能

​ 特征
​ 并发:并行性是在同一时刻发生,多道是同一时间间隔

​ 共享:分为互斥共享(打印机)和同时共享(磁盘)
​ 虚拟
​ 异步(不确定)
​ 功能
​ 处理机管理-对处理器进程分配调度
​ 储存器管理-管理内存资源
​ 设备管理-除了CPU内存之外的设备都管
​ 文件管理
​ 提供用户接口

用户接口

​ 程序接口API–操作系统内核与应用程序之间
​ 系统调用
​ 系统调用号、系统调用服务例程、系统调用入口表
​ 系统调用号及参数传递
​ 系统调用处理流程
​ 命令接口CIL
​ 联机用户接口
​ 键盘操作命令
​ 脱机用户接口
​ 作业控制语言
​ 图形接口GUI

内核结构

​ 结构模型
​ 整体化(Linux内核结构)早期unix
​ 优点:功能的实现和高效率
​ 缺点:缺乏清晰的程序结构、 错误多、难以维护、模块独立性差

​ 模块化结构
​ 优点:有利于系统设计和扩展

​ 缺点:模块间存在着复杂的依赖关系,使OS结构不清

​ 层次结构-Unix
​ 优点:易保证系统的正确性,易扩充和易维护性
​ 缺点:系统效率降低

​ 微内核结构windows NT

​ 提高系统的可扩展性
增强系统的可靠性、可移植性
提供了对分布式系统的支持
融入了面向对象技术

​ 没有高效性

处理器指令

计算机的所有操作都是由机器指令/计算机指令所决定的。每条计算机指令必须包含处理器执行所需的信息:操作码、源操作数、目的操作数和下一条指令地址。

image-20210403143103937

每条计算机指令必须包含处理器执行所需的信息:操作码、源操作数、目的操作数和下一条指令地址

操作码代表了指令要完成的具体操作。操作码分成几类:数据传递、算术运算、逻辑运算、转换、输入输出、系统控制和控制传递。

源操作数是指具体操作所需的输入,可以是一个也可以是多个。源操作数一般来源于寄存器、内存。

目的操作数是指具体操作的结果,可在寄存器或者内存中。

下一条指令地址给出了当前这条指令执行完成后去哪里取下一条指令。

image-20210403150836309

在指令的执行过程中,根据指令计数器给出的地址从内存读取一条指令到处理器相应的指令寄存器中,处理器通过分析这条指令所包含的各个字段获取操作数,并执行操作码所指定的操作,最后将操作结果存入目的操作数当中。同时,在读入这条指令后,指令计数器根据当前指令所占存储空间,自动的指向下一条指令的地址。

操作码,源操作码,目的操作数都可以用符号表示

寻址方式

寻址方式就是处理器根据指令中给出的地址信息来寻找物理地址的方式,是确定本条指令相关的数据地址以及下一条要执行的指令地址的方法。

根据查找数据地址还是指令地址,寻址方式分为两类,即指令寻址方式和数据寻址方式,前者比较简单,后者比较复杂。但是指令寻址与数据寻址是交替进行的,先进行指令寻址,查找到指令后读入处理器,在执行这条指令的过程中需要进行多次的数据寻址,找到操作数。执行完这条指令后,又一次进行指令寻址,查找下一条指令。此过程交替执行。

指令寻址方式
  • 顺序寻址方式

    指令一般顺序的存储在内存中,当执行一段程序时,通常是一条指令接一条指令地顺序取指执行。也就是说,从存储器取出第1条指令,执行这条指令,接着从存储器取出第2条指令,执行第2条指令,依次执行。

    必须使用程序计数器(又称指令计数器)PC来记录指令的地址。处理器根据PC给出的地址取出相应指令,同时修改PC的值,使其指向下一条指令地址。

  • 跳跃寻址方式

    当程序执行转移或者函数调用等相关指令时,需要采取跳跃寻址方式。所谓跳跃,是指下条指令的地址码不是由程序计数器PC给出,而是由正在处理器上执行的指令给出。程序跳跃后,按新的指令地址开始顺序执行。因此,PC的内容也必须相应改变,以便及时跟踪新的指令地址。

    特点:可以实现程序转移,构成循环程序,从而能缩短程序长度,或将某些程序作为公共程序引用。

    指令系统中的各种条件转移、无条件转移指令以及函数调用,就是为了实现指令的跳跃寻址而设置的。

数据寻址方式

形成操作数的有效地址的方法称为数据寻址方式。下面介绍一些比较典型又常用的数据寻址方式。

(1)隐含寻址

指令中隐含着操作数的地址

(2)立即寻址

指令的地址字段指出的本身就是操作数

(3)直接寻址

指令地址字段中直接给出操作数的地址及运算结果的存放地址

(4)间接寻址

指令地址字段指向操作数的指针

(5)寄存器寻址方式和寄存器间接寻址方式

操作数放在CPU寄存器里,采用寄存器寻址方式

(6)相对寻址方式

指令给出的地址是操作数位置相对于当前指令位置的偏移量,其有效地址就是当前指令地址加上偏移量。

(7)基址寻址方式

将CPU中的基址寄存器内容加上变址寄存器的内容形成操作数地址

(8)变址寻址方式

CPU中某个变址寄存器的内容加上偏移量形成操作数地址

(9)块寻址方式

经常用在输入输出指令,以实现外存储器或外围设备同内存之间的数据块传送。

image-20210403152401958

寄存器

分类

(1)数据寄存器:用来储存整数数字。在某些简单/旧的CPU中,特别地,数据寄存器是累加器,作为数学计算之用。

(2)地址寄存器:用来存放存储器地址,用来访问存储器。

(3)通用目的寄存器(GPRs):保存数据或地址,即结合了数据寄存器或地址寄存器的功能。

(4)浮点寄存器(FPRs):用来储存浮点数据。

(5)常数寄存器:用来存放只读的数值(例如0、1、圆周率等等)。

(6)向量寄存器:用来储存由向量处理器运行SIMD(Single Instruction, Multiple Data)指令所得到的数据。

(7)特殊目的寄存器:储存CPU内部的数据,比如程序计数器、堆栈寄存器、程序状态寄存器等。

(8)指令寄存器:储存当前正在被执行的指令。

(9)索引寄存器:用于在程序运行过程中更改运算对象的地址。

X86寄存器

X86 有14个32位寄存器,按用途可分为通用寄存器、指令指针、标志寄存器和和段寄存器4类。

通用寄存器

可用于传送和暂存数据,也可参与算术逻辑运算,并保存运算结果。除此之外,它们还各自具有一些特殊功能。通用寄存器的长度取决于机器字长。

32位CPU的通用寄存器有8个: EAX,EBX,ECX,EDX,EBP,ESP,ESI,EDI,又可以分成2组,一组是数据寄存器(4个),另一组是指针寄存器及变址寄存器(4个)。

数据寄存器

  1. EAX:累加寄存器,常用于运算,在乘除等指令中指定用来存放操作数。另外,所有的I/O指令都使用EAX与外设传送数据。
  2. EBX:基址寄存器,常用于地址索引。
  3. ECX:计数寄存器,常用于计数,如在移位指令、循环(loop)和串处理指令中用作隐含的计数器。
  4. EDX:数据寄存器,常用于数据传递。

指针寄存器和变址寄存器:

  1. ESP:堆栈指针,与SS配合使用,可指向目前的堆栈位置。
  2. EBP:基址指针寄存器,可用作SS的一个相对基址位置。
  3. ESI:源变址寄存器,可用来存放相对于DS段之源变址指针。
  4. EDI:目的变址寄存器,可用来存放相对于ES 段之目的变址指针。

这4个32位寄存器主要用来形成操作数的有效地址。

  • 指令指针EIP

指令指针EIP是一个32位专用寄存器,它指向当前需要取出的指令字节,当BIU(总线接口部件)从内存中取出一个指令字节后,EIP就自动加上所取出指令的长度,以指向下一个指令字节

注意,EIP指向的是指令地址的段内地址偏移量

处理特权级

​ 区分运行状态原因
​ 管态 核心态,管理程序(系统态):控制程序运行
​ 目态用户程序(用户态):被控制,执行处理器所处的状态,只能访问自己的存储区域,不能执行特权指令,只能向操作系统提出资源使用需求
​ 两种运行状态区别
​ 特权指令概念

存储系统

存储系统是指计算机中由存放程序和数据的各种存储设备、控制部件及管理信息调度的(硬件)和算法(软件)所组成的系统。计算机的主存储器不能同时满足存取速度快、存储容量大和成本低的要求,在计算机中必须有速度由慢到快、容量由大到小的多级层次存储器,以最优的控制调度算法和合理的成本,构成具有性能可接受的存储系统。

制约计算机存储器设计的因素主要有三个:容量、速度、价格。

为了权衡以上多种因素,目前主要采用存储器层次结构。在现代计算机系统中存储层次可分为高速缓冲存储器、主存储器、辅助存储器三级。高速缓冲存储器用来改善主存储器与中央处理器的速度匹配问题。辅助存储器用于扩大存储空间。

image-20210403155316053

image-20210403155340639

内存

  • 只读ROM

ROM表示只读存储器(Read Only Memory),在制造ROM的时候,信息(数据或程序)就被存入并永久保存。只能读出,一般不能写入,即使机器停电,数据也不会丢失。ROM一般用于存放计算机的基本程序和数据,如BIOS ROM。

  • 随机RAM

随机存储器(Random Access Memory)表示既可以从中读取数据,也可以写入数据。当机器电源关闭时,存于其中的数据就会丢失。内存条(SIMM)就是用作电脑的内存,它是将RAM集成块集中在一起的一小块电路板,插在计算机中的内存插槽上,以减少RAM集成块占用的空间。

区分物理存储器和存储地址空间这两个概念

物理存储器是指实际存在的具体存储器芯片。如主板上装插的内存条和装载有系统的BIOS的ROM芯片,显示卡上的显示RAM芯片和装载显示BIOS的ROM芯片,以及各种适配卡上的RAM芯片和ROM芯片都是物理存储器。

存储地址空间是指对存储器编码(编码地址)的范围。所谓编码就是对每一个物理存储单元(一个字节)分配一个号码,通常叫作“编址”。分配一个号码给一个存储单元的目的是为了便于找到它,完成数据的读写,这就是所谓的“寻址”,因此有人也把地址空间称为寻址空间。

高速缓存存储器Cache

高速缓冲存储器(Cache),其原始意义是指存取速度比一般随机存取存储器(RAM)更快的一种RAM,使用昂贵但较快速的静态随机存储器(SRAM)技术。

高速缓冲存储器是介于主存与CPU之间的一级存储器,由静态存储芯片(SRAM)组成,容量较小但速度比主存快得多,接近于CPU的速度,其最重要的技术指标是它的命中率。高速缓冲存储器与主存储器之间信息的调度和传送是由硬件自动进行的。

由三大部分组成:

Cache存储体:存放由主存调入的指令与数据。

地址转换部件:建立目录表以实现主存地址到缓存地址的转换。

置换换部件:在缓存已满时按一定策略进行数据替换,并修改地址转换部件中的目录表。

由高速存储器、联想存储器、置换逻辑电路和相应的控制线路组成。

地址映象与转换

(1)全相联方式

全相联方式的地址映象规则是:主存储器中的任意一块可以映象到Cache中的任意一块。其基本实现思路是:①主存与缓存分成相同大小的数据块;②主存的某一数据块可以装入缓存的任意一块空间中。

目录表的容量应当与缓存的块数相同

全相联方式的优点是命中率比较高,Cache存储空间利用率高;

缺点是访问相关存储器时,每次都要与全部内容比较,速度低,成本高,因而应用少。

image-20210403164318357

(2)直接相联方式

直接相联方式的地址映象规则是主存储器中某一块只能映象到Cache的一个特定的块中。其基本实现思路是:①主存与缓存分成相同大小的数据块;②主存容量应是缓存容量的整数倍,将主存空间按缓存的容量分成区,主存中每一区的块数与缓存的总块数相等;③主存中某区的一块存入缓存时只能存入缓存中块号相同的位置。

目录表的容量与缓存的块数相同

直接相联方式的优点是地址映象方式简单,数据访问时,只需检查区号是否相等即可,因而可以得到比较快的访问速度,且硬件设备简单;缺点是置换操作频繁,命中率比较低。

image-20210403164326979

(3)组相联映象方式

组相联映象方式的地址映象规则是主存储器中某一块只能存入缓存的同组号的任一块中。其基本实现思路是:①主存和缓存按同样大小划分成块;②主存和缓存按同样大小划分成组;③主存容量是缓存容量的整数倍,将主存空间按缓存区的大小分成区,主存中每一区的组数与缓存的组数相同;④当主存的数据调入缓存时,主存与缓存的组号应相等,也就是各区中的某一块只能存入缓存的同组号的空间内,但组内各块之间可任意存放,即从主存的组到缓存的组之间采用直接映象方式;在两个对应的组内部采用全相联映象方式。

组相联映象方式的优点是块的冲突概率比较低,块的利用率大幅度提高,块失效率明显降低;而缺点是实现难度和造价要比直接映象方式高。

image-20210403164305697

堆栈

函数调用过程中对堆栈的操作情况如图2-5所示,一个主函数调用了一个子函数的过程。
调用过程具体步骤:

① 执行call之前,esp指向栈顶,ebp指向栈底;

② 执行call时,cs:eip原来的值被保存到栈顶,然后cs:eip的值指向被调用程序的入口地址;

③ 进入被调用程序,第一条指令:pushl %ebp,第二条指令:movl%esp,%ebp

④ 此时进入被调用程序,进入后栈可进行入栈出栈等常规操作;

退出被调用程序,第一条指令:movl%ebp,%esp,第二条指令:popl %ebp,第三条指令:ret,此时从被调用程序退出,通过ret将地址恢复到eip中。

image-20210404095108080

中断

中断与异常

  • 概念

中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动暂停正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

程序运行过程中,如果系统外部、系统内部或者当前运行程序本身出现紧急事件,处理机将立即暂停当前程序的运行,自动转入相应的处理程序(中断服务程序),待处理完后,再返回原来的程序运行,这整个过程称为程序中断。

  • 中断目的

①提高计算机系统效率

②维持系统可靠正常工作

③满足实时处理要求

④提供故障现场处理手段

  • 分类

功能
① 输入输出中断 I/O传输结束或出错中断
② 外中断 时钟中断、操作员控制台中断、通信中断等
③ 机器故障中断 电源故障、主存取指令错等
④ 程序性中断 定点溢出、用户态下用核态指令、非法操作
⑤ 访管中断 对操作系统提出某种需求时所发出的中断

来源

① 中断 由处理机外部事件引起的中断
② 俘获 由处理机内部事件引起的中断,x86中称异常

异常

1)处理器探测异常探测到的一个反常条件所产生的异常,取决于处理器控制单元产生异常时保存在内核态堆栈eip寄存器中的值。

①故障:保存在eip中的值是引起故障的指令地址,因此,当异常处理程序终止时,那条指令会被重新执行。

②陷阱:保存在eip中的值是一个随后要执行的指令地址。只有当没有必要重新执行已终止的指令时,才触发陷阱。陷阱的主要用途是为了调试程序。在这种情况下,中断信号的作用是通知调试程序一条特殊指令已被执行(例如到了一个程序内的断点)。一旦用户检查到调试程序所提供的数据,它就可能要求被调试程序从下一条执行重新开始执行。

③异常中止:发生一个严重的错误,控制单元出了问题,不能在eip寄存器中保存引起异常的指令所在的确切位置。

2)编程异常

由编程者发出请求产生的异常。是由int或int3指令触发的;当into(检查溢出)和bound(检查地址出界)指令检查的条件不为真时,也引起编程异常。

  • 中断屏蔽
    由特权指令设置中断屏蔽寄存器

①可屏蔽中断:可由程序控制其屏蔽性的中断称为可屏蔽中断,处于屏蔽状态时,处理机将忽略该中断信号。I/O设备发出的所有中断请求都属于可屏蔽中断。

②非屏蔽中断:与可屏蔽中断相对应,不能由程序控制其屏蔽性,处理机一定要立即处理的中断称为非屏蔽中断或不可屏蔽中断。非屏蔽中断数量较少,通常是由一些紧急事件引发的中断,如断电、电源故障等情况,需要处理器立即处理。

  • 中断优先级
    优先处理高优先级中断

同级中断根据任意的顺序

  • 中断嵌套处理
    在处理低优先级中断时允许高优先级中断发生

保证高优先级中断被及时处理

  • 中断响应
    中断响应是当CPU发现已有中断请求时,暂停现行程序执行,并自动引出中断处理程序的过程。

  • 中断向量

为了方便异常和中断的处理,系统为每个异常和中断都赋予了一个唯一的标识号,称为向量(vector),将其用作中断描述符表IDT(Interrupt Descriptor Table)中的一个索引号,以快速定位一个异常或中断的处理程序的入口地址。

系统允许的向量号范围是0~255,其中0~31保留用作80x86处理器定义的异常和中断, 32~255用于用户定义的中断。这些中断通常用于外部I/O设备,使得这些设备可以通过外部硬件中断机制向处理器发送中断。

image-20210403165503531

image-20210403165532719

任务门

image-20210403170136701

(1)中断门(Interrupt gate)

中断门的类型码为110,其中包含了一个中断或异常处理程序所在段的选择符和段内偏移量,如图上所示。当控制权通过中断门进入中断处理程序时,处理器清除IF标志,即关中断,以避免嵌套中断的发生。中断门中的DPL(Descriptor Privilege Level)为0,因此,用户态的进程不能访问中断门。所有的中断处理程序都由中断门激活,并全部限制在内核态中执行。

(2)陷阱门(Trap gate)

陷阱门的类型码为111,与中断门类似,两者唯一的区别是,控制权通过陷阱门进入处理程序时维持IF标志位不变,即不关中断

(3)系统门(System gate)

这是Linux内核特别设置的,用来让用户态的进程访问陷阱门,因此,其门描述符的DPL为3。通过系统门来激活4个Linux异常处理程序,它们的向量是3、4、5及128,即在用户态下可以执行int3、into、bound 及int0x80四条汇编指令。

  • 传统的中断控制器:8259A

image-20210403170701604

(1)确定与中断或者异常关联的向量i(0~255);

(2)读idtr寄存器指向的IDT表中的第i项;

(3)从gdtr寄存器获得GDT的基地址,并查找GDT,以读取IDT表项中的段选择符所标识的段描述符;

(4)确定中断是由授权的发生源发出的:

(5)检查是否发生了特权级的变化,一般指是否由用户态陷入了内核态,如果是,则控制单元必须开始使用与新的特权级相关的堆栈:读tr寄存器,访问运行进程的tss段,找到与新特权级相关的栈段和栈指针的值;然后用这些值装载CPU的ss和esp寄存器;最后在新的栈中保存ss和esp以前的值,这些值指明了与旧特权级相关的栈的地址。

(6)若发生的是故障,用引起异常的指令地址修改cs和eip寄存器的值,以使得这条指令在异常处理结束后能被再次执行;

(7)在栈中保存eflags、cs和eip的内容;

(8)如果异常产生一个硬件出错码,则将它保存在栈中;

(9)装载cs和eip寄存器,其值分别是IDT表中第i项门描述符的段选择符和偏移量字段。这对寄存器值给出中断或者异常处理程序的第一条指令的地址。之后CPU就将去执行中断或异常处理程序了。

当中断或异常处理程序执行完成后,相应的处理程序会执行一条iret汇编指令,实现从中断或异常的返回。iret指令将完成如下工作:

(1)用保存在栈中的值装载cs、eip和eflags寄存器。如果一个硬件出错码曾被压入栈中,那么弹出这个硬件出错码;

(2)检查处理程序的特权级是否等于cs中最低两位的值(即判断进程在被中断时是否运行在内核态)。若是,iret终止执行;否则,转入(3);

(3)从栈中装载ss和esp寄存器,即返回到与旧特权级相关的栈;

(4)检查ds、es、fs和gs段寄存器的内容,如果其中一个寄存器包含的选择符是一个段描述符,并且特权级比当前特权级高,则清除相应的寄存器,以防止怀有恶意的用户程序利用这些寄存器访问内核空间。

系统调用

系统调用是一种用户在程序一级请求操作系统内核完成某种功能服务的过程调用

​ 系统调用号、系统调用服务例程、系统调用入口表
​ 系统调用号及参数传递
系统调用处理流程

  • 应用程序产生异常,系统进行中断处理,由用户态转换为系统态,保存被中断进程
  • 使用向量号128查找描述符,得到该异常中断处理程序(system_(call))的入口地址
  • 执行system_(call)函数,保存中断进程现场信息,检查系统调用正确性,再用EAX寄存器传的系统调用号查找入口表,进而得到入口地址
  • 执行系统调用服务例程,完成具体服务功能处理
  • 执行完后,恢复被中断的进程的CPU现场信息,返回被中断进程继续执行

系统时钟

  • 实时时钟RTC

  • 时间戳计数器TSC

  • 可编程间隔定时器PIT

3.进程管理

进程的引入

3.1.1 程序的并发执行及特征

单个处理,

提高效率-多个程序并发进行

并发执行特征、提高资源利用率和系统吞吐量

间断性

失去封闭性导致的不可再现型

3.1.2 进程管理功能

进程控制: 控制进程状态转换
进程互斥与同步:协调进程间的运行顺序

互斥方式:进程间竞争临界资源
同步方式:进程间相互合作

进程通信:进程间的信息交换
调度: 作业调度,进程调度

进程的概念

3.2.1 进程定义

进程是具有一定独立功能的程序关于某个数据集的一次运行过程,是系统进行资源分配和调度的一个独立单位

特征:动态性、并发性、独立性、异步性(相对于程序而言)

动态性:程序是静态的,进程是动态的,有生命周期

独立性:进程是可独立运行的单位

异步性:进程可异步进行,而程序必须顺序

并发性:程序与进程并非是一一对应的

进程映像是指进程实体的组成。

进程和程序的区别

从定义上看

进程是程序处理数据的过程,而程序是一组指令的有序集合;
进程具有动态性、并发性、独立性和异步性等,而程序不具有这些特性;

从进程结构特性上看

进程映像:它包含程序、数据集、(栈)和PCB进程控制块;
进程和程序并非一一对应:通过多次执行,一个程序可对应多个进程;通过调用关系,一个进程可执行多个程序。

3.2.2 进程的状态与转化

就绪状态:进程已分配到除处理器外的所有必要资源,获得处理器可立即执行

运行状态:处于就绪状态的获得处理器,其程序正在执行

阻塞状态:正在执行的进程因等待某事件的发生

img

创建状态:正在创建,尚未进入就绪的状态

终止状态:完成工作后正常结束的状态

img

3.2.3 Linux进行状态解析

TASK_RUNNING 可运行状态

TASK_INTERRUPTIBLE 可中断睡眠

TASK_UNINTERRUPTIBLE 不可中断睡眠

TASK_STOPPED 暂停

TASK_TRACED 跟踪

TASK_DEAD 终止

TASK_WAKEKILL 可响应致命信号的不可中断睡眠状态

EXIT_ZOMBIE 僵尸

EXIT_DEAD 进程最终状态

image-20210404103508066

3.2.4 进程控制块PCB

1.进程组成

image-20210404103656060

①程序、数据
描述进程本身所应完成的功能
② 栈
过程调用相关信息:返址、参数传递、局部变量等
③ PCB
记录进程的动态特征,该进程与其他进程和系统资源的关系。

2.进程控制块内容

  • 进程标识信息:
    进程标识符:PID
    用户标识符
    家族关系

  • 进程调度信息:
    进程状态;
    进程优先级;
    进程的时间片;
    等待事件;
    其他调度相关信息

进程控制

原语(primitive):
由若干条指令构成的“原子操作(atomic operation)”过程,完成某种特定的功能,作为一个整体而不可分割--要么全都完成,要么全都不做。

进程控制原语

创建进程原语 撤销进程原语 阻塞进程原语 唤醒进程原语

3.3.1 进程创建
  • 引起进程创建的典型事例

    作业调度

​ 用户登录

​ 提供特定服务

​ 应用请求

image-20210404104436134
3.3.2 进程撤销
image-20210404104520844 image-20210404104602327
3.3.3 进程阻塞与唤醒

image-20210404104632754image-20210404104654196

image-20210404104812130
3.3.4 Linux进程管理

进程描述符task_struct
进程创建do_fork()
执行程序exec()系列函数
进程终止do_exit()
等待子进程结束wait()
进程睡眠wait_event*()
进程唤醒wake_up()

task_struct进程描述符

pid_t pid; 
pid_t tgid;  

Unix系统通过pid来标识进程,linux把不同的pid与系统中每个进程或轻量级线程关联,而unix程序员希望同一组线程具有共同的pid,遵照这个标准linux引入线程组的概念。一个线程组所有线程与领头线程具有相同的pid,存入tgid字段,getpid()返回当前进程的tgid值而不是pid的值。

在CONFIG_BASE_SMALL配置为0的情况下,PID的取值范围是0到32767,即系统中的进程数最大为32768个。

进程内核线

void *stack

对每个进程,Linux内核都把两个不同的数据结构紧凑的存放在一个单独为进程分配的内存区域中

  • 一个是内核态的进程堆栈,
  • 另一个是紧挨着进程描述符的小数据结构thread_info,叫做线程描述符。

Linux把thread_info(线程描述符)和内核态的线程堆栈存放在一起,这块区域通常是8192K(占两个页框),其实地址必须是8192的整数倍。

Linux进程创建

fork()调用执行一次返回两个值,对于父进程,fork函数返回子程序的进程号,而对于子程序,fork函数则返回零,这就是一个函数返回两次的本质。

vfork系统调用不同于fork,用vfork创建的子进程与父进程共享地址空间,也就是说子进程完全运行在父进程的地址空间上,如果这时子进程修改了某个变量,这将影响到父进程。

系统调用fork()和vfork()是无参数的,而clone()则带有参数。fork()是全部复制,vfork()是共享内存,而clone() 是则可以将父进程资源有选择地复制给子进程,而没有复制的数据结构则通过指针的复制让子进程共享,具体要复制哪些资源给子进程,由参数列表中的 clone_flags来决定。另外,clone()返回的是子进程的pid。

do_fork() 函数生成一个新的进程,大致分为三个步骤。定义在kernel/fork.c文件里

建立进程控制结构并赋初值,使其成为进程映像。
为新进程的执行设置跟踪进程执行情况的相关内核数据结构。包括 任务数组、自由时间列表 tarray_freelist 以及 pidhash[] 数组。
启动调度程序,使子进程获得运行的机会。

进程终止

释放进程所占用的资源,向父进程发信号,由exit()系统调用完成

进程睡眠

linux2.7之前是sleep_on()之后改用wait_event()

唤醒进程

调用__wake_up()对指定等待队列中的进程完成唤醒操作

对指定等待队列加锁

调用后唤醒进程大部分工作,遍历指定等待队列,从第一个睡眠进程开始,根据指定参数唤醒

对指定等待队列解锁

进程同步

3.4.1 进程同步基本概念
  • 并发进程间的间接制约关系与进程互斥

临界资源:一次仅允许一个进程使用的资源称为临界资源

由于两个相反进程的同时进行,导致结果出现错误

临界区

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

同步机制规则

多个进程互斥进入临界区

空闲让进、忙则等待、有限等待(保证有限时间内进入临界区,防止永远等待)、让权等待(当进程不能进入临界区,应立即释放CPU,以免CPU陷入忙等状态,提高利用率)

并发进程间的直接制约关系与进程同步

进程并发执行的过程中,除了互斥使用临界资源,相互合作的多个进程还必须在某些特定时刻协调配合实现同步执行

3.4.2 进程同步机制及其应用

1.用硬件方法解决进程互斥问题

禁止中断

禁止中断-》临界区-》开放中断-》剩余区

缺点:禁止中断权限赋予普通用户,会增加系统风险

只能用于单处理器系统

利用专用机器指令解决进程互斥问题

TSL指令(test和set lock)

Swap指令

2.利用软件方法解决进程互斥问题

不正确算法1

turn==0,P0运行;turn=1,P1运行

违背空闲让进、让权等待

不正确算法2

turn==0,P0运行

image-20210426212501581

turn=1,P1运行

image-20210426212534950

不能保证互斥

peterson算法

flag[2]布尔数组初始均为false判断是否希望进入临界区

turn==0,P0运行;turn=1,P1运行;turn判断那个优先进

面包店算法

取号排队

//每个进程设置一个唯一的编号Pi(i=0‥n-1)
 //boolean choosing[n]:表示进程是否正在取号,初值为False
 //int number[n]:记录每个进程取到的号码,初值为0

void  Pi()(i=0‥n-1{
 while (true) {
     choosing[i] = True;   //pi正在取号
    number[i] = 1 + max(Number[1],...,Number[N]);  //Pi取到的号码
     choosing [i] =False;
     for (j=0; j<N; ++j) {
          while (choosing[j] != 0);
         while ((number[j]!=0) ((number[j]<number[i] ) ||  
                           ((number[j] =number[i])&&( j<i) ));
           //当多个进程取到同号时,保证编号小的进程优先进入临界区
        }
      临界区
     number[i] = 0;
     剩余区    }
}

3.利用锁机制解决进程互斥问题

临界资源设置一个lock,0为可用,1为被占用

image-20210426212736691

4.利用信号量机制解决进程互斥与同步问题

P/V操作

P:wait或down

V:signal或up

整型信号量机制

共享整型变量

由于S<=0时一直在测试,违背让权等待,存在忙等

记录型信号量机制-使用最广泛

结构体类型变量:包含一个整型变量和一个指向对应阻塞队列

信号量S的值表示某类资源的数量,S>0代表当前可分配的资源数目

S<0表示绝对值是阻塞队列中等待进程的数目

信号量集机制

一次性分配所有资源,结束后一次性释放

利用信号量机制实现进程互斥–方便解决多个进程互斥使用临界资源

设置初值S信号量为1

wait(S)申请资源 signal(S)释放资源

信号量机制解决同步和互斥问题的步骤

确定进程:
(1)包括进程的数量、进程的工作内容。

(2)确定进程同步互斥关系:
根据进程间是竞争临界资源还是相互合作处理上的前后关系,来确定进程间是互斥还是同步。

(3)确定信号量:
根据进程间的同步互斥关系确定信号量个数、含义、初始值,各进程需要对信号量进行的wait()/signal()操作。

(4)用类程序语言描述算法

设与某资源相关联的信号量初值为3,当前值为1,若M表示该资源的可用个数,N表示等待资源的进程数,则M,N分别是( )
A.0,1
B.1,0
C.1,2
D.2,0
[解析] 信号量的初值表示系统中某类资源的数目,已知为3;
信号量的值小于0时,表示该类资源已分配完毕,此时信号量的绝对值表示在该信号量链表中已阻塞进程的数目。
信号量的值大于0时,表示该类资源还没有完全分配完,此时信号量的值表示该资源的可用个数。
信号量的当前值为1,大于0,则M表示该资源的可用个数为1,N表示等待资源的进程数为0。

在使用信号量机制实现互斥时,互斥信号量的初值一般为(1);而使用信号量机制实现同步时,同步信号量的初值一般为(0)。
3.4.3 经典进程同步问题

生产者-消费者问题

问题:若干进程通过有限的共享缓冲区交换数据。其中,"生产者"进程不断写入,而"消费者"进程不断读出;共享缓冲区共有K个;任何时刻只能有一个进程可对共享缓冲区进行操作。

image-20210426213257487

确定进程:进程数量及工作内容;
确定进程间的关系:
互斥:多个进程间互斥使用同一个缓冲池;
同步:当缓冲池空时,消费者必须阻塞等待;
当缓冲池满时,生产者必须阻塞等待。
设置信号量:
Mutex:用于访问缓冲池时的互斥,初值是1
Full:“满缓冲”数目,初值为0;
Empty:“空缓冲"数目,初值为K。full+empty=K

  #define K 100
  #define MAXLEN 80
   typedef struct{
         int  num;
         char array[MAXLEN];
         }Message ;
  semaphore mutex;
  semaphore empty;
  semaphore full;
  Message buffers[K];
  int in, out;
   void main()
{ 
     mutex={1,NULL};
     empty={K,NULL};
     full={0,NULL};
     Message buffers[K];
     in =0;  out=0;
    parbegin(produceri, consumerj)
}
 program  produceri
      {
            Message p_puf;
            while(1){
             produce a message in p_buf;
                   wait(empty)wait(mutex);
                   buffers[in] = p_buf
                   in =( in +1)%K;
                  signal(mutex)signal(full)}
     }
program  consumerj
 {
       Message c_buf;
       while(1){
      wait(full)wait(mutex);        
      c_buf = buffers[out];
      out =(out+1)%K;
            signal(mutex)signal(empty);
            consume the message in c_buf;  }
 }

哲学家进餐问题

image-20210426213611019
//算法1
semaphore chopstick[04]; 
void main() {
      chopstick[04]={1,NULL};
      parbegin(philosopher(i));
}
program  philosopher(i)
        {  thinking…
            wait(chopstick[i])wait(chopstick[(i+1)mod 5]);
            eating;
            signal(chopstick[i])signal (chopstick[(i+1)mod 5]);
         }
//算法2
semaphore chopstick[04];
  semaphore sm;
void main() {
      chopstick[04]={1,NULL};
   sm={4,NULL};
      parbegin(philosopher(i));
}
program  philosopher(i){
       wait(sm);
       wait (chopstick[i]);
       wait (chopstick[(i+1)mod 5]);
         eating;
       signal(chopstick[i]);
       signal (chopstick[(i+1)mod 5]);  
       signal(sm);
}

读者-写者问题

有多个读者进程和多个写者进程共享一数据。要求多进程对共享数据进行读写操作时,任一时刻“写者”最多只允许一个,而“读者”则允许多个,即:
当有写者在写数据时,其他写者和读者必须等待;
当有读者在读数据时,其他写者必须等待;但其他读者可以同时读数据。

信号量wmutex表示“允许写”,写者与其他进程互斥使用数据
公共整形变量readcount表示“正在读”的读者数
信号量rmutex:实现多个读者对readcount的互斥操作

wmutex:semaphore=1 //读者与写者之间、写者与写者之间互斥使用共享数据

readcount: int = 0; //当前正在读的读者数量

rmutex:semaphore = 1 //多个读者互斥使用readcount

semaphore wmutex,rmutex;
int readcount;
void main()
{
    wmutex=1;
    rmutex=1;
    readcount=0;
    parbegin(writeri,readerj);
}
void writeri {
    while(1){
	wait(wmutex);
	writing…
	signal(wmutex);
     }
}
void readerj {
      while(1) {
              wait(rmutex)
              if readcount==0 then  wait (wmutex);
              readcount++;
              signal (rmutex);      
              reading…
              wait (rmutex);
              readcount--;
             if readcount==0 then signal(wmutex);
             signal(rmutex);
      }
}


写者优先的读者-写者问题算法

 semaphore wmutex=1    //读者与写者之间、写者与写者之间互斥使用共享数据
Semaphore S=1        //当至少有一个写者准备访问共享数据时,它可使后续的读者 等待写完成
Semaphore S2=1         //阻塞第二个以后的等待读者
 int readcount,writecount= 0,0;  //当前读者数量、写者数量
Semaphore mutex1 =  1      //多个读者互斥使用readcount
Semaphore mutex2 = 1        //多个写者互斥使用writecount
Reader() {
      while(1){
           wait(S2);     wait(S);      wait(mutex1)
           if readcount=0 then  wait(wmutex);
            readcount++;
           signal (mutex1);      signal(S);       signal(S2);
           reading…
           wait(mutex1);
           readcount--;
           if readcount=0 then signal(wmutex);
           signal(mutex1);
        }
}
void writer() {
    while(1)
            wait(mutex2);
            if writecount=0 then wait(S);
            writecount++;
            signal (mutex2);
            wait(wmutex);
            writing…
            signal(wmutex);
            wait(mutex2);
            writecount--;
            if writecount=0 then signal(S);            
            signal (mutex2);
       }
}

理发师问题

理发店里有一位理发师、一把理发椅和n把供等候理发的顾客坐的椅子(等候椅)。如果没有顾客,理发师便在理发椅上睡觉;第一个顾客到来时,他必须叫醒理发师;若理发师正在理发时又有顾客到达,则如果有空等候椅,顾客就坐下来等待,如果满座了就离开理发店。理发店问题经常被用来模拟各种排队情形。

int waiting;
     semaphore cust_ready,finished,mutex,chair;

     void main() {
           waiting=0;
           cust_ready=finished=0;
           mutex= chair=1;
           parbegin(barber,customer-i);
}
    

void  barber() {
  do {
     wait(cust_ready);
     cut_hair;
     signal(finished);
  }while(1);
}
void  customer-i ()  {
    wait(mutex)if( waiting < n) {
        waiting=waiting+1;
        signal(mutex); }     
    else {
        signal(mutex);
        离开理发店;
        return; }
     wait(chair);
     sit_in_chair;
     wait(mutex);
     waiting=waiting-1;
     signal(mutex);     
     signal(cust_ready);
     get-haircut;
     wait(finished);
     stand_from_chair;
     signal(chair);
  }

3.4.4 管程机制

管程定义

一种需要编译器支持的进程同步机制

一个管程定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据

image-20210413214817998

信息隐蔽性

任一时刻,管程中只能有一个活跃进程

局部于管程的数据结构只能被管程内的过程访问,任何外部过程不能访问,管程的过程也只能访问该管程内部的数据结构

一个进程若想访问管程内部数据结构需要调用管程内的某个过程实现间接访问

条件变量

每个独立的条件变量是和进程需要等待的某种原因(或说条件)相联系的,当定义一个条件变量时,系统就建立一个相应的等待队列。

C.wait: 阻塞调用进程,并使管程可用;
C.signal: 唤醒相应条件变量上的等待进程。

type PC=monitor
	in, out, count: int;
    buffer[N]: item;
	notfull,notempty:condition;	
procedure  put(item)                             
{                                                               
    if (count==N) then                                  
        notfull.wait();                                     
    buffer(in)=item;                                    
    in=(in+1)mod N;                                  
    count=count+1;                                    
    notempty.signal();                                 
}
     Begin  in=out=count=0; end
procedure get(item)
 {
   if (count=0) then 
   notempty.wait();
   nextc=buffer(out);
   out=(out+1)mod N;
   count=count-1;
   notfull.signal();
   }
procedure  producer                           procedure consumer
{                                                            {
    while(true)                                           while(true)
      {                                                         {
         producer an item ;                            PC.get(item); 
         PC.put(item);                                    consume the item;
      }                                                          }
}                                                      
3.4.5 Linux同步机制解析

内核程序使用的同步机制

1.原子操作

执行过程中不会被别的代码路径所中断的操作,不可分割,不会被中断

原子整数操作 主要用于实现计数器

原子位操作 针对位这一级数据的一组原子操作接口

2.自旋锁

任意时刻只能被一个进程持有,实现多个进程互斥

3.读写自旋锁

在自旋锁的基础上完成

4.内核信号量

允许进程睡眠的内核同步机制,适用于锁会被长时间持有的情况

5.读写信号量

定义在文件include/asm-ia64/rwsem.h中,等待时是挂起而不是自旋

用户程序使用的同步机制

6.IPC信号量(System V信号量 )
可用于任何进程间的同步,使用时需包含三个头文件:
sys/ipc.h、sys/types.h、sys/sem.h。
是信号量集机制,一次可对一组信号量进行操作

7.Posix信号量

无名信号量:线程间或相关进程间的同步

有名信号量:线程或任意进程间的同步

必须包含头文件#include<semaphore.h>

进程调度

3.5.1调度基本概念

计算机系统使用有限的资源,按照一定的原则进行多个作业或进程占用资源参与运行

高级调度–长程调度—根据资源情况把外存上的后备队列选择性调入内存

中级调度–提高内存利用率和系统吞吐量,负责内存与外存之间的换入换出

低级调度–短程调度—决定就绪队列里那个进入处理器

低级调度访问次数最多

调度的功能 CPU调度+CPU切换

调度的方式 非抢占方式+抢占方式

非抢占方式:一旦进程占用CPU就一直运行,直到终止或等待。
抢占方式:系统强行剥夺已分配给现运行进程的CPU,而重新分 配给其他进程运行。

抢占原则:时间片原则,优先权原则,还需要的运行时间

调度时机

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

考虑因素

系统设计目标

调度公平性

资源的均衡利用

合理的系统开销

评价指标

CPU利用率

系统吞吐量 单位时间完成的任务数量

周转时间

image-20210503091542381

带权周转时间

作业的周转时间T与系统为它提供服务的时间Ts之比,即W=T/Ts,称为带权周转时间。

image-20210503091614404

响应时间 交互式系统

对截止时间的保证 实时系统

3.5.2进程调度算法

先来先服务FCFS(非抢占) 长作业

正常排队

利于长作业,不利于短作业,系统平均周转时间会较长

短作业优先调度SJF(可抢占可非抢占) 短作业

时间越短,优先级越高

有效降低作业的平均周转时间和平均带权周转时间,较高系统吞吐量

缺点,实现有困难,难以估计未运行过的程序时间,不公平调度算法,没有考虑任务紧迫度,对长作业不利(饥饿状态)

高响应比优先调度算法(可抢占)

响应比=1+等待时间/要求服务时间

对作业调度和进程调度都适用,照顾了短作业,也考虑了长作业的等待时间,避免了饥饿问题

不足 每次需要重新计算所有作业的响应比,增加系统开销,不能保证紧迫任务及时处理

优先级调度 有非抢占和抢占的

静态优先级

进程创建时就确定优先级,整个生命周期内不变

动态优先级

进程创建时先设置初值,运行期间动态调整权值

时间片轮转调度 交互性系统

image-20210503094223639

将固定时间片改为时间片大小的确定:
N为就绪队列中进程数,T为系统响应时间, q为时间片
T=Nq

改进:

将固定时间改为可变时间片:
每当一轮开始时,系统根据响应时间及当前就绪进程数目重新 计算时间片:
q=T/N
将单就绪队列改为多就绪队列:
如按优先级组建多个就绪队列。

image-20210626161132879

多级队列调度

系统中设置多个就绪队列,每个队列优先级不同;
每个队列有自己独立的进程调度算法;
一个进程依据其属性固定位于某个就绪队列中。

image-20210503094501279

多级反馈队列调度:综合性

有5个批处理作业A、B、C、D、E几乎同时到达,其预计运行时间分别为10、6、2、4、8,其优先级(由外部设定)分别为3、5、2、1、4,这里5为最高优先级。以下各种调度算法中,平均周转时间为14的是( D)调度算法。

A.时间片轮转
B.优先级调度
C.先来先服务(按照顺序10、6、2、4、8)
D.短作业优先
Linux中能支持两台计算机之间的通信机制是()
进程调度算法采用固定时间片轮转调度算法时,如果时间片过大,就会使时间片轮转算法转化为(先来先)调度算法
优先级和短进程优先都分为可抢占和非抢占,只有时间片轮转是时间片一到肯定抢占的

Linux调度算法解析

CFS调度(完全公平调度)

​ 期望每个进程都能同时在CPU上并行执行,且各进程的执行速度相同,各占CPU的1/n的时间。

虚拟运行时间:vruntime
将进程nice值转换为权重weight

下列选项中,会导致用户进程从用户态切换到内核态的操作有()

整数除0,read系统调用

进程通信

3.6.1 进程通信类型

共享存储器系统通信

通过共享存储分区实现进程之间的信息交换。

通信过程:
(1)申请共享存储分区;
(2)将共享存储分区映射到
本进程地址空间中;
(3)进行数据读写;
(4)解除共享存储分区映射;
(5)删除共享存储分区:

image-20210503100337959

特点:

没有中间环节,共享内存直接映射到不同进程的虚拟地址空间中,可快速访问

没有提供进程同步机制

消息传递系统通信

进程间的数据交换是以消息(或报文)为单位。程序员直接利用系统提供的一组通讯命令(原语)来实现通信。
在消息通信中,接收方和发送方之间有明确的协议和消息格式 。

直接通信

send(),receive()

image-20210503100946908

间接通信

​ 通信双方利用一个共享的称为信箱的中间实体实现信息交换。

image-20210503101048281

管道(pipe)通信

管道:用于连接一个发送进程和一个接收进程,以实现它们之间通信的共享文件(pipe文件,又称为FIFO文件)

每次写入的信息长度是可变的

容量不是由系统文件大小决定的

FIFO文件的写入和读出 :严格遵循先进先出 ,不支持文件定位操作。

无名管道:存在于高速缓存中的临时文件,关闭管道后,不存在,用于父子进程之间

有名管道:可用于系统中任意进程间的通信 FIFO

注意:

  • 管道的读写必须互斥进行
  • 管道的读写必须同步进行
  • 通信双方必须同时存在

客户-服务器系统通信

客户端提出请求,服务器提供服务

Socket套接字

image-20210503102129407

image-20210503102145063

远程过程调用RPC

RPC允许客户机上的进程通过网络调用位于远程主机上的过程。

image-20210503102240840

3.6.2 消息缓冲队列通信机制

基本思想

image-20210503102303017

相关数据结构

//消息缓冲区
struct mesg_buffer {
        int  sender;
        char *text;
        int   size;
        time send_time;
       struct mesg_buffer *next;
                     ……
}buffer;    
//消息缓冲队列:
  //空白消息缓冲区队列;
  //接收进程的消息队列;
 PCB中与通信有关的数据项:
  struct message_buf  *mq:  消息队列队首指针;
 semaphore mutex:消息队列互斥信号量;
 semaphore sm:消息队列同步信号量   

发送原语:send()

申请空白消息缓冲区;
填写该空白消息缓冲区;
将该消息缓冲区挂到接收进程的消息队列上。

procedure send(receiver,a)
	{    
	getbuf(a.size,i);
	copy(i,a);
    receiver_pcb=getpcb(receiver);
    wait(receiver_pcb.mutex);
    insert(receiver_pcb.mq, i);
    signal(receiver_pcb.mutex);
    signal(receiver_pcb.sm)
 }


接受原语:receive()

从接收队列中取下一个消息缓冲区;
将该消息缓冲区内容复制到接收区中;
将该消息缓冲区清空后挂到空白消息缓冲队列上。

        procedure receive(b)
         {    
	    wait(receiver_pcb.sm)
	    wait(receiver_pcb.mutex);
	    remove(receiver_pcb.mq,j)
	    signal(receiver_pcb.mutex);
	    copy(b, j);
	    releasebuf(j);   
            }
wait(freebuf_mutex);
      put a freebuf  
      signal(freebuf_mutex);
signal(freebuf_sm);


3.6.3 Linux进程通信系统调用

无名管道

基本概念

无名管道只能用于具有亲缘关系的进程之间的通信
无名管道是半双工的
无名管道没有对应的磁盘映像,只存在于内存中

创建无名管道

int pipe(int filedes[2])

功能:在内存缓冲区中创建一个管道,主要是建立相关VFS对象,并将读写该管道的一对文件描述符保存在filedes[2]中。

有名管道

可用于任意进程间的信息交换

创建有名管道:

int mkfifo(const char * pathname, mode_t mode)
功能:在磁盘上创建一个有名管道文件

IPC消息队列数据结构

共享内存通信

1、进程调度算法中,可以设计成可抢占式的算法有( B C   ) CD
A.先来先服务调度算法          B. 最高响应比优先调度算法
C.最短作业优先调度算法        D. 时间片轮转调度算法
2、若每个作业只能建立一个进程,为了照顾短作业用户,应采用( B   );为了照顾紧急性作业,应采用(  E   );为了实现人机交互,应采用(   C );为了使短作业、长作业和交互作业用户都满意,应采用(D    )。
A.FCFS调度算法;    B. 短作业优先调度算法;    C. 时间片轮转算法
D.多级反馈队列调度算法;    E. 基于优先级的剥夺调度算法
3、若某单处理机多进程系统中有多个就绪进程,则下列关于处理机调度的叙述中,正确的有哪些?(可多选 A B D  )
A.   在进程结束时能进行处理机调度
B.   创建新进程后能进行处理机调度
C.   在进程处于临界区时不能进行处理机调度
D.   在系统调用完成并返回用户态时能进行处理机调度
4、有3个作业J1,J2,J3,其运行时间分别是2,6,4小时,假设它们同时到达系统,并在同一处理器上以单道方式运行,则平均周转时间最小的执行序列是___短作业优先____。
5、下列选项中,提升进程优先级的合理时机有( C  F  G)。BCFG
A.进程时间片用完         B. 进程刚完成I/O操作,进入就绪队列
C. 进程长期处于就绪队列中     D. 进程从就绪状态转为运行状态
E. 刚创建完成的新进程     F. 刚被V操作唤醒的进程
G. 当进行进程调度时,提升就绪队列中进程的优先级


进程死锁

死锁基本概念

死锁的定义:
一组进程中,每个进程都无限等待被该组进程中另一进程所占有的且永远无法释放的资源,这种现象称为进程死锁,这一组进程就称为死锁进程。

产生原因

竞争资源

image-20210503103842010

请求和释放资源的时机不当

产生死锁的必要条件

互斥条件

请求和保持条件

不剥夺条件

环路等待条件

四个条件只要一个不满足就不会形成死锁

image-20210426215223414

预防死锁

破坏占有且等待条件

静态分配资源,一旦分配后,资源被独占

要求进程在不占有资源时才能申请资源

缺点

  • 资源利用率低

  • 进程运行可能被推迟,产生饥饿

    要求进程在没有资源时才可申请资源

破坏不可剥夺条件

进程申请资源r时,有则分配,无则释放所占有全部资源进入阻塞

进程申请资源r时,有则分配,无则进一步判断是否能从其他进程那里进行剥夺,若不能剥夺,则释放所占有全部资源进入阻塞状态

破坏循环等待条件

采用按序分配资源策略

将所有的系统资源按类型进行线性排队,并赋予不同的序号;
所有进程对资源的请求应严格按资源序号递增顺序提出。

优点:较之前面两种方法提高了资源利用率和系统吞吐量。
缺点:(1) 降低了添加新设备的灵活性;
(2) 作业使用资源的顺序与系统规定的申请顺序不同时,会降低资
源的利用率

避免死锁

安全状态

设系统中有n个进程,若存在一个进程序列<P1,P2,…,Pn>。使得进程Pi (i=1,2,…,n)以后还需要的资源可以通过系统现有空闲资源加上所有Pj(j<i)已占有的资源来满足,则称此时系统处于安全状态,进程序列<P1,P2,…,Pn>称为安全序列,因为各进程至少可以按照安全序列中的顺序依次执行完成。
如果系统无法找到这样一个安全序列,则称系统处于不安全状态。 当p2多申请了两个,可用变为2个,系统便进入不安全状态

进程最大需求已分配数量还需要的数量剩余可用数量
P****012664
P****1523
P****21037

P1 P0 P2,安全序列

银行家算法

银行家算法中的数据结构

① 可利用资源向量Available[m]。
其中的每一个元素代表一类可利用的资源数目
Available[j]=K,则表示系统中现有Rj类资源K个。
② 最大需求矩阵Max[1…n,1…m]
该矩阵定义了系统中n个进程对m类资源的最大需求。
Max[i,j]=K,则表示进程i需要Rj类资源的最大数目为K。

③ 分配矩阵Allocation[1…n,1…m]
该矩阵表示系统中每个进程当前已分配到的每类资源数量。
Allocation[i,j]=K,表示进程i当前已分得Rj类资源的数目为K。
④ 需求矩阵Need[1…n,1…m]
该矩阵表示每个进程尚需的各类资源数。Need[i,j]=K,则表示进程i还需要Rj类资源K个,方能完成其任务。

Need[i,j]=Max[i,j]-Allocation[i,j]

⑤ 请求向量Requesti[m];
某进程提出的资源请求向量。

安全性算法描述

① 设置两个临时向量:
工作向量Work: 表示系统可提供给进程继续运行所需的各类资源数目,它含有m个元素,在执行安全算法开始时,Work=Available;
Finish[n]: 它表示系统是否有足够的资源分配给进程,使之运行完成。开始时先做Finish[i]=false; 当有足够资源分配给进程时, 再令Finish[i]=true。

②从进程集合中找到一个能满足下述条件的进程:
1) Finish[i]=false; 2)Need[i,j]≤Work[j];
若找到, 执行步骤③ , 否则,执行步骤④ ;
③ 当进程Pi获得资源后,可顺利执行,直至完成,并释放 出分配给它的资源,故应执行:
Work[j]=
Finish[i]=
go to step ②;
④ 如果所有进程的Finish[i]=true都满足, 则表示系统处于安全状态;否则,系统处于不安全状态

死锁的检测与解除

资源分配图

死锁定理–用于处理死锁的检测方法

死锁检测算法

数据结构:
可利用资源向量Available[m];
资源分配矩阵Allocation[n,m];
资源请求矩阵Request[n,m];
工作向量Work=Available

finish[n]向量:
若Allocation[i]=0,则finish[i]=True ;
否则finish[i]=False

算法描述:
work=available; Finish[i]=False或True;(i=0,1,2, …n-1)
① 寻找同时满足下述条件的进程:
Finish[i]=False; Requesti≤Work
若找到,则转②;否则转③;
② 执行:Work=Work + Allocationi,Finish[i]=True;
再转回第②步
③ 判断:若对所有i=(0,1,2, …n-1),Finish[i]=True,则无死锁;否则,若Finish[i]=False,则进程Pi死锁

死锁检测时机

考虑因素

死锁出现的频繁程度;
发生死锁时受到死锁影响的进程数量
进行死锁检测的系统开销

时机:

每当有进程请求资源且得不得满足时就做检测
周期性定时检测
依据CPU利用率确定是否进行检测

死锁的解除

撤销进程

撤销所有死锁进程
一次只撤销一个进程直到解除死锁为止

  • 撤销的进程数最少
    撤销代价最小
    进程优先数;
    进程类的外部代价;
    进程运行代价

抢占资源

选择被抢占资源的进程
“回滚”问题
避免“饥饿” 现象

1.某系统中有5个并发进程,每个进程都需要4个同类资源才能运行完成并释放出所占用的资源,则系统不会发生死锁的最少资源数是_______个。
       答案:16
2.   某系统中有11台打印机,N个进程共享这些打印机资源,每个进程要求3台,当N的值不超过______时,系统不会发生死锁。
              答案:5
假定某计算机系统中有R1设备3台,R2设备4台,它们被P1,P2,P3和P4这4个进程共享,且已知这4个进程均以下面所示的顺序使用设备:
→申请R1→申请R2→申请R1→释放R1→释放R2→释放R1→
(1)系统运行过程中是否有产生死锁的可能性?为什么?
(2)如果有可能产生死锁,请列举一种情况,并画出表示该死锁状态的进程资源分配图。

参考答案:
可能死锁:当P1,P2,P3三个进程都申请到R1,R2后,再申请R1时将死锁。

线程机制

3.8.1线程基本概念

线程的引入
“基于同数据区的同时多请求”问题的解决思路:
例:数据库服务器需要同时处理来自多个客户端的数据查询请求,这些请求都是针对同一数据库的数据的。
解决思路:
(1)设置一个进程顺序处理所有请求;
(2)设置多个进程分别处理多个请求;

存在问题:

  • 进程同步复杂
  • 系统开销大(进程是系统拥有资源的独立单位,是独立调度和分派的基本单位)

什么是线程

线程是隶属于进程的一个实体,是比进程更小的一个运行单位。

image-20210503114603926

线程进程的比较

调度: 线程是CPU的调度单位
并发性:进程间可并发执行;线程间也可并发执行
拥有资源:进程是资源分配和拥有单位;线程基本不拥有资源,同一进程中所有线程共享进程所拥有的资源
系统开销:线程开销<进程开销
创建及删除线程的开销<进程
线程切换开销<进程

线程管理

线程状态

就绪态 运行态 阻塞态 终止态

线程控制

线程创建:
分配并设置TCB;建立堆栈;插入就绪队列
线程终止:
回收所有资源
线程阻塞:
释放CPU,将状态置为“阻塞”,插入阻塞队列,转调度程序
线程唤醒:
移出阻塞队列,将状态置为“就绪”,插入就绪队列

3.8.2线程实现机制

用户级线程ULT

完全由用户空间程序实现的线程机制

image-20210503115332280

运行时系统(线程库):
提供多线程应用程序的开发环境和运行环境。
由一组管理和控制线程的函数集合组成。它们作为进程代码的一部分,驻留在进程的用户空间。

运行时系统的功能

线程控制;
线程调度;
线程同步;
线程通信;

用户级线程的优点:
线程的调度及切换开销小;
线程调度由应用程序完成,可以选择最适当的算法;
可运行在任何操作系统上。

image-20210503115553897

内核级线程KLT

内核级线程优点:
(1)对多处理器,核心可以同时调度同一进程的多个线程;
(2)阻塞是在线程一级完成;
(3)核心例程是多线程的;

内核级线程缺点:
同一进程内的线程切换调用内核,系统开销大。

image-20210503115703889

组合方式

多对一模型

多个用户级线程映射到一个内核级

优点:线程管理开销小,效率高

缺点:线程系统调用时,将导致所属进程阻塞

​ 核心只将处理器分配给进程,同一个进程中的两个线程不能同时运行与两个处理器

image-20210503115801054

一对一模型

把一个用户级线程映射到一个内核级线程上。

image-20210503120040976

优点:线程系统调用时,仅阻塞线程

​ 能获得多处理器的好处

缺点:线程管理开销大

多对多模型

多个用户级对较少或者同数量内核级

image-20210503120242056

优点:
线程系统调用时,仅阻塞线程
能获得多处理器的好处
线程管理开销 不至于增加太大

3.8.3 Linux线程机制

创建线程clone()

#include <sched.h>
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);

参数说明

(1)fn:新线(进)程即将要执行的函数。
(2)child_stack:为新线程分配的内核堆栈空间的起始地址。
(3)arg:传给新线程要执行的函数fn的参数。
(4)flags:创建标志,描述新线(进)程将从父进程继承哪些资源

Linux下Pthread线程库介绍

4.存储器管理

4.1 存储器管理概述

4.1.1 多级存储器体系

寄存器 Cache 主存 辅存 可移动存储器 网络存储器

高层存储器容量小,其存放的数据只是低层存储器数据的子集

Cache访存时间

有效访问时间=命中率×访Cache时间+(1-命中率)×(访Cache时间+访主存时间)

4.1.2 存储器管理功能

1.内存分配与回收

分配:进程创建和运行过程中,为进程分配所需要的存储空间

回收:在进程结束或不需要的时候及时回收空间

分为静态和动态分配和回收

2.地址映射

编译器对程序编译时,会从0开始为程序代码编址,程序中所有的地址都是相对0确定的虚地址、或者相对地址或逻辑地址–》虚地址空间 可一维空间可二维

程序加载到内存中,在物理内存中的都是实地址或物理地址–》实地址空间 总是一维线性空间

地址映射:虚地址不能直接用来访存,于是需要将虚地址转为实地址,这个地址转换的功能就是地址映射

存储器管理方式不同,地址映射方式不同

连续分配方式中,起始物理地址存在重定位寄存器,只需要将虚地址与重定位的地址求和就是物理地址

3.内存的共享和保护

共享:多个进程共享访问一段内存空间的数据–》节省公共代码空间,提高内存利用率

保护:避免进程之间相互干扰

界地址寄存器:设置上下界两个地址寄存器,每一次执行指令,物理地址是否在下界和上界之间

判别式:下界寄存器 ≤ 物理地址 ≤ 上界寄存器

限长寄存器:直接用虚地址与寄存器中的值进行比较,可判断是否越界

判别式: 0≤逻辑地址<限长寄存器

4.内存扩充

外存补充内存

时间局限性 循环

空间局限性 顺序

4.1.3 程序的装入和链接

编译–装入–链接

链接 形成逻辑地址
(根据时间不同划分)

动态链接与程序的逻辑结构相关,分段存储管理将程序按照逻辑段划分,有利于其动态链接。

静态链接 程序装入内存前就将目标模块和库模块链接,不能拆开,不能共享模块,无法单独模块升级

装入时动态链接 边装入边链接,可模块单独升级,不能模块共享
运行时动态链接 装入的是目标模块,执行时才会相应链接,既方便模块独立更新,模块共享;按需链接,节省空间

装入
绝对装入(单道程序) 装入后地址确定

静态重定位装入
不允许程序运行时在内存中移动位置

动态运行时的装入方式(重定位寄存器)
经常换入换出

为了保证一个程序在主存中改变了存放位置后仍能正确执行,则对主存空间应采用(  B  )技术。
A.静态重定位                        B.动态重定位
C.动态分配                            D. 静态分配
进行地址映射时,( A  )方式可不需要硬件的支持,完全由软件来实现。
A.静态重定位                        B.动态重定位
C.动态分配                            D. 静态分配
7. 使每道程序能在内存中“各得其所”是通过(      )功能实现的;保证每道程序在不受干扰的环境下运行,是通过(   )功能实现的;能让较大的程序在较小的内存空间运行,是通过(      )功能实现的。

4.2 连续存储器管理方式

(分区是在装入时建立的)
单一连续分配:将一维虚地址空间映射到一维线性连续物理内存空间
分为系统区、用户区,单一程序用户空间独占

4.2.1 固定分区方式

可实现静态重定位

分区大小相同和分区大小不同

4.2.2 可变分区方式(动态)

基于顺序搜索的动态分区分配算法(注意:可能每次分区需要排序,问第几个的时候特别注意)
首次适应(FF)
① 优先利用低地址空闲区,保留高地址大空闲区;

② 低地址不断划分,留下碎片;

③ 每次都从低地址开始查找,增加开销。

循环首次适应(NF)
从上一次找到的空闲分区的下个分区开始查找第一个合适的空闲分区。

使分区更均匀,减少了开销,但缺少大分区。

最佳适应(BF) (最易产生内存碎片)
需要排序 空闲分区由小到大

容易产生碎片。

最坏适应(WF)
需要排序 空间分区由大到小

产生碎片的可能性最小,查找效率高。

基于索引搜索的动态分区分配算法
快速适应(quick fit)
对于每一类具有相同容量的所有空闲分区,单独设立一个空闲分区链表。

在内存中设立一张管理索引表,其中的每一个索引表项对应了一种空闲分区类型。

以进程为单位分配空闲分区。

查找效率高,系统开销大。

伙伴系统(buddy system)
分配长度为n的存储空间,计算i,使2i-1<n≤2i,在2i空闲分区链表中找,找不到则在2(i+1)中分出一个,分成两个相等的分区,一个分配,一个加入链表。

特点:(与前面的动态分区相比)
空间利用率有所降低;时间上性能更佳。

哈希算法
构造一张以空闲分区大小为关键字的哈希表

分区回收思路
① 检查释放分区 (即为回收分区)在主存中的邻接情况
若上、下邻接空闲区,则合并,成为一个连续的空闲区
② 若回收分区不与任何空闲区相邻接
建立一个新的空闲区,并加入到空闲区队列中。
③ 修改相关数据结构

分区回收算法

内存回收分为上邻接,下邻接,上下邻接,无邻接

紧凑

动态重定位(增设重定位寄存器,存放程序在内存中的起始地址) 动态运行时装入的方式中,作业装入内存后的所有地址是逻辑地址。执行时再 相对地址+重定位寄存器中存放的程序在内存中的起始地址

动态重定位分区分配算法与动态分区分配算法区别:增加了紧凑

1. 外部碎片是指()。D
存储分配完后所剩的空闲区
没有被使用的存储区
不能被使用的存储区
未被使用,而又不容易满足用户大小要求的存储区

2. 当内存外部碎片总容量大于某一作业所申请的内存容量时,()C
可以为该作业直接分配内存
不可以为该作业分配内存
拼接后,可以为该作业分配内存
一定能为该作业分配内存
3. 填空:重定位方式有两种,_静态重定位____把作业的指令和数据地址在作业装入时全部转换成绝对地址;_动态重定位____则在每条指令执行时才做地址转换工作。
4. 动态分区管理要求对每一个作业都分配( A  )的内存单元。
A地址连续                      B. 若干地址不连续
C若干连续的块                    D. 若干不连续的块
5. 首次适应算法的空闲分区是( C   );最佳适应算法的空闲区是
(  B   ),最坏适应算法的空闲区是(A    )。
A按大小递减顺序链接            B. 按大小递增顺序链接
C.   按起始地址递增顺序链接        D.按起始地址递减顺序链接 

4.3 分页存储管理方式

物理块=页框或帧

地址变换:页号 -> 物理块号

逻辑地址结构:页号P,页内地址d(页内偏移量)

页面:将进程逻辑空间划分为若干等长的区域,称为页(或页面);并对每个页面顺序编号,称为页号。

页面大小L决定页内地址位数,页号位数决定了逻辑地址中页面的总数。

P=INT(A/L);d=A modL;

页表

地址映射时,要知道对于页面的物理块,系统为每个进程设置了一张页号到物理块号的映射表-》页表

页表的每个表项PTE(Page Table Entry)由页号P和对应物理块号F组成

页表大小=进程页数×PTE大小

存储页表的页面数=PTE总数/每页PTE数

进程页表里有页号对应物理块号

物理地址A’=F×L+d

地址越界保护

具有快表 (TLB)

计算访存时间
EAT=а×λ+(t+λ)(1—а)+t=2t+λ—t×а

λ表示查找快表所需要的时间,а表示命中率,t表示访问一次内存所需要的时间

注意一次访存时间是必需的,其他的查找过程另算。

两级、多级页表
多级页表不会加快地址的变换速度。但能减少页表所占的连续内存空间。

页面大小2的k次方决定页内地址的d的位数k,若页面存放的PTE为2的m次方,32位操作系统

n级=INT(32-k/m)

反置页表
每个物理块设置一个页表项,按物理块编号排序。

1. 在一个分页存储管理系统中,逻辑地址的结构长度为为18位,其中11 ~17位表示页号,0 ~10表示页内偏移量。若有一个作业共3个页面,各页依次装入2、3、7号物理块中,试问:
(1)进程逻辑地址空间最大可以为多少KB?分为多少页?每页有多大?
(2)逻辑地址1500对应的物理地址是多少?逻辑地址10000对应的物理地址是多少?
2.  设有8页的逻辑空间,每页有1024字节,它们被映射到有32块的物理存储区中,那么逻辑地址的有效位是()位,物理地址至少是()位。
(1) 2^18 = 256KB,   2^7 = 128页,每页 2^11 = 2KB   (2) 1500 mod 2048 第0页 1500偏移,对应 第2物理块,偏移1500,故为2048*2+1500=5596; 10000 mod 2048 = 4 余 1808, 第4页超出,故越界中断
3+10=13位;5+10=15位
3.如果一个程序占用200M大小空间,若页面大小为4KB,每个页表项4B,它的页表需要多大的空间存储?
50k个页面,4*50k页表大小
4.页表也存储在内存中,那么如果页表比一个页面还要大会怎么样?有哪些办法解决这个问题?
对页表本身采用离散分配方式存储;
只将当前需要的部分页表调入内存,其余的页表仍驻留在磁盘上,需要时再调入。
两级和多级页表
5.为满足264地址空间的运行,采用多级页表管理方式,假设页面大小为4KB,页表中的每个页表项需要占8B,且最高级页表只能占用一个块存储,则该分页系统至少应采用几级页表?
页面大小:4KB=2^12B,所以一个块中可以存放2^9个页表项,即逻辑地址结构中每个中间层页号需要占用9个二进制位,所以需要的页表级数为:
     (64-12)/9=6(向上取整)

image-20210618140200416

4.4 分段存储管理方式

将进程的虚拟地址空间按逻辑信息划分为若干段

段号位数决定了每个进程最多可以分几个段,段内地址位数决定了每个段的最大长度。

逻辑地址结构

低位0-15位段内地址d,16-32位段号S

分段逻辑地址空间是二维的非线性地址空间

段表:段的映射表,记录各个段在内存中的起始地址和段长;每个分段占一个表项,每个表项有段号,段长,段基址构成,按段号排序

(1)每个段在段表中占一个表项
(2)段号、段基址、段长、段状态等
(3)段表连续存放在OS内核所在区域
(4)段表始址及段表长度保存在进程PCB中

优点/要求:

方便编程
信息共享
信息保护
动态增长
动态链接
2次访存

image-20210523170512376

假如一个作业的段表如下所示,其中存取控制字段中W表示可写,R表示可读,E表示可执行。分别说明下面指令执行时,地址转换的情况。
(1)STORE  R1,[0,70] ;    (2)STORE  R1,[1,20] 
(3)LOAD   R1,[3,100];    (4)JMP     [2,100]
1.STORE =500+70=570
2.STORE 存取控制 不符合,硬件将产生保护性中断信号。?
3. 段内地址超过了段长,将产生越界中断信号。
4.3000+100;
分页和分段的区别:
  1. 页是信息的物理单位,段是信息的逻辑单位
  2. 页的大小固定且由系统决定,段的大小动态变化
  3. 分页的用户程序地址空间是一维的,分段是二维
  4. 分页对用户不可见,分段可见
  5. 分段比分页更方便信息的共享和保护
  6. 分段与分页更适合动态链接的实现
信息共享:可重入

不允许可重入代码在执行中有任何改变。

全局变量不可重入

局部变量可重入

4.5 段页式存储管理方式-碎片最少

3次访存

碎片
内碎片:分页、固定分区、段页式存储管理

外碎片:分段式存储管理

基本思想
结合分页和分段技术,先分段后分页
分页:有效提高内存利用率
分段:很好的满足用户需求
原理
对内存进行分页(物理块/页框);
对用户作业先分段,各段再分页。
地址结构与地址变换
段号、段内页号、页内地址三部分
段表和页表

1.作业分段

2.段内分页

3.逻辑地址结构

image-20210523171331728

image-20210523171425944

question
q:逻辑地址到物理地址的转换是在编译时候进行的还是在装入的时候进行的?

a:装入时进行。

q:重定位是指在装入的时候进行的吗?

q:重定位就是指逻辑到物理地址的转换吗?

a:可重定位是一种装入方式,是。

q:动态分区与固定分区分配方式相比,是否解决了碎片问题

4.6 虚拟存储系统

传统存储管理
一次性
驻留性
局部性原理
时间局限性
空间局限性

4.6.1 虚拟存储器基本概念

定义
虚拟存储器,是指具有请求调入和置换功能,能从逻辑上对内存容量加以扩充的一种存储器系统。

特征
多次性(最重要)允许作业
置换性 多次、对换建立在离散分配基础上
虚拟性 以前两者为基础
虚拟内存最大容量由计算机的地址结构(CPU寻址范围)确定

实际容量=min(内存和外存容量和,CPU寻址范围)

4.6.2 请求分页存储管理方式

分页系统基础上增加请求调页、页面置换。

以页面为单位置换,页面换入换出需要启动慢速I/O操作

硬件支持

页表

请求分页的页表机制

页号、物理块号、状态位、访问字段、修改位、外存地址

状态位P:表示页面是否在内存,不在则产生缺页中断,–存在位

访问字段A:记录页面被访问的情况

修改位M:表示页面装入内存后是否被修改过

外存地址:记录页面在外存上的地址

缺页中断机构

缺页中断属于内中断(故障),需要保留CPU现场。访管中断。

查快表(未命中)——查慢表(发现未调入)——调页(调入的页面直接加入快表)——查快表(命中)——访存

image-20210516153730278

地址映射机构

缺页中断和一般中断的区别
指令执行期间产生和处理中断信号
一条指令执行期间可能产生多次缺页中断
页面置换算法
最佳置换算法(OPT)
淘汰永不使用、在最长时间内不被访问的页面。向后检查最后一个出现的页号。

先进先出(FIFO)
会产生belady异常:分配的内存块增大,但缺页率增加。

最近最久未使用(LRU)
逆向扫描最后一个出现的页号。性能最接近OPT,开销大,需要硬件支持(移位寄存器/栈)

最少使用(LFU)
设移位寄存器,选择在最近使用最少的页面作为淘汰页。每次访问将最高位置1.每隔一段时间右移1位

简单时钟置换算法
链接循环队列,扫描访问位,是1则置零,是0则换出。

最多两轮扫描。

改进型的时钟置换算法
增加修改位,修改位=0未修改。

第一轮扫描到(0,0)替换

第二轮找(0,1),经过的访问位设0

第三轮找(0,0)

第四轮找(0,1)

最多四轮,实际找(0,0)(0,1)(1,0)(1,1)

页面缓冲算法PBA

有一个未被修改的页要换出时,不将它换出内存,把该页所在的物理块挂在自由页链表。换一个已修改的页面时挂在修改页面链表的末尾。使已被修改的页面和未被修改的页面保留在内存中。当被修改的页面数目达到一定值时再将它们一起写回到磁盘上。

影响页面换进换出效率的若干因素

页面置换算法
写回磁盘的频率
读入内存的频率

驻留集

指给进程分配的物理块的集合

驻留集大小一般小于进程的总大小

页面置换策略

内存分配策略
固定分配
每个进程分配一组固定数目的物理块,运行期间不改变。

平均分配算法

按比例分配算法

考虑优先权分配算法

可变分配
先为进程分配一定数目的物理块,在进程运行期间,根据情况增加减少。

局部置换 只能从本身已经在内存的页面中选择置换
全局置换 可从所以页面内置换
缺页则取出一个空闲物理块分配,无空闲块则选一页调出。

全局置换和固定分配策略不存在。

固定分配局部置换

进程分配的物理块固定不变

可变分配全局置换
当空闲块用完时,选择一页调出

可变分配局部置换
根据缺页频率动态增加减少物理块

抖动与工作集
多道程序度:希望能在系统中运行更多进程,即增加多道程序度,调高处理机的利用率。

抖动:频繁的页面调度。频繁的页面换入和换出

原因
进程频繁访问的页面高于可用物理块数。同时在系统中运行的进程太多。

工作集:某段时间内进程实际访问页面的集合。可能小于窗口尺寸。驻留集不能小于工作集。

为进程分配较大的物理块

正确选择工作集的大小,利于存储器的利用率和系统吞吐量提高

页面置换算法:选一个不在工作集中的页面置换。

如何解决:

如何预防:几乎都采用调节多道程序度

采用局部置换

把工作集算法融入到处理机调度中(为缺页率高的作业增加新物理块s)

利用L=S准则调节缺页率

L是缺页之间的平均时间,S是平均缺页服务时间,即用于置换一个页面所需的时间。

选择暂停的进程

何时调入页面
预调页(运行前):主要用于进程的首次调入。

请求调页(运行期间):运行期间发现缺页时将所缺调入

何处调入页面
对换区速度>文件区

影响缺页率的因素
页面大小、所分物理块数、置换算法、程序固有特性。

缺页中断处理时间
被置换的页面被修改的概率是β,其缺页中断处理时间为ta,被置换页面没有被修改的缺页中断时间为tb,缺页中断处理时间的计算公式为
      t=β×ta+(1—β)×tb

请求分段
分段系统基础上增加了请求调段、分段置换。

以段为单位置换

硬件支持:

请求分段的段表机智
缺段中断机构
地址变换机构
请求段页式
question
q:覆盖技术与虚拟存储技术有何本质上的不同?

a:覆盖程序段的最大长度受内存容量大小限制。

虚拟存储器中程序最大长度不受内存容量限制,只受计算机地址结构的限制。

覆盖技术覆盖段由程序员设计,要求覆盖段中的各个覆盖具有相对独立性,不存在直接联系或相互交叉访问。

虚拟存储无要求。

q:交换技术与虚拟存储的调入调出由什么不同?

a:交换就是把不用的某程序及数据从内存移到外存。

相同点:都在内存与外存之间交换信息。

区别:交换技术调入调出整个进程,一个进程大小受内存容量大小限制。

虚存的调入调出传递的是页面或分段,允许的进程大小比可用内存大。

q:实现LRU算法所需的硬件支持是什么?

a:寄存器/栈

386保护模式地址转换
CR3控制寄存器,利用页目录索引项找到页表,找到物理块+

SSTF

SCAN

    某请求分页系统中,若其指令系统指令长16位,每个操作数的地址码长6位,操作码4位,每个操作数16位,则执行一条双操作数指令,则最多可能发生几次缺页中断?
6次,指令本身可能跨页,所以可能缺两次页,每个操作数都可能缺页两次,所以最多缺页共6次

针对某虚拟存储系统,进行了CPU利用率和页面交换磁盘的利用率的检测,发现有四种情况:
(1)CPU利用率低,磁盘利用率高;
(2)CPU利用率高,磁盘利用率低;
(3)CPU利用率低,磁盘利用率低;
(4)CPU利用率高,磁盘利用率高。

     请你分析一下,系统在这些不同情况时发生了什么事情?当CPU利用率低的时候,如果增加进程并发数能提高CPU利用率吗?虚拟存储器是否起到作用了?
(1)可能发生了抖动。(2)在系统中计算密集型进程为主。(3)系统中进程较少,或者进程大部分处于阻塞状态。
(4)系统可能处于非正常状态,进程异常大量占用CPU和内存。
如果系统发生了抖动,此时CPU利用率低,但是如果继续增加进程并发数,则适得其反,会加剧系统的抖动。这可能是页面置换策略不当引起的,所以虚拟存储器此时无法解决。

在虚拟分页系统中,页面调入策略将对系统性能产生较大的影响。页面调入策略主要考虑两方面的事情:一是什么时候调入页面,二是从哪里调入页面。请你设计一种调页策略,希望达到以下目的:(1)能尽量降低进程的缺页率;(2)能尽量减少磁盘I/O的启动次数;(3)能尽量提高内存的利用率;(4)尽量提高页面调入速度。请详细说明你的调页策略的思路,并说明你的策略是如何满足上述性能要求的。
(1)什么时候调入页面:可以采用请求调页策略与预调页策略相结合的方式:请求调页策略是不提前调入,当发生缺页时把所缺页面调入内存,保证调入的页面就是要访问的,避免调入无用页面占据内存空间,提高内存利用率;在调入所缺页面的同时,依据进程运行的局部性原理,将该页面后续临近的若干页面(如8个页面)同时调入内存,当进程之后要访问这些页面时就不会发生缺页了,从而降低缺页率。

4.7 Linux内存管理机制

5.设备管理

5.1设备管理的功能

1.设备分配:

数据结构,分配算法

2.缓冲管理

目的、实现机制

3. 设备处理

设备驱动+中断处理

5.2 输入输出系统I/O

设备分类

1、按数据传输率分类:
高速设备: ≥几百万字节/秒
中速设备:几千到几十万字节/秒
低速设备:几个到几百个字节/秒

2、按信息交换的单位分类:
一次I/O操作的最小数据传输单位

块设备–以数据块来传送数据的设备,如磁盘

特点:1)信息交换的单位为等长数据块;
2)可寻址;
3)I/O控制采用DMA方式。
4)信息存储设备

字符设备-以单个字符为单位来传送信息的设备,如终端,打印机

特点:1)信息交换的单位为字符或字节;
2)不可寻址;
3)I/O控制采用中断驱动方式。
4)信息输入输出设备

按共享属性

独占设备–共享设备–虚拟设备

按工作特性

存储设备–I/O设备

设备控制器

功能

接受和识别命令

数据交换

地址识别

数据缓冲

识别和报告设备状态

组成

image-20210617204430442

(1) 设备控制器与CPU间的接口
系统接口: 主要由一组接口寄存器和信号线组成。

接口寄存器类型:
数据寄存器:
存放要输入输出的数据;
地址寄存器:
存放块设备的内部寻址信息;
控制寄存器:
存放CPU向控制器发出的命令;
状态寄存器:
存放设备状态信息。

(2) 设备控制器与设备的接口–一个设备控制器上连接一个或多个接口

控制器中的I/O逻辑根据处理器的地址信号选择一个接口

(3) I/O逻辑,用于实现对I/O设备的控制

I/O通道

通道概念

I/O通道是一种特殊的处理机,它控制设备与内存直接进行数据交换 。
I/O通道与一般的处理机的区别:
通道的指令类型单一,主要局限于与I/O操作有关的指令;
通道没有自己的内存,是与CPU共享内存。

通道类型

字节多路通道
分时控制多台低速或中速设备

数组选择通道
串行控制多台高速设备

数组多路通道
分时控制多台高速设备

通道程序

保存在主存里,由一系列通道指令构成

① 操作码:规定指令所执行的操作。
② 内存地址:内存缓冲区首址。
③ 计数:本条指令要读写的字节数。
④ 通道程序结束位P:P=1表示本条指令是通道程序最后一条指令。

⑤ 记录结束标志R:R=0表示本条指令与下一条指令所处理的数据属同一条记录。

I/O系统结构

总线型I/O系统结构:微机I/O系统

总线:是一组线和一组严格定义的可以描述在线上传输信息的协议,这一组线用来连接多个设备,这种连接称为总线。

通道型I/O系统结构:主机I/O系统

I/O通道是一种特殊的处理机,它控制设备与内存直接进行数据交换 。
I/O通道与一般的处理机的区别:

  • 通道的指令类型单一,主要局限于与I/O操作有关的指令
  • 通道没有自己的内存,是与CPU共享内存。

5.3 输入/输出控制方式

程序I/O方式

image-20210617210011570

中断驱动I/O控制方式–常用于字符设备I/O控制

image-20210617210641517

直接存储器访问(DMA)I/O控制方式

DMA控制方式的特征:–常用于块设备的I/O控制

① 数据传输的基本单位是数据块;
② 数据传送是在设备与内存之间直接进行的;
③整块数据的传送是在DMA控制器的控制下完成的,仅在传送一个或多个数据块的开始和结束时,才需CPU干预。

image-20210617211506063

传送过程:

(1)DMA传送前预处理:(CPU完成)

测试设备状态

CPU初始化DMA控制器

  • 主存缓冲区首址–>MAR(内存地址寄存器)

  • 传送数据字节数–>DC (传输字节数计数器)

  • 设置传输方式

启动设备

(2)完成设备与主存之间的数据传送

① 磁盘控制器将整块数据从磁盘读入DMA控制器的DR中;
② 磁盘控制器校验读入的数据;
③ 磁盘控制器向DMA控制器发DMA请求
④DMA控制器向CPU请求总线周期,获得总线控制权后:

image-20210617211947566

image-20210617212104747

(3)CPU响应中断进行后处理:(CPU完成)

I/O通道控制方式

​ 通道控制方式是一种以内存为中心,实现设备和内存间直接交换数据的控制方式。

1.通道的运算控制部件

① 通道地址字(CAW):记录下一条通道指令的地址,其功能类似于CPU的指令计数器。

② 通道命令字(CCW):记录正在执行的通道指令,其作用相当于CPU的指令寄存器。

③ 通道状态字(CSW):记录通道、控制器、设备的状态,包括I/O传输完成信息、出错信息、重复执行次数等。

2.I/O控制过程

image-20210617212318040

1、磁盘设备的I/O控制方式主要是采用____DMA____方式;打印机的I/O控制方式主要是采用__中断驱动______方式;
2、DMA方式是在( A )之间建立一条直接数据通路。???
A.I/O设备和主存                        B.两个I/O设备之间
C.I/O设备和CPU                         D.CPU和主存
3、通道又称I/O处理机,用于实现( A )之间的数据传输。
A. 内存与外设                        B. CPU与外设
C. 内存与Cache                        D. CPU与主存
4.计算机系统中,不属于DMA控制器的是( D )。
A.命令/状态寄存器               B.内存地址寄存器
C. 数据寄存器                         D. 堆栈指针寄存器
5、本地用户通过键盘登录系统时,首先获得键盘输入信息的是(  B  )。
A.命令解释程序                        B.中断处理程序
C.系统调用服务程序                     D.用户登录程序
6、I/O中断是CPU与通道协调工作的一种手段,所以在( C )时,便要产生中断。
CPU执行“启动I/O”指令而被通道拒绝接收
通道接收了CPU的启动请求
通道完成了通道程序的执行
通道在执行通道程序的过程中
(CPU启动通道时不管启动成功与否,通道都要回答CPU)
7、某计算机系统中,时钟中断处理程序每次执行时间为2ms(包括进程切换开销),若时钟中断频率为60HZ,试问CPU用于时钟中断处理的时间比率是多少?
简答:时钟中断频率为60HZ,则中断周期为1/60s,所以比率是:(2ms)/(1/60s)=12%

5.4缓冲管理

缓冲是在两种不同速度的设备之间传输信息时平滑传输过程的常用手段

缓冲的引入

CPU和I/O设备间的速度不匹配

提高两者的并行性

减少对CPU的中断率,放宽对CPU中断响应时间的限制

缓冲实现机制

单缓冲

image-20210617213127203

系统对一块数据的处理时间:Max(C,T)+M

双缓冲

image-20210618141442981

系统对一块数据的处理时间:Max(C+M,T)

循环缓冲

在双缓冲的基础上,增加缓冲区的数量来优化

缓冲池

缓冲池组成

①缓冲队列

image-20210617213514156

②工作缓冲区

image-20210617213559021

image-20210617213611833

缓冲池的使用

/*信号量设置:
资源信号量:表示某类缓冲队列中缓冲区的数量:
    RS(emq)=n;  RS(inq)=0;   RS(outq)=0;
互斥信号量:实现相应缓冲队列的互斥使用:
      MS(emq)=MS(inq)=MS(outq)=1;
*/
void Get_buf(type)//申请缓冲区
{
wait(Rs(type));
wait(mutex(type));
B(number)=takebuf(type);
signal(mutex(type));
}

void Put_buf(type,number)//释放缓冲区
{
wait(mutex(type));
add_buf(type,number);
signal(mutex(type)); 
signal(Rs(type));
}

image-20210617213820439

UNIX系统的缓存区管理(块缓冲)

1.系统缓冲管理的思路

减少对磁盘的I/O操作次数,加快系统响应

预先缓存:当进程要从磁盘读取数据时,首先从高速缓冲中读

image-20210617214120040

延迟发送:当进程要写数据到磁盘时,先写入高速缓冲中

image-20210617214202142

2.缓冲管理数据结构

缓冲区组成:缓存数组,缓存首部

缓存首部结构:设备号,块号,状态flag

缓存区队列结构:设备缓冲队列,空闲缓冲区队列

UNIX缓冲管理算法:LRU算法

首先寻找该设备的b链-找到/未找到

对延迟写的满足

释放缓冲区

1、引入缓冲的主要目的是(  C   )。
A.提高CPU的利用率
B. 提高I/O设备的利用率
C. 改善CPU与I/O设备速度不匹配的问题
D.节省内存
2、为了使并发进程能有效地进行输入和输出,最好采用( A )
结构的缓冲技术。
A.缓冲池     B.循环缓冲      C.单缓冲    D. 双缓冲
3、缓冲技术中的缓冲区设置在( A   )。
A.主存      B. 外存           C.ROM         D.寄存器
3、设从磁盘将一块数据传送到缓冲区所用时间为80us,将缓冲区中的数据传送到用户区所用时间为40us,CPU处理一块数据所用时间为30us。如果有多块数据要处理,并采用单缓冲区机制,则处理一块数据所用的总时间是多少?
M+MAX(C,T)
40+(80,30)
4、某文件占10个磁盘块,现要把该文件磁盘块依次读入主存缓冲区,并送至用户区进行处理,假设一个缓冲区与一个磁盘块大小相同,把一个磁盘块读入缓冲区的时间为100us,将缓冲区的数据传送到用户区的时间为50us,CPU对一块数据的处理时间为50us,在单缓冲区和双缓冲区机制下,读入并处理完成该文件的时间分别是多少?
50+100=150,150*10+50=1550单缓冲
100*10+50+50=1100双缓冲

5.5 I/O软件

层次模型

image-20210617214808846

独立于设备的软件

1.设备独立性的概念
是指用户在程序中使用的设备与实际使用的设备无关,即在用户程序中仅使用逻辑设备名。也称为设备无关性。

image-20210617214913656

image-20210617215159334

2.引入设备独立性的好处
(1) 提高设备分配的灵活性
(2) 容易实现I/O重定向 例: cat test.sh > test.txt

3.设备独立性的实现
逻辑设备表LUT:
内容:逻辑设备名、物理设备名、设备驱动程序入口地址
设置方式:
整个系统一张表
每个用户一张表

4.设备独立性软件的功能
(1) 实现设备命名及映射

逻辑设备名物理设备名
/dev/sdb18, 17

(2) 提供设备保护,存取控制检查
(3)提供与设备无关的逻辑块
(4)实现缓冲技术
(5)出错处理

设备驱动程序

1.设备驱动程序功能

(1) 接收由上层软件发来的抽象命令, 将其转换为具体要求,并插入请求队列。如打印机驱动程序需要将打印数据从存储编码(内码)转换成输出编码(字库)
(2) 完成I/O操作的初始化工作:检查用户I/O请求的合法性,了解I/O设备的状态,传递有关参数,设置设备的工作方式。
(3) 发出I/O命令,启动I/O设备。
(4) 及时响应由控制器或通道发来的中断请求。
(5) 根据用户的I/O请求,自动地构成通道程序。

2.设备驱动程序的特点

(1) 驱动程序是请求I/O的进程与设备控制器之间的一个通信和转换程序。
(2) 驱动程序与设备控制器和I/O设备的硬件特性紧密相关, 因而对不同类型的设备应配置不同的驱动程序。
(3) 驱动程序与I/O设备所采用的I/O控制方式紧密相关。
(4) 由于驱动程序与硬件紧密相关, 因而其中的一部分代码必须用汇编语言书写。
(5) 驱动程序应允许可重入。

3.设备处理方式

指I/O进程的设置方式:
(1)为每类设备设置一个I/O进程
(2)整个系统中设置一个I/O进程
(3)不设置专门的设备处理进程 (目前主流方式)

4.设备驱动程序的处理过程

(1)将抽象要求转换为具体要求
(2)对服务请求进行校验
(3)检查设备的状态
(4)传送必要的参数
(5)启动I/O设备

举例:键盘驱动程序、打印机驱动程序

用户空间的I/O软件

与I/O操作相关的库过程:
open,write,read等

  • 设置系统调用参数
  • 完成I/O操作格式化工作
1、一个计算机系统配置了2台相同的绘图仪和3台相同的打印机为正确驱动这些设备,系统应该提供(  C   )个驱动程序。
5          B.3          C.2            D.1
2、用户程序发出磁盘I/O请求后,系统的处理流程是:用户程序→ 系统调用处理程序→ 设备驱动程序→ 中断处理程序。其中,计算数据所在磁盘的柱面号、磁头号、扇区号的程序是( C )。
A.用户程序                   B.系统调用处理程序      
C.设备驱动程序             D. 中断处理程序
3、下列关于设备独立性的说法中,正确的是(  B  )。
A.设备独立性是指I/O设备具有独立执行I/O功能的一种特性
B.设备独立性是指用户程序独立于具体物理设备的一种特性
C.设备独立性是指能够实现设备共享的一种特性
D.设备独立性是指设备驱动程序独立于具体物理设备的一种特性
4、说明以下工作分别是由I/O软件中的哪一层软件完成的?
(1)向设备寄存器写命令。 驱动
(2)检查用户是否有权使用设备。  独立
(3)将二进制整数转换成ASCII码格式打印。 用户
(4)缓冲管理    独立      (5)为文件分配磁盘存储空间  设备驱动

5.6 设备分配

设备标识

设备号:设备绝对号(物理设备名)

image-20210618131503291

设备分配中的数据结构

设备控制表-DCT-一个设备一个表

控制器控制表

通道控制表

系统设备表SDT

设备分配算法
  • 先来先服务

  • 优先级高者优先

设备分配过程

分配设备–分配控制器–分配通道–

5.7 SPOOLing系统

在联机情况下实现的同时外围操作称为SPOOLing,或称为假脱机操作。

使用磁盘作为缓冲区实现虚拟设备

设计思想

① 预输入
由OS预先将程序所需数据输入到辅存输入井存放;当应用程序 (或进程) 需要数据时,可直接从辅存中读入主存。
② 缓输出
在应用程序执行时,将输出数据写入辅存输出井中。以后适当时候,由操作系统将数据输出。

系统组成

输入井:模拟脱机输入时的磁盘

输出井:模拟脱机输出时的磁盘

预输入程序:输入进程–模拟脱机输入时的外围机

缓输入程序:输出进程–模拟脱机输出时的外围机

井管理程序–输入井读;输出井写

image-20210618132454118

SPOOLING系统的工作原理

image-20210618132538873

共享打印机的实现

(1)当用户进程请求打印时, SPOOLing系统为它做两件事:
① 在输出井中为之申请一个空闲磁盘分区, 并将要打印的数据送入其中;
② 再为用户进程申请一张空白的用户请求打印表,并将用户的打印要求填入其中, 再将该表挂到打印请求队列上

(2)打印机空闲时:输出进程取出一张打印请求表,再从输出井中取出打印数据到输出缓冲区,通过打印机进行打印

image-20210618132757216

用户程序通过系统调用向系统提出使用外部设备的要求

用户态执行的是命令解释程序

微内核的优点不包括高效性

设计多道批处理系统时,首先要考虑系统效率和吞吐量

计算机系统判断中断事件发生是在执行完一条指令后

在多线程的系统中,进程P创建的多个线程不能共享某线程的栈指针

在10个生产者、8个消费者共享具有6个单元缓冲区的缓冲池的生产者-消费者问题中,互斥使用缓冲池的信号量的初始值为( 1 )。

6.文件系统

文件系统

  1. 文件系统的概念
    文件系统是指被管理的文件、对文件进行管理的一组软件集合
    以及实现管理功能所需要的数据结构的总体。

  2. 文件系统的功能
    (1)从用户的角度看:
    文件系统实现了“按名存取”的功能
    (2)从系统的角度看:

    文件存储空间管理

    文件目录管理

    文件地址映射

    文件读写管理

    文件共享和保存

文件的结构与存取

文件的逻辑结构

设计原则

  • 操作手段简单易用
  • 提高文件信息的检索速度
  • 方便文件内容的修改
  • 数据空间紧凑降低文件的存储费用
  • 系统灵活性

有结构文件:指整个文件由若干条记录构成,也称记录式文件

  • 数据组织形式分成据项、记录和文件三级
  • 定长记录文件与变长记录文件
  • 大量的数据结构和数据库,是采用有结构文件的形式

无结构文件:由一组相关信息组成的有序字符流,也称流式文件

  • 文件长度按字节计算。
  • 对流式文件的访问,是用读写指针指出下一个要访问的字符。

UNIX、DOS、WINDOWS系统中的文件都是流式文件

1.有结构文件

顺序文件

索引文件

索引顺序文件

直接文件和散列文件

文件的物理结构

文件的物理结构又称为文件的存储结构,是指文件在外存上的存储组织形式,它与存储介质的物理特性、文件的存取方法以及所采用的存储空间的分配方式都有关。

物理块(又称为磁盘块或者簇):磁盘上一组连续扇区,大小一般是2的n次方(n为整数)个扇区,它是文件分配和传输信息的基本单位。

连续文件

又称顺序文件,它是把逻辑文件中的信息顺序地存放到一组相邻接的磁盘块中而形成的物理文件

image-20210606160159703

优点:顺序存取速度快,方便随机存取

缺点:磁盘空间会产生碎片,文件修改困难

链接文件

一个逻辑上连续的文件分散存放在多个不连续的磁盘块中,再使用链接指针将这多个离散的磁盘块链接起来,这样形成的物理文件称为链接文件

①隐式链接

image-20210606160450980

缺点:文件只能顺序存取;文件容易丢失

②显式链接

所有链接指针统一存放在一张显示的链接表(FAT,File Allocation File,称为文件分配表)中。一个逻辑磁盘设置一张表,以物理盘块号为序,表项内容为指向某文件的下一盘块的指针。

image-20210606160539067

主要优点:检索速度快;支持随机存取

主要缺点:FAT表占内存空间;FAT表较大时,随机存取效率降低

FAT是以磁盘上的物理盘块为序,故FAT表项数量是由此磁盘块的数量决定。

查找FAT是在内存而非磁盘中进行,可显著提高文件检索速度

FAT表大小的计算方法
例:一个磁盘分区大小为20GB,若盘块大小为1KB,计算该磁盘分区的FAT表大小。
答:盘块数=20GB/1KB =20M,则盘块号大小为3.5B (2^25≈33M)      
FAT表大小=20M×3.5B=70MB
注意:每个磁盘块号大小取半个字节的整数倍。
练习:一个磁盘分区大小为10GB,若盘块大小为4KB,计算该磁盘分区的FAT表大小。
盘块数= 10GB/4KB = 10/4 M= 2.5M,    2^22 = 4M,  故盘块号大小为3B, FAT表大小= 2.5M* 3B = 7.5M

链接文件性能评价:

  • 存储空间利用率高
  • 文件创建时用户不必指出文件的大小
  • 文件动态扩充和修改容易
  • 顺序存取效率高,随机存取效率较低

索引文件

优点:支持高效的随机访问,采用离散分配方式,既不会产生外部碎片,也支持文件动态增长

缺点:每个文件需要额外分配索引块,增加系统开销,对小文件来说有些浪费空间

  • 索引表:系统为每个文件建立一张索引表,每个逻辑块占一个表项,以逻辑块号为序,表项内容为该逻辑块所对应的磁盘块号。

  • 索引块:存放索引表的盘块。

  • 索引文件:由数据文件和索引表构成。

    image-20210606161656942

①单级索引文件

如File_A存放在10#,17#,2#,11#盘块中,其索引块为20#盘块

image-20210606161741940

假设磁盘块大小为4KB,每个盘块号4B,那么单级索引文件结构能支持的最大文件是多少字节?若一个文件为10MB,则其索引表有多大?
4K/4=1K, 1K*4KB = 4M      10MB/4KB = 10/4 K = 2560项索引,需要3块磁盘块

②多级索引文件

对于大文件,其索引表本身可能会占多个磁盘块,这种情况下,可以将索引表离散存放在多个索引块中,再为这些索引块建立一级索引,这样就形成了两级索引文件。如果文件非常大时,还可使用三级、四级索引文件。

最大磁盘管理空间:2^表项位*最大文件–见例4

image-20210606161841174

③混合索引文件

将多种索引分配方式相结合形成的一种文件。 Unix和Linux采用的是混合索引文件,在其文件的物理地址字段中,既有直接地址,又有一级索引、两极索引和三级索引。

例如:UNIX system V:i节点中的物理地址字段iaddr(13):
iaddr(0) ~iaddr(9): 直接地址;
iaddr(10):一级索引;
iaddr(11): 二级索引;
iaddr(12): 三级索引。

设某文件长度为xB,若盘块大小为4KB,每个盘块号4B,则:
(1)文件盘块数量为:     n=[x/4k] + 1
(2)每个索引块能存放的盘块号数量:   =4K/4 =1K(个)

image-20210606162118375

最大文件计算:
假设每个磁盘块大小为4KB,每个磁盘块号需要大小为4B,则一个索引块中可以存放4KB/4B=1K个磁盘块号,那么UNIX system V文件系统中采用的混合索引,允许的最大文件长度为4TB(三级索引)+4GB(二级索引)+4MB(一级索引)+40KB(直接地址)。

文件物理结构的比较

  • 连续文件的优点是不需要额外的空间开销,只要在文件目录中指出文件的大小和首块的块号即可,对顺序的访问效率很高。适应于顺序存取且文件不经常修改的情况。

    缺点是文件动态地增长和缩小时系统开销很大;文件创建时要求用户提供文件的大小;存储空间浪费较大。

  • 链接文件克服了连续文件的不足之处,但文件的随机访问系统开销较大。适应于顺序访问的文件。

  • 索引文件既适应于顺序存访问,也适应于随机访问,是一种比较好的文件物理结构,但要有用于索引表的空间开销和文件索引的时间开销。UNIX系统是使用索引结构成功的例子

例1:在经常有随机存取需求和文件长度动态增长的情况下,宜选择(   )方式。
    A. 索引分配                 B. 连续分配
    C. 链接分配                 D. 都不对
 答案:A
例2:设有一个包含1000个逻辑记录的索引文件,每个记录刚好占用一个物理块。一个物理块可以存放10个索引表目。建立索引时,一个物理块应有一个索引表目,试问该文件系统至少应该建立(  )级索引?(假设最高级索引只占用一个物理块)。
      A.1      B.2       C.3      D.4
 答案:C

例3:一个采用二级索引的文件系统,若它所在目录文件已在内存,则存取一个文件的某个盘块信息通常要访问_______次磁盘。
   答案:3次
例4:某文件系统采用单级索引文件结构,假定文件索引表的每个表项占3个字节,存放一个磁盘块的块号(磁盘块的大小为512B)。试问:

1)该文件系统能支持的最大文件大小是多少字节?能管理的最大磁盘空间是多大?

2)若采用2级或3级索引,该文件系统能支持的最大文件大小是多少字节?能管理的最大磁盘空间是多大?
答案:
1)由于索引表占用一个大小为512B的磁盘,所以该文件系统的索引表可以管理512/3=170个表项,而每一个表项对应一个物理块,因此该文件系统可以支持的最大文件为:  170*512B=87040B=85K
表项3个字节--3*8=24
   能管理的最大磁盘空间:2^24*512B
2)若采用二级索引,则是:170*170*512B=7225KB

3)若采用三级索引,则是:170*170*170*512B=2456500KB=2398.93M

文件存取

文件的存取方法不仅与文件的物理结构有关,而且与用户如何使用文件有关。根据对文件记录的存取顺序,存取方法可分为顺序存取和随机存取两种。

  • 顺序存取是指按文件中的记录顺序依次进行读/写操作的存取方法。
  • 随机存取是指按任意的次序随机读/写文件中的记录。

image-20210606163201858

文件目录管理

实现按名存取

提高目录的检索速度

允许文件重名

允许文件共享

文件目录的概念

一组文件控制块(或文件目录项)的有序集合。每个文件控制块是用于描述和控制文件的数据结构,它保存系统管理文件所需要的全部属性信息。

文件控制块FCB

文件与FCB一一对应,是文件存在的唯一标志。

文件控制块包含以下三点:

1.基本信息

  • 文件名
  • 用户名:文件主、同组用户、用户组等
  • 文件类型
  • 文件物理地址和文件长度
  • 文件逻辑结构和文件物理结构

2.存取控制信息

文件主、文件主同组用户(或授权用户)、一般用户对该文件的存取权限

3.使用信息

  • 文件的建立日期及时间
  • 上次存取文件的日期及时间
  • 当前的使用状态信息
  • 共享链接计数等

Windows 98(FAT32)包含基本目录项和长文件目录项

image-20210606164345981

image-20210606164412818

索引节点

(1)索引节点引入原因

查找文件时平均启动磁盘I/O操作的次数较大,降低了目录检索效率。

检索目录的过程中实际上只用到了文件控制块中的文件名用户名,不需要将文件其它描述信息加载到内存。

​ 索引节点(简称i节点):文件系统文件控制块中除文件名以外的描述信息单独形成的数据结构

image-20210606164625470

分类

磁盘索引节点
存放在磁盘上的索引节点

  • 文件属性
  • 用户标识符
  • 文件物理长度
  • 文件物理地址
  • 文件的时间相关信息
  • 文件链接计数

内存索引节点
每当打开一个文件时,都会在内存中为该打开文件建立一个内存索引节点。内容在磁盘索引节点上增加

  • 状态
  • 访问计数
  • 逻辑设备号
  • 链接指针
文件目录结构

单级目录结构

整个文件系统只建立一张目录表。

image-20210606165007715

优点:实现简单,按名存取

缺点:查找速度慢,不允许重名,不便于实现文件共享

两级目录结构

将文件目录分为主文件目录和用户文件目录

image-20210606165409176

优点:提高了目录检索速度;允许文件重名;不同用户可以使用不同的文件名来访问系统中的同一个共享文件

缺点:无法很好地满足文件多的用户的需要

多级目录结构

两级目录结构加以推广,允许用户文件目录再建立下级子目录,由此形成了多级目录结构。在树形目录中,主目录则称为根目录,目录树中的非叶节点均为目录文件(又称子目录),叶节点为数据文件

image-20210606165523654

绝对路径:目录/子目录名…/文件名
相对路径:当前目录/子目录名…/文件名

优点:
层次清楚
允许文件重名
进一步提高目录检索速度
容易实现共享

目录检索技术

1.线性检索法

image-20210606165836600

2.Hash方法

建立一个Hash索引文件目录,当用户给定文件名之后,直接把它转换为文件目录的索引值,再利用该索引值查找目录。该方法不支持使用通配符的模式匹配查找功能。
冲突问题:文件名转换时不同的文件名可能转换成相同的Hash值
解决方法:此Hash值再加上一个常数形成新的索引值,然后重新开始查找。

思考题:
1.列举5种以上流行的文件系统,并指出它们分别在什么操作系统中使用?

2.从路径文件名可以唯一确定一个文件但是操作效率不是很理想(或者说通过路径找文件比较费时)。采取什么方法可以改进之。

3.设计文件系统时,子目录可以当作特殊的文件,也可以当作一般数据看待,请分析其优缺点

文件存储空间管理

文件存储空间管理是设置相应的数据结构来记录存储空间的使用情况,并提供对存储空间进行分配和回收的方法。

  • 空闲表法
  • 空闲块链表法
  • 位示图
  • 成组链接法

空闲表法

文件系统为外存上的所有空闲分区建立一张空闲表,每个空闲区占一个表项,包括序号、该空闲区的起始盘块号以及空闲盘块数等信息,再将所有空闲区按其起始盘块号递增的次序排列

image-20210606170052579

分配和回收方法类似于内存的动态分区分配和回收

空闲块链表法
(1)空闲盘块链:磁盘的每一个空闲盘块中存放一个指针,指向另一个空闲盘块,这样磁盘上的所有空闲块链接在一起形成一个链表。

image-20210606170138261

(2)空闲盘区链:每个空闲盘区包含若干个连续的空闲盘块

image-20210606170200348

位示图法Linux

概念

image-20210606170408321

盘块的分配:i,j,b从1开始计数

顺序扫描位示图,从中找出一个或一组其值为“0”的二进制位(“0”表示空闲时)。
将所找到的一个或一组二进制位, 转换成与之相应的盘块号。假定找到的其值为“0”的二进制位,位于位示图的第i行、第j列,则其相应的盘块号应按下式计算:
b=n(i-1)+j
式中, n代表每行的位数,i,j,b从1开始计数。
修改位示图, 令map[i,j]=1。

盘块的回收: i,j,b从1开始计数

将回收盘块的盘块号b转换成位示图中的行号i和列号j。 转换公式为:
i=(b-1)DIV n+1
j=(b-1)MOD n+1
修改位示图。 令map [i,j]=0。

成组链接法:Unix

空闲盘块的组织
对所有空闲盘块分组:
例如一磁盘有512块,编号为0#~511#,其中8#~499#是空闲盘块。每组100块,从后往前分组,则分组情况是:
最末组为99块:499~401;
其余每组100块,分别为:
400~301; 300~201; 200~101; 100~8

image-20210606171322782
空闲盘块的链接:
① 从第2组开始,将每组的总块数及相应的块号记录在前一组的最末块中:
② 对第1组,其总块数和各块块号记录在空闲盘块栈中,放在超级块里。
系统启动后,将超级块复制到主存中,并建立空闲盘块号栈,栈顶指针S_Free=第1组总块数。

image-20210606193506234

空闲盘块的分配针对空闲盘块栈进行。

count=当前组空闲盘块总数;
      S_Free--;
	b=*S_Free;
	if (count>1)then
		{  count--;
		    return b;}
	else if (count==1)then
		{  if (b==0)then 
			拒绝分配,返回0;
		   else
			{ 将b中内容读入空闲盘块栈;
               count=当前组空闲盘块总数;
			   S_Free=count;
			   return b;}
		}

空闲盘块的回收针对空闲盘块栈进行。

count=当前组空闲盘块总数;
	b:回收块号;(如回收50、60号块)
	if(count<100) then
		{   *S_Free=b;
		    count++;
		    S_Free++;
		    return;   }
		else if( count==100) then
		{   将空闲盘块栈内容写入b中;
		    count=1;
		    S_Free=0;
		    *S_Free=b;
		    S_Free++;
		    return;   }

文件共享与文件保护

文件共享

文件共享
指某一个或某一部分文件可以让事先规定的某些用户共同使用。

基于索引节点的共享方式

假设B用户想使用“/B/B1/B12”文件名访问共享文件C21。

image-20210606174341998

Linux:又称为硬链接
ln 共享文件名 新文件名
ln /C/C2/C21 /B/B1/B12

image-20210606174513702

符号链接法

image-20210606174648836

硬链接(hard link):

A是B的硬链接(A和B都是文件名),则A的目录项中的inode节点号与B的目录项中的inode节点号相同,即一个inode节点对应两个不同的文件名,两个文件名指向同一个文件,A和B对文件系统来说是完全平等的。如果删除了其中一个,对另外一个没有影响。每增加一个文件名,inode节点上的链接数增加一,每删除一个对应的文件名,inode节点上的链接数减一,直到为0,inode节点和对应的数据块被回收。注:文件和文件名是不同的东西,rm A删除的只是A这个文件名,而A对应的数据块(文件)只有在inode节点链接数减少为0的时候才会被系统回收。

软链接(soft link):

A是B的软链接(A和B都是文件名),A的目录项中的inode节点号与B的目录项中的inode节点号不相同,A和B指向的是两个不同的inode,继而指向两块不同的数据块。但是A的数据块中存放的只是B的路径名(可以根据这个找到B的目录项)。A和B之间是“主从”关系,如果B被删除了,A仍然存在(因为两个是不同的文件),但指向的是一个无效的链接。

主要区别、限制:

硬链接:

a:不能对目录创建硬链接,原因有几种,最重要的是:文件系统不能存在链接环(目录创建时的"…"除外,这个系统可以识别出来),存在环的后果会导致例如文件遍历等操作的混乱(du,pwd等命令的运作原理就是基于文件硬链接,顺便一提,ls -l结果的第二列也是文件的硬链接数,即inode节点的链接数)

b:不能对不同的文件系统创建硬链接,即两个文件名要在相同的文件系统下。

c:不能对不存在的文件创建硬链接,由原理即可知原因。

软链接:

a:可以对目录创建软链接,遍历操作会忽略目录的软链接。

b:可以跨文件系统

c:可以对不存在的文件创建软链接,因为放的只是一个字符串,至于这个字符串是不是对于一个实际的文件,就是另外一回事了

ln - 新建链接
ln 用于创建软或硬链接。

参数 作用
-s 创建软链接(如果不带 -s 参数,默认创建硬链接)
-f 强制创建文件或目录的链接
-i 覆盖前先询问
-v 显示创建链接的过程

软链接示例 :

[apple@VM_0_8_centos ~]$ echo "hello world" > helloWorld 
[apple@VM_0_8_centos ~]$ ln -s helloWorld  sHelloWorld
[apple@VM_0_8_centos ~]$ cat helloWorld 
hello world
[apple@VM_0_8_centos ~]$ cat sHelloWorld 
hello world
[apple@VM_0_8_centos ~]$ rm helloWorld 
[apple@VM_0_8_centos ~]$ cat sHelloWorld 
cat: sHelloWorld: No such file or directory
硬链接示例 :

[apple@VM_0_8_centos ~]$ echo "hello world" > helloWorld
[apple@VM_0_8_centos ~]$ ln helloWorld  hardHelloWorld
[apple@VM_0_8_centos ~]$ cat helloWorld 
hello world
[apple@VM_0_8_centos ~]$ cat hardHelloWorld 
hello world
[apple@VM_0_8_centos ~]$ ls  -lhi hardHelloWorld  helloWorld 
360888 -rw-rw-r-- 2 apple apple 12 Jun  4 17:10 hardHelloWorld  // 第一列为 inode 号,第三列为 inode 连接数
360888 -rw-rw-r-- 2 apple apple 12 Jun  4 17:10 helloWorld    // 两个文件的 inode 号是一样的。
[apple@VM_0_8_centos ~]$ rm helloWorld 
[apple@VM_0_8_centos ~]$ cat hardHelloWorld 
hello world


文件保护

1.文件备份

批量备份

同步备份

2.文件访问保护

~口令保护-设置密码

~加密保护-加密编码后保存

~设置文件访问权限

对每一类用户分别规定使用文件的权限,然后将这些使用权限作为文件的存取控制信息保存在文件控制块或索引节点中。当用户要求访问某个文件时,系统首先检查该用户类型,然后再按存取权限的规定核用户的使用要求,仅当权限符合时才允许访问该文件。
访问控制矩阵,访问控制表,访问权限表

磁盘调度

磁盘管理概述

1 数据的组织和格式

盘面号(磁头号): 0 ~ M-1;
柱面号(磁道号): 0 ~ L-1;
扇区号: 1 ~ N;

image-20210606175141031

image-20210606175226820

扇区编址方式
CHS(Cylinder/Head/Sector,柱面/磁头/扇区)方式:
使用柱面号、磁头号和扇区号表示每个扇区,DOS中称为
“绝对扇区”表示法。
LBA(Logical Block Addressing,相对扇区号)方式:
相对扇区号标识扇区,以磁盘第一个扇区(0柱面、0磁头、
1扇区)作为LBA的0扇区。

LBA与CHS的转换
若L、M、N分别表示一个磁盘的柱面数(磁道数)、盘面数(磁头数)、扇区数,则第i柱面、j磁头、k扇区所对应的LBA扇区号为:
L B A = ( i ∗ M ∗ N ) + ( j ∗ N ) + k − 1 LBA= (i*M*N) + (j*N) + k-1 LBA=(iMN)+(jN)+k1
若知道LBA扇区号,则对应的柱面号、磁头号、扇区号分别是:

$$
柱面号:i=int(LBA /(M*N))

磁头号:j=[LBA mod(M*N)]/N

扇区号:k =[LBA mod(M*N)] mod N+1
$$
存储容量
=磁头数×磁道(柱面)数×每道扇区数 ×每扇区字节数

2 磁盘访问时间

(1)寻道时间
磁头从当前位置移动到指定磁道所需要的时间
Ts=m*n+s
其中s是启动磁臂的时间;m是磁头每移动一条磁道所需要的时间;n是移动的磁道数。
m:一般磁盘:0.2~0.3;
高速磁盘:m≤0.1
S:磁臂启动时间,约为2ms~ 3ms

(2)旋转延迟时间Tr
欲访问扇区旋转到磁头下面所需要的时间,粗略的认为是磁盘旋转半周的时间
Tr = 1/2r
这里r表示旋转速度
(3)传输时间Tt
把数据从磁盘读出或向磁盘写入所需要的时间(N为一个磁道上字节数)
T t = b / r N Tt=b/rN Tt=b/rN
因此,可将磁盘访问时间Ta表示为
T a = T s + 1 / 2 r + b / r N Ta=Ts+1/2r+b/rN Ta=Ts+1/2r+b/rN

磁盘调度算法

移臂调度:当同时有多条磁道访问请求时,确定磁道访问顺序,以减少平均寻道时间

(1)先来先服务(FCFS,First Come First Served)算法

image-20210606180008583

(2)最短寻道时间优先(SSTF ,Shortest Seek Time First)算法

image-20210606180052272

问题: (1)不能保证平均寻道距离最短 (未总体统筹);
(2)会产生饥饿现象;
(3)影响磁盘的机械寿命(未考虑当前移动方向)。

(3)扫描(SCAN)算法 (又称为电梯算法)

1欲访问磁道与当前磁道的距离;
2磁头当前的移动方向。

image-20210606180224907

(4)循环扫描(CSAN ,Circular SCAN)算法

1欲访问磁道与当前磁道的距离;
2磁头仅某一单向移动方向时访问磁道。

132 190 205 376 4 23 29 40 61

(5) N-Step-SCAN算法

算法思想:将磁盘请求队列分成若干个长度为N的子队列,磁盘调度将按FCFS算法依次处理这些子队列;

而每处理一个子队列时又采用SCAN算法。

N=4,分为3个队列;23 376 205 132;61 190 29 40;4

132 205 376 23 29 40 61 190 4

(6) FSCAN(Fair SCAN)算法

该算法实质上是N步SCAN算法的简化, 它只将磁盘请求队列分成两个子队列:
① 由当前所有请求磁盘形成的队列,由磁盘调度按SCAN算法进行处理。
② 在扫描期间,将新出现的所有磁盘I/O请求, 放入另一个等待处理的请求队列。

没有新到达的磁盘所以和SCAN一样

旋转调度:当一条磁道上有多个扇区访问请求时,确定扇区访问顺序,以减少旋转延迟时间

当同一磁道(柱面)上有多个扇区请求时,总是选取与当前读写头最近的那个I/O请求,使旋转圈数最少。

image-20210606180438172

6.7 Linux文件系统

Linux的文件类型
(1)普通文件:又称为常规文件,存放用户及操作系统的程序代码、数据等信息。
(2)目录文件:存放所有文件的目录项。
(3) 设备文件:所有的外部设备都当作文件来看待,每个设备都对应一个设备文件。
(4)管道文件:为支持管道通信机制设置,又称为FIFO文件。
(5)符号链接文件:实现基于符号链接的文件共享机制。
(6)Socket文件:用于实现Socket通信机制的文件。

Linux的文件存取权限
Linux系统把文件的用户分成3类:文件主、同组用户和其他普通用户,然后针对每个文件分别对3类用户设置访问权限,权限包括三种操作:可读R、可写W和执行X。

image-20210606181541673

VFS的主要对象类型
(1)超级块对象(superblock object)
存放系统中已安装文件系统的有关信息,每个文件系统一个,是各种具体文件系统在安装时建立的,只存在于内存中。
(2) 索引节点对象(inode object)
存放关于具体文件的一般信息。每个打开文件都有一个索引节点对象。
(3)目录项对象(dentry object)
存放目录项与对应文件进行链接的信息。
(4)文件对象(file object)
存放打开文件与进程之间进行交互的有关信息。这类信息仅当进程访问文件期间存在于内存中。

Linux文件系统对文件的操作

1、open操作

在linux中,open操作是由open系统调用完成的;而open系统调用的服务例程是sys_open()(在文件/fs/open.c中实现)。

//fs/open.c
long do_sys_open(int dfd, const char _ _user *filename, int flags, int mode)
{
	char *tmp = getname(filename);
	int fd = PTR_ERR(tmp);
	if (!IS_ERR(tmp)) {
		fd = get_unused_fd_flags(flags);
		if (fd >= 0) {
			struct file *f = do_filp_open(dfd, tmp, flags, mode);
			if (IS_ERR(f)) {
				put_unused_fd(fd);
				fd = PTR_ERR(f);
			} else {
				fsnotify_open(f->f_path.dentry);
				fd_install(fd, f);
			}
		}
		putname(tmp);
	}
	return fd;
}      
//系统调用
asmlinkage long sys_open(const char __user *filename, int flags, int mode)
{
	long ret;
	if (force_o_largefile())
		    flags |= O_LARGEFILE;
	ret = do_sys_open(AT_FDCWD, filename, flags, mode);
	prevent_tail_call(ret);
	return ret;
}
/**主要算法步骤:
 getname()函数做路径名的合法性检测。getname()在检查名字合法性的同时要把filename从用户数据区拷贝到内核数据区。它会检验给出的地址是否在当前进程中的虚存段内;在内核空间中申请一页;并把filename字符的内容拷贝到该页中去。
 get_unused_fd()得到空的文件描述符指针。
 do_filp_open() 按照指定的读写方式和用户权限打开文件,并返回文件描述符指针f。
 如果所获file结构有错误,则释放文件描述符,设置fd为返回出错信息。
 fd_install() 将打开文件的文件描述符指针f装入当前进程打开文件表中。
 返回打开的文件描述符。*/
 //下面是sys_oepn 流程中重要函数do_filp_open函数实现的分析
static struct file *do_filp_open(int dfd, const char *filename, int flags, int mode)
{
	int namei_flags, error;
	struct nameidata nd;
	namei_flags = flags;
	if ((namei_flags+1) & O_ACCMODE)
		    namei_flags++;
	error = open_namei(dfd, filename, namei_flags, mode, &nd);
	if (!error)
		    return nameidata_to_filp(&nd, flags);
	return ERR_PTR(error);
}
/**主要算法步骤:
  根据参数flags计算namei_flags
  open_namei()通过路径名获取其相应的dentry和vfsmount结构。
  如果open_namei正常返回,则调用nameidata_to_filp(), 通过open_namei()得到的dentry和vfsmount来得到file结构。否则返回错误信息。*/

2、close操作

在Linux中,close操作是由close系统调用完成的。close系统调用的服务例程是sys_close()(在文件/fs/open.c中实现)。下面是对sys_close函数实现的分析。

asmlinkage long sys_close(unsigned int fd)
{
	struct file * filp;
	struct files_struct *files = current->files;
	struct fdtable *fdt;
	int retval;
	spin_lock(&files->file_lock);
	fdt = files_fdtable(files);
	if (fd >= fdt->max_fds)
		     goto out_unlock;
	filp = fdt->fd[fd];
	if (!filp)
             goto out_unlock;
	rcu_assign_pointer(fdt->fd[fd], NULL);
	    FD_CLR(fd, fdt->close_on_exec);
	    _ _put_unused_fd(files, fd);
	spin_unlock(&files->file_lock);
	retval = filp_close(filp, files);
	  	if (unlikely(retval == -ERESTARTSYS ||
		     retval == -ERESTARTNOINTR ||
		     retval == -ERESTARTNOHAND ||
		     retval == -ERESTART_RESTARTBLOCK))
		retval = -EINTR;
	return retval;
out_unlock:
	spin_unlock(&files->file_lock);
	return -EBADF;
}
/**主要算法步骤:
  检查所要关闭的fd的合法性,是否小于进程可使用最大文件max_fds。如果不合法,则跳转到out_unlock段释放file_lock并返回。
  根据fd获得文件描述符filp指针并对filp的合法性进行检查。如果不合法,同样跳转到out_unlock段释放file_lock并返回。
  将当前进程使用文件结构files中文件描述符指针数组fd的对应指针置为空,清除对应文件描述符的索引位。
  put_unused_fd将对应的文件描述符的索引位重新置为可用。
  filp_close释放文件描述符,关闭文件。*/

3、read操作

在linux中,read操作是通过sys_read系统调用实现的(在文件/fs/read_write.c中实现)。下面是sys_read函数

asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
{
	struct file *file;
	ssize_t ret = -EBADF;
	int fput_needed;
	file = fget_light(fd, &fput_needed);
	if (file) {
		    loff_t pos = file_pos_read(file);
		    ret = vfs_read(file, buf, count, &pos);
		    file_pos_write(file, pos);
		    fput_light(file, fput_needed);
	}
	return ret;
}

ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
	    ssize_t ret;
 
	if (!(file->f_mode & FMODE_READ))
		    return -EBADF;
	if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
		    return -EINVAL;
	if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
		    return -EFAULT;
	ret = rw_verify_area(READ, file, pos, count);
	if (ret >= 0) {
		    count = ret;
		    ret = security_file_permission (file, MAY_READ);
		    if (!ret) {
			    if (file->f_op->read)
				    ret = file->f_op->read(file, buf, count, pos);
			    else
				    ret = do_sync_read(file, buf, count, pos);
			    if (ret > 0) {
				    fsnotify_access(file->f_path.dentry);
				    add_rchar(current, ret);
			    }
		        inc_syscr(current);
		    }
	}
	    return ret;
}
/**主要算法步骤:
根据文件描述符fd调用fget_light() 函数获得要读取文件的file结构变量指针。fget_light()会判断打开文件表是否多个进程共享,如果不是的话,没有必要增加对应file结构的引用计数。
得到读文件开始时候的文件偏移量。
调用vfs_read()函数
判断访问模式是否为读模式
在vfs_read()函数中,rw_verify_area() 以读模式READ访问区域,返回负数表示不能访问。
如果file->f_op->read函数不为空,则调用read,从文件位置指针file开始读取count个字节的内容到用户缓冲区buf。File->f_op->read就是具体文件系统的read函数的实现。
调用 fsnotify_access()通知感兴趣的进程,该文件已经被访问过。vfs_read()函数结束返回。
重新写文件偏移量。
执行fput_light(), 如果需要会释放file结构的引用计数。*/

4、write操作

在linux中,write操作是通过sys_write系统调用实现(在文件/fs/read_write.c中实现)。下面是对sys_write函数实现的分析。


asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count)
{
	struct file *file;
	ssize_t ret = -EBADF;
	int fput_needed;
	file = fget_light(fd, &fput_needed);
	if (file) {
		    loff_t pos = file_pos_read(file);
		    ret = vfs_write(file, buf, count, &pos);
		    file_pos_write(file, pos);
		    fput_light(file, fput_needed);
	}
	    return ret;
}
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
	ssize_t ret;
	if (!(file->f_mode & FMODE_WRITE))
		    return -EBADF;
	    if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write))
		    return -EINVAL;
	if (unlikely(!access_ok(VERIFY_READ, buf, count)))
		    return -EFAULT;
	ret = rw_verify_area(WRITE, file, pos, count);
	if (ret >= 0) {
		    count = ret;
		    ret = security_file_permission (file, MAY_WRITE);
		    if (!ret) {
			    if (file->f_op->write)
				ret = file->f_op->write(file, buf, count, pos);
			else
				ret = do_sync_write(file, buf, count, pos);
			if (ret > 0) {
				fsnotify_modify(file->f_path.dentry);
				add_wchar(current, ret);
			}
			inc_syscw(current);
		  }
	}
return ret;
}
/**文件write操作和read操作非常类似,主要算法步骤:
  根据fd调用fget_light()函数得到要写文件的file结构变量指针。fget_light()会判断打开文件表是否多个进程共享,如果不是的话,没有必要增加对应file结构的引用计数。
  得到写文件开始时候的文件偏移量。
  调用vfs_write()函数。
判断访问模式是否为写模式
在vfs_write()函数中, rw_verify_area() 以写模式WRITE访问区域,返回负数表示不能访问。
如果file->f_op->write函数不为空,则调用write,把用户缓冲区buf中的count个字节的内容写入文件偏移量位置。
调用fsnotify_modify() 通知感兴趣的进程,该文件已经被修改过。vfs_write()函数结束返回。file->f_op->write 就是具体文件系统write函数的实现。
  写回文件偏移量。
  执行fput_light(), 如果需要会释放file结构的引用次数。*/


 retval == -ERESTARTNOINTR ||
	     retval == -ERESTARTNOHAND ||
	     retval == -ERESTART_RESTARTBLOCK))
	retval = -EINTR;
return retval;

out_unlock:
spin_unlock(&files->file_lock);
return -EBADF;
}
/**主要算法步骤:
检查所要关闭的fd的合法性,是否小于进程可使用最大文件max_fds。如果不合法,则跳转到out_unlock段释放file_lock并返回。
根据fd获得文件描述符filp指针并对filp的合法性进行检查。如果不合法,同样跳转到out_unlock段释放file_lock并返回。
将当前进程使用文件结构files中文件描述符指针数组fd的对应指针置为空,清除对应文件描述符的索引位。
put_unused_fd将对应的文件描述符的索引位重新置为可用。
filp_close释放文件描述符,关闭文件。*/


3、read操作

在linux中,read操作是通过sys_read系统调用实现的(在文件/fs/read_write.c中实现)。下面是sys_read函数

```c
asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
{
	struct file *file;
	ssize_t ret = -EBADF;
	int fput_needed;
	file = fget_light(fd, &fput_needed);
	if (file) {
		    loff_t pos = file_pos_read(file);
		    ret = vfs_read(file, buf, count, &pos);
		    file_pos_write(file, pos);
		    fput_light(file, fput_needed);
	}
	return ret;
}

ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
	    ssize_t ret;
 
	if (!(file->f_mode & FMODE_READ))
		    return -EBADF;
	if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
		    return -EINVAL;
	if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
		    return -EFAULT;
	ret = rw_verify_area(READ, file, pos, count);
	if (ret >= 0) {
		    count = ret;
		    ret = security_file_permission (file, MAY_READ);
		    if (!ret) {
			    if (file->f_op->read)
				    ret = file->f_op->read(file, buf, count, pos);
			    else
				    ret = do_sync_read(file, buf, count, pos);
			    if (ret > 0) {
				    fsnotify_access(file->f_path.dentry);
				    add_rchar(current, ret);
			    }
		        inc_syscr(current);
		    }
	}
	    return ret;
}
/**主要算法步骤:
根据文件描述符fd调用fget_light() 函数获得要读取文件的file结构变量指针。fget_light()会判断打开文件表是否多个进程共享,如果不是的话,没有必要增加对应file结构的引用计数。
得到读文件开始时候的文件偏移量。
调用vfs_read()函数
判断访问模式是否为读模式
在vfs_read()函数中,rw_verify_area() 以读模式READ访问区域,返回负数表示不能访问。
如果file->f_op->read函数不为空,则调用read,从文件位置指针file开始读取count个字节的内容到用户缓冲区buf。File->f_op->read就是具体文件系统的read函数的实现。
调用 fsnotify_access()通知感兴趣的进程,该文件已经被访问过。vfs_read()函数结束返回。
重新写文件偏移量。
执行fput_light(), 如果需要会释放file结构的引用计数。*/

4、write操作

在linux中,write操作是通过sys_write系统调用实现(在文件/fs/read_write.c中实现)。下面是对sys_write函数实现的分析。


asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count)
{
	struct file *file;
	ssize_t ret = -EBADF;
	int fput_needed;
	file = fget_light(fd, &fput_needed);
	if (file) {
		    loff_t pos = file_pos_read(file);
		    ret = vfs_write(file, buf, count, &pos);
		    file_pos_write(file, pos);
		    fput_light(file, fput_needed);
	}
	    return ret;
}
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
	ssize_t ret;
	if (!(file->f_mode & FMODE_WRITE))
		    return -EBADF;
	    if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write))
		    return -EINVAL;
	if (unlikely(!access_ok(VERIFY_READ, buf, count)))
		    return -EFAULT;
	ret = rw_verify_area(WRITE, file, pos, count);
	if (ret >= 0) {
		    count = ret;
		    ret = security_file_permission (file, MAY_WRITE);
		    if (!ret) {
			    if (file->f_op->write)
				ret = file->f_op->write(file, buf, count, pos);
			else
				ret = do_sync_write(file, buf, count, pos);
			if (ret > 0) {
				fsnotify_modify(file->f_path.dentry);
				add_wchar(current, ret);
			}
			inc_syscw(current);
		  }
	}
return ret;
}
/**文件write操作和read操作非常类似,主要算法步骤:
  根据fd调用fget_light()函数得到要写文件的file结构变量指针。fget_light()会判断打开文件表是否多个进程共享,如果不是的话,没有必要增加对应file结构的引用计数。
  得到写文件开始时候的文件偏移量。
  调用vfs_write()函数。
判断访问模式是否为写模式
在vfs_write()函数中, rw_verify_area() 以写模式WRITE访问区域,返回负数表示不能访问。
如果file->f_op->write函数不为空,则调用write,把用户缓冲区buf中的count个字节的内容写入文件偏移量位置。
调用fsnotify_modify() 通知感兴趣的进程,该文件已经被修改过。vfs_write()函数结束返回。file->f_op->write 就是具体文件系统write函数的实现。
  写回文件偏移量。
  执行fput_light(), 如果需要会释放file结构的引用次数。*/


  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Challfate

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值