计算机操作系统

概论

操作系统的定义

操作系统发展

发展阶段 存在问题或特点 特性
手工操作阶段 人机速度矛盾
单道批处理系统 资源利用率低 自动性、顺序性、单道性
多道批处理系统 没有人机交互 资源利用率高、系统吞吐量大、平均周转时间长、无交互能力
分时OS 没有优先级 多路/同时性(主机同时连接多个终端)
独立性(用户终端彼此不干扰)
及时性(及时响应用户请求)
交互性
硬实时OS 严格规定时间内完成 多路性、独立性、及时性、交互性、可靠性
软实时OS 可偶尔违反时间规定 多路性、独立性、及时性、交互性、可靠性
微机操作系统 单用户单任务、单用户多任务、多用户多任务
分布式操作系统

操作系统特征

操作系统运行机制

状态 PSW 程序 指令
核心态/管态 1 内核程序 全部指令(特权、非特权)
用户态/目态 0 应用程序 非特权指令
graph LR U([用户态]); S([核心态]); U--"中断(唯一途径)"--->S; S--"通过特权指令设置PSW=0"--->U;

用户态可以发生:系统调用、中断(外部中断、缺页等),但不能发生进程切换。

系统调用

系统调用需要执行一些特权指令,故需要在核心态下进行。

系统调用是操作系统与程序之间的接口。

系统调用和库函数:系统调用需要使用汇编语言调用。高级语言库函数可以是对系统调用的封装(即库函数不一定使用到系统调用)。

操作系统主要功能(系统调用功能):处理机管理(进程控制、进程同步、进程通信、进程调度)、存储器管理/内存管理、设备管理、文件管理。

中断分类

外中断处理过程

操作系统体系结构

操作系统是层次且模块化的。

分类:无结构、模块化结构、分层式结构、微内核结构(小内核、C/S模式、机制与策略分离、面向对象技术)。

原语:由若干条指令组成的程序段,用来实现某个特定功能,在执行过程中不可被中断,由关中断开中断来实现。

用户
应用程序
操作系统 非内核功能
大内核 资源管理(进程管理、存储器管理、设备管理等)
微内核 时钟 中断 原语(设备驱动、CPU切换)
裸机等
内核类型 优点 缺点
大内核 高性能 内核代码庞大,结构混乱,难以维护
微内核 结构清晰,方便维护 性能低

进程与线程

程序(Program):是指令的有序集合(静态概念)。

进程(Process):进程实体(PCB)的运行过程(动态概念),是系统进行资源分配和调度的独立单位。一个进程可以执行多个程序。

作业(Job):起源于批处理系统,用户在一次计算过程中或者事务处理过程中,要求计算机所作工作的集合。一个作业可由多个进程组成。

顺序执行的程序具有:顺序性、封闭性可再现性

并发执行的程序具有:间断性、无封闭性、不可再现性。

进程的特征

进程

进程实体进程实体(进程映像)={程序段(程序代码),数据段(程序运行时数据),PCB}\textnormal{\footnotesize 进程实体(进程映像)} = \{\textnormal{\footnotesize 程序段(程序代码)}, \textnormal{\footnotesize 数据段(程序运行时数据)}, PCB\}

PCB(Process Control Block):常驻内存,PCB={进程标识符,处理机状态信息,进程调度信息,进程控制信息}PCB = \{\textnormal{\footnotesize 进程标识符}, \textnormal{\footnotesize 处理机状态信息}, \textnormal{\footnotesize 进程调度信息}, \textnormal{\footnotesize 进程控制信息}\}创建进程就是创建进程实体中的PCB。撤销进程就是撤销进程实体中的PCB。

进程的状态

flowchart TD style create fill:LightGray,stroke:Blue; style stop fill:LightGray,stroke:Blue; style running fill:SpringGreen,stroke:Blue; style block fill:OrangeRed,stroke:Blue; style ready fill:Yellow,stroke:Blue; create([创建态]); ready([就绪态]); running([运行态]); stop([终止态]); block([阻塞态]); create--创建-->ready<==切换==>running--终止-->stop; running--阻塞-->block--唤醒-->ready;

进程的组织

flowchart subgraph 链接方式 pA1([执行指针])-->P1[PCB-1]; pA2([就绪队列指针])-->P2[PCB-2]-->P3[PCB-3]; pA3([阻塞队列指针])-->P5[PCB-4]-->P6[PCB-5]; end subgraph 索引方式 pB1([执行指针])-->P0[PCB]; pB2([就绪队列指针])-->table1[[就绪索引表]]; pB3([阻塞队列指针])-->table2[[阻塞索引表]]; end

进程通信

线程

没有引入线程时,传统的进程是程序执行流的最小单位。引入线程后,进程是资源分配的基本单位线程是调度的基本单位。同一进程的线程间并发不需要切换进程的运行环境,所以开销更小。

线程特点:轻型实体、独立调度和分配基本单位、可并发、共享进程资源。

线程的实现方式

用户级线程(协程) 内核级线程
维护者 应用程序 操作系统内核
线程切换态 用户态 核心态
操作系统可见性 不可见 可见
等效实际调度数
管理开销 较大
用户线程与内核级线程 优点 缺点
多对一 线程管理开销小 并发不高
一对一 并发高 线程管理成本高
多对多

进程互斥与同步

graph LR style entry fill:LightGray,stroke:Blue; style critical fill:OrangeRed,stroke:Blue; style exit fill:LightGray,stroke:Blue; style remainder fill:LightGray,stroke:Blue; entry([进入区]) -->critical([临界区]) -->exit([退出区]) -->remainder([剩余区])

进程互斥:对临界资源的访问,需要互斥进行。即当一个进程进入临界区使用临界资源时,另一个进程必须等待。

访问临界资源原则:空闲让进、忙则等待、有限等待、让权等待

进程同步:保证各个进程按预期的方式推进。

互斥软件实现方法

单标致法、双标志先检查法、双标志后检查法、Peterson算法

/**
 * 【单标致法】
 * 说明:每个进程进入临界区的权限只能被另一个进程赋予。
 * 问题:不遵循空闲让进。
 */
int turn = 0;

// P0
while(0 != turn) {  // 进入区
    ...;            // 临界区
    turn = 1;       // 退出区
    ...;            // 剩余区
}

// P1
while(1 != turn) {  // 进入区
    ...;            // 临界区
    turn = 0;       // 退出区
    ...;            // 剩余区
}
/**
 * 【双标志先检查法】
 * 说明:先检查后上锁,标记每个进程想进入临界区的意愿。
 * 问题:不遵循忙则等待,若按①⑥②⑦③⑧顺序,则两者同时访问临界区。
 */
bool flag[2];
flag[0] = false;
flag[1] = false;

// P0
while(flag[1]) {        // ①进入区,其它进程是否想进入
    flag[0] = true;     // ②进入区,表明自己意愿
    ...;                // ③临界区
    flag[0] = false;    // ④退出区
    ...;                // ⑤剩余区
}

// P1
while(flag[0]) {        // ⑥进入区,其它进程是否想进入
    flag[1] = true;     // ⑦进入区,表明自己意愿
    ...;                // ⑧临界区
    flag[1] = false;    // ⑨退出区
    ...;                // ⑩剩余区
}
/**
 * 【双标志后检查法】
 * 说明:先上锁后检查。
 * 问题:不遵循空闲让进和有限等待,若按①⑥②⑦顺序,则两者均无法访问临界区。
 */
bool flag[2];
flag[0] = false;
flag[1] = false;

// P0
flag[0] = true;         // ①进入区,表明自己意愿
while(flag[1]) {        // ②进入区,其它进程是否想进入
    ...;                // ③临界区
    flag[0] = false;    // ④退出区
    ...;                // ⑤剩余区
}

// P1
flag[1] = true;         // ⑥进入区,表明自己意愿
while(flag[0]) {        // ⑦进入区,其它进程是否想进入
    ...;                // ⑧临界区
    flag[1] = false;    // ⑨退出区
    ...;                // ⑩剩余区
}
/**
 * 【Peterson算法】
 * 说明:主动谦让。
 * 问题:不遵循让权等待。
 */
bool flag[2];
flag[0] = false;
flag[1] = false;
int turn = 0;

// P0
flag[0] = true;                 // ①进入区,表明自己意愿
turn = 1                        // ②进入区,主动避让
while(flag[1] && 1 == turn) {   // ③进入区,其它进程是否想进入且当前礼让中
    ...;                        // ④临界区
    flag[0] = false;            // ⑤退出区
    ...;                        // ⑥剩余区
}

// P1
flag[1] = true;                 // ⑦进入区,表明自己意愿
turn = 0                        // ⑧进入区,主动避让
while(flag[0] && 0 == turn) {   // ⑨进入区,其它进程是否想进入且当前礼让中
    ...;                        // ⑩临界区
    flag[1] = false;            // ⑪退出区
    ...;                        // ⑫剩余区
}

互斥硬件实现方法

实现方式 优点 缺点
中断屏蔽 开/关中断指令 简单高效 只适用于单机处理
TestAndSet 记录、上锁、检查 实现简单,多机适用 不遵循让权等待
Swap 记录、上锁、检查 实现简单,多机适用 不遵循让权等待

信号量机制

软件和硬件互斥方法存在问题:
1.检查和上锁操作无法一气呵成。
2.无法实现让权等待。

原语:由关中断/开中断实现,一旦开始执行,就不能被中断的若干条指令。

PV操作:一对原语,wait(S)signal(S)简称P(S)V(S),是一对低级进程通信原语。

整型信号量

int S = 1;          // 初始化整型信号量

void wait(int S) {
    while(S <= 0);  // 资源不够则等待资源
    S--;
}

void signal(int S) {
    S++;
}
// P0进程
wait(S);        // 申请资源S
...;            // 使用资源S
signal(S);      // 释放资源S

记录型信号量:为解决整型信号量忙等待问题,即不满足让权等待问题。

typedef struct {
    int value;          // 可用资源数量
    struct process *L;  // 等待进程队列
} semaphore;

void wait(semaphore S) {
    S.value--;
    if(S < 0) block(S.L);           // RUNNING --> BLOCKED
}

void signal(semaphore S) {
    S.value++;
    if(S.value <= 0) wakeup(S.L)    // BLOCKED --> READY
}
// P0进程
wait(S);        // 申请资源S
...;            // 使用资源S
signal(S);      // 释放资源S

例1:有pp个进程并发,每个进程需要rr1rR1≤r≤R)个资源,问至少有多少资源不会发生死锁。

每个进程差11个资源可以运行,再补充一台使至少有11台不会无限等待则有:

R=(r1)p+1R=(r-1)p + 1

例2:有可用资源RR,每个进程需要使用rr1rR1≤r≤R)个资源,问至多有几个线程pp不会发生死锁。

p=R1r1p=\dfrac{R-1}{r-1}

例3:有pp个程序共享同一程序段,每次最多允许RR个进程进入该程序短,问信号量SS取值范围

S[Rp,R]S∈[R-p,R]

管程

用于解决信号量机制编程复杂的问题,包含共享数据结构、数据结构初始化语句、一组用来访问数据结构的过程。

进程调度

调度

flowchart TD style create fill:LightGray,stroke:Blue; style stop fill:LightGray,stroke:Blue; style running fill:SpringGreen,stroke:Blue; style block fill:OrangeRed,stroke:Blue; style ready fill:Yellow,stroke:Blue; style readyHang fill:LightSkyBlue,stroke:Blue; style blockHang fill:LightSkyBlue,stroke:Blue; create([创建态]); ready([就绪态]); running([运行态]); stop([终止态]); block([阻塞态]); readyHang([就绪挂起]); blockHang([阻塞挂起]); create-->ready<===>running-->stop; running-->block-->ready; create-->readyHang<==挂起/激活==>ready; block<==挂起/激活==>blockHang--事件出现-->readyHang; running-->readyHang;

挂起:暂时被淘汰出内存的进程。

调度算法准则:面向用户准则(周转时间短、响应时间快、截止时间保证、优先权准则)、面向系统准则(吞吐量高、处理机利用率高、各资源的平衡利用)

进程调度时机:主动放弃(异常、IO操作)、被动放弃(时间片耗尽、高优先进程进入就绪队列)

进程调度方式:非抢占式调度算法(非抢占式轮换调度算法、非抢占式优先调度算法)、抢占式调度算法(基于时钟中断的抢占式优先权调度算法、立即抢占的优先级调度算法)

非剥夺调度方式(只允许主动放弃)、剥夺调度方式(抢占式)

调度算法性能指标

周转时间=作业完成时间作业提交时间\textnormal{\footnotesize 周转时间} = \textnormal{\footnotesize 作业完成时间} - \textnormal{\footnotesize 作业提交时间}

平均周转时间=各作业周转时间之和作业数\textnormal{\footnotesize 平均周转时间} = \dfrac{\textnormal{\footnotesize 各作业周转时间之和}}{\textnormal{\footnotesize 作业数}}

带权周转时间=作业周转时间作业实际运行时间\textnormal{\footnotesize 带权周转时间} = \dfrac{\textnormal{\footnotesize 作业周转时间}}{\textnormal{\footnotesize 作业实际运行时间}}

平均带权周转时间=各作业带权周转时间之和作业数\textnormal{\footnotesize 平均带权周转时间} = \dfrac{\textnormal{\footnotesize 各作业带权周转时间之和}}{\textnormal{\footnotesize 作业数}}

等待时间=作业处于等待处理机状态时间之和\textnormal{\footnotesize 等待时间} = \textnormal{\footnotesize 作业处于等待处理机状态时间之和}

响应时间=从用户提交请求到首次产生响应所用的时间\textnormal{\footnotesize 响应时间} = \textnormal{\footnotesize 从用户提交请求到首次产生响应所用的时间}

调度层次

调度层次 方向 发生频率 状态变化
高级调度/作业调度 外存→内存 最低 无→创建态→就绪态 将外存后备队列作业加入内存
中级调度/内存调度 外存→内存 中等 挂起态→就绪态 将挂起进程数据调回内存
低级调度/进程调度 内存→CPU 最高 就绪态→运行态 为就绪队列分配处理机

作业调度工作:检查资源要求、分配内存调入作业、进程载入就绪队列。

调度算法

算法 是否抢占 可能饥饿 特点
FCFS(先来先服务) 不利于短作业
SJF(短作业优先) 均可 对长作业不利,平均等待时间最小
高优先权优先 均可 静态优先权
HRRN(高响应比优先) 动态优先权,兼顾长/短作业
时间片轮换 频繁进程切换的开销大
多级反馈队列 目前效果最好

死锁

产生原因:竞争资源、请求和释放资源的顺序不当

产生必要条件:互斥访问、请求和保持(等待)、不可抢占、环路等待

死锁的处理

内存

硬件结构:寄存器、高速缓存、内存

内存保护:上下限寄存器、限长寄存器(届地址寄存器、重定位寄存器)

覆盖技术:按照逻辑,让不能同时被访问的程序段共享同一个覆盖区。

交换技术:内存紧张时,将内存中某些进程暂时换出到外存交换区(PCB则常驻内存)。

内存碎片:不能被使用的内存区域。

程序装入方式

装入方式 可装入内存中不同位置 可在内存中移动 说明
绝对装入 × × 程序中的逻辑地址即为物理地址
可重定位装入 × 装入时将逻辑地址转换为物理地址
动态运行时装入 运行时将逻辑地址转换为物理地址
需设置重定位寄存器

连续内存分配

实现方法 有外部碎片 有内部碎片
单一连续分配 直接分为系统区用户区 ×
固定分区分配 用户区分为固定大小的多个子分区 ×
动态分区分配 装入时基于程序大小动态创建 ×
动态可重定位分配 空间不够时整理分散的小分区为一个大分区 ×

动态分区分配算法:需要能描述空闲分区已分配分区的数据结构,常用数据结构有空闲分区表空闲链表。若分区回收后与前后分区均为空闲分区,则应将其合并存储。

动态分区分配算法 思想 空闲分区 开销 大作业 特点
首次适应 从头到尾找最适 地址递增 有利 优先使用低地址
邻近/循环首次 从上次结束开始 地址递增 不利 高低地址使用均匀
最佳适应 优先使用小分区 容量递增 有利 许多难以利用小空间
最坏适应 优先使用大分区 容量递减 不利 产生碎片概率低
快速适应 分区按容量分类 分类存放 有利 多个空闲分区链表

非连续内存分配

分页存储管理

基本思想:非连续分配,把进程分页,各个页面离散的放到各个内存块中。

物理层面:将内存空间分为多个大小相等(太大可能产生过大的内部碎片)的分区,每个分区就是一个页框(页帧、内存块、物理块),每个页框有一个编号即页框号(页帧号、内存块号、物理块号),页框号从0开始。

逻辑层面:用户进程的地址空间也分为与页框大小相等的一个个区域称为页(页面),每个页有一个页号,页号从0开始。

页表:存储了逻辑页物理块的对应关系(页号, 块号)。故CPU每次读取数据都需要22次访存,第一次访问内存中的页表,第二次通过查询页表获得的物理地址访问内存获得所需数据。

页表长度页表有几个页表项(多少行)。

页表项长度页表项占存储空间大小(一行大小)。

页面长度页面占存储空间大小。

快表:当页表项缓存于寄存器时,即为快表。此时每次读取数据仅需11次访存。

多级页表:各级页表大小不能超过一个页面大小,使用两级页表((一级页号, 二级页号, 页内偏移量)(页号, 块号))需要33次访存,NN级页表需要N+1N+1次访存。

地址转换

:页面长度=1KB = 0x400,页表如下,问逻辑地址0A1F(H)对应的物理地址

页号 块号
0 1
1 5
2 3
3 7
4 2

分段存储管理

分段 分页
信息单位 信息的物理单位 信息的逻辑单位
目的 提高内存利用率 更好地满足用户需要
用户可见性 不可见 可见
大小 由系统决定的固定大小 取决于用户编译时信息的性质
维度 一维(逻辑地址) 二维(段名,段内地址)

段表:存储了逻辑段物理地址的对应关系(段号(隐含), 段长, 基址)

段页式存储管理

先将程序分为若干段,再将每段分为若干页。逻辑地址结构为用户提供(段号, 段内地址),实际存储(段号, 页号, 页内偏移量)。段页式段表为(段号(隐含), 页表长度, 页表存放地址)与分段存储管理中的段表不同。段页式访问一个逻辑地址需要访存33次(段表->页表->物理地址),若快表命中则仅需11次访存。

虚拟内存

从逻辑上扩充内存容量,程序不需要全部装入内存即可允许,运行时根据需要动态调入数据,内存不够时换出一部分数据。

虚拟内存的最大容量:由系统的地址结构和外存空间大小决定。

虚拟内存组成:主存、辅存、管理单元、管理软件。

调入策略:预调页策略、请求调页策略

局部性原理

虚拟内存特征

实现技术

页面置换算法

优先淘汰 性能 特点
最佳置换算法(OPT) 最长时间不被访问的页面 最好 缺页率最小,但无法实现
先进先出(FIFO) 最先进入内存的页面 很差 实现简单,可能出现Belady异常
最近最久未使用(LRU) 最近最久没有访问的页面 算法开销大,需硬件支持
CLOCK/NRU 第一个访问位为0的 实现简单,开销小
最少使用(LFU)
页面缓冲(PBA)

CLOCK/NRU算法:采用循环队列构建页面队列,记录(访问位),访问后若为1则置为0,最多2轮即可找到要淘汰的页。

改进的CLOCK/NRU算法:增加了置换代价因素,记录(访问位, 修改位),最多进行4轮扫描即可找到要淘汰的页。

页面分配策略

驻留集:请求分页存储管理中给进程分配的物理块的集合。

工作集:某段时间间隔内,进程实际访问的页面的集合。

固定分配/可变分配:运行期间驻留集大小是否可变。

局部替换/全局替换:缺页时,是否只能换出进程自己的页面。

抖动:刚换入的页面马上又要换出,主要原因是进程频繁访问的页面数目高于可用物理块数,即分配给进程的物理块不够。

设备

设备IO方式:轮询、中断、直接访存、通道控制

作业IO方式:联机、脱机、假脱机

缓冲区管理

层次结构

设备分类

按设备使用分类:存储设备、输入输出设备

按设备速率分类:低速、中速、高速

按设备交换单位分类:块设备(传输快,可寻址)、字符设备(传输慢,不可寻址,常采用中断驱动方式)

按设备共享属性分类:独占设备、共享设备、虚拟设备

设备控制器

功能:接受和识别CPU发出的命令、向CPU报告设备状态、数据交换、地址识别

组成:CPU与控制器接口、IO逻辑、控制器与设备之间的接口

寄存器编址方式:内存映射I/O、寄存器独立编址

控制方式

过程 CPU干预频率 数据传输单位 数据流向
程序直接控制方式 CPU发出I/O命令后需要不断轮询 极高 设备→CPU→内存;
内存→CPU→设备
中断驱动方式 CPU发出I/O命令后可以做其他事,
本次I/O完成后设备控制器发出中断信号
设备→CPU→内存;
内存→CPU→设备
DMA方式 CPU发出命令后可以做其他事,
本次I/O完成后DMA控制器发出中断信号
设备→内存;
内存→设备
通道控制方式 CPU发出I/O命令后可以做其他事,
通道会执行通道程序以完成I/O,
完成后通道向CPU发出中断信号
一组块 设备→内存;
内存→设备

设备分配

相关数据结构:设备控制表(DCT)、控制器控制表(COCT)、通道控制表(CHCT)、系统涉笔表(SDT)。

分配时考虑因素:设备固有属性(独占性、共享性、可虚拟性)、设备分配算法、分配中安全性。

设备分配步骤:用户编程时使用逻辑设备名,OS通过物理设备名映射表(LUT)实现从逻辑设备名到物理设备名的映射。

SPOOLing:提高IO速度、独占设备改造为共享设备、实现了虚拟设备功能。是一种以空间换时间的技术。

SPOOLing

磁盘

磁盘

磁盘读写耗时

磁盘调度算法

算法 说明
先来先服务(FCFS)
最短寻找时间优先(SSTF) 仅能保证眼前最优,无法保证总体最优,且可能导致饥饿
扫描算法(SCAN) 磁头移动到最边缘的磁道时才可改变磁头移动方向
LOOK 磁头移动方向上不再有请求,就立即改变磁头移动方向
循环扫描(C-SCAN) 磁头朝某方向移动时才会响应请求,移动到边缘后立即让磁头折回起点
C-LOOK 只有在磁头移动方向上不再有请求,就立即让磁头返回

数据交付方式

减少延迟方法

磁盘初始化

磁盘分区

磁盘容错

磁盘冗余阵列

raid015

RAID等级 速度 容错 可用容量 磁盘数 说明
RAID-0 ↑↑ × 100%100\% n1n≥1 双区域并行交叉存取
RAID-1 - 50%50\% n=2N+n=2N^+ 提供磁盘镜像功能
RAID-5 n1n\dfrac{n-1}{n} n3n≥3 分布奇偶校验条带

磁盘容错技术

文件

文件系统:由文件管理有关软件、被管理文件、实施管理所需的数据结构等组成。

文件数据

文件共享方式

文件保护方式

文件系统层次结构

文件存储空间管理

主要解决如何为新文件分配存储空间的问题。

文件存储空间划分:逻辑卷/文件卷、目录区(文件目录、空闲表、位示图、超级块)、文件区

filevol

管理方法 特点
空闲表法 连续分配方式
空闲盘块链 以盘块为单位组成一条空闲链
空闲盘区链 以盘区为单位组成一条空闲链
位示图法 每个盘块对应一个二进制位
成组链表法 空闲盘块分组成链,UNIX采用,适合大型文件系统

文件逻辑结构

有结构文件由记录组成,又称记录式文件,记录分为定长记录可变长记录

文件目录结构

文件目录:一个文件对应一个FCB、一个FCB就是一个目录项,多个FCB组成文件目录。

索引结点:除文件名之外所有信息都放到索引节点中,每个文件对应一个索引结点。

目录查询方法:线性检索、Hash方法

目录结构

文件物理结构

包括:顺序结构、链式结构、索引结构。

实现 目录 优点 缺点
连续分配 文件在磁盘占有连续的块 起始块号,文件长度 支持随机访问,
顺序存取速度快
会产生碎片,不方便扩展
隐式链接 块中包含指向下一块的指针 起始块号,结束始块号 方便扩展,
不会有碎片,外存利用率高
只支持顺序访问
显式链接 文件分配表FAT记录了
物理块指针并常驻内存
起始块号 支持随机访问,
方便扩展,
不会有碎片,外存利用率高
FAT会占用内存空间
链接索引 索引表链接存放 第一个索引块块号 支持随机访问,
方便扩展
大文件索引表很长
多层索引 一级索引表指向二级索引表… 顶层索引块块号 支持随机访问,
方便扩展
小文件也要多次访问磁盘找到下级索引表
混合索引 索引表既存放物理块指针
也存放下一级索引表
顶层索引块块号 支持随机访问,
方便扩展,
小文件访磁盘次数更少