操作系统笔记 清华大学陈渝
程序员文章站
2022-06-22 09:54:31
...
课程概要
- 基本概念及原理
- 操作系统介绍
- 中断及系统调用
- 内存管理
- 进程及线程
- 调度
- 同步
- 文件系统
- I/O子系统
1. 基本概念
- 操作系统是控制软件,管理应用程序,为应用程序提供服务,杀死应用程序,分配资源,管理外设
- 抽象:CPU - 进程,内存 - 地址空间,磁盘 - 文件
- OS分为Shell(界面)和Kernel(内核)
- 并发(concurrent,交替运行) vs 并行(parallel,同时运行)
- 实例:UNIX BSD,LINUX,WIN
2. 启动、系统调用、异常、中断
- 启动过程:BIOS检测外设,加载BootLoader,再由BootLoader加载OS
- 系统调用:应用程序向OS发出请求
- 异常:应用程序发生异常,需要OS处理
- 中断:外设发出请求
- 系统调用之上有API供应用程序调用:Win32 API,POSIX API(UNIX LINUX Mac OS)
- CPU不同状态:用户态 内核态
3. 内存管理
- CPU:运算器、控制器、Register、Cache、MMU
- 内存管理能够实现:物理存储抽象为逻辑地址空间,保护各程序各自内存空间,程序间共享内存,虚拟化 - 暂时不用的数据移动到磁盘
- 程序经过编译、汇编、链接、载入对应到逻辑地址
- 连续内存分配、内存碎片(外碎片 - 程序之间、内碎片 - 程序之内)
- 首次适配、最优适配、最差适配
- 内存碎片整理:压缩式(移动空闲内存)、交换式(暂时不用的数据移动到磁盘)
4. 非连续内存分配
- 解决碎片问题,提高内存利用率
- 分段segment:内存块大小可变
- 内存地址表示为(segment number, offset)
- 段表:段号 - 基址,长度
- 分页page:内存块大小固定(帧)
- 物理地址:(frame number, offset)
- 逻辑地址:(page number, offset)
- 页表:page - frame
- 解决页表过大:
- TLB(Translation Lookaside Buffer)用于缓存常用地址
- 多级页表
- 反向页表:frame - page
- 反向页表的实现:页寄存器、关联内存、hash table
5. 虚拟内存
- 缓解内存不足
- 覆盖技术(overlay),常用程序模块独占常驻内存,不常用程序模块分时共享内存,但需要程序员设计覆盖关系,增加了程序设计复杂度,并且有从硬盘读写模块的开销,粒度是程序模块
- 交换技术(swapping),由OS执行换入换出,以程序为单位,内存可能不足时进行内存和硬盘间数据交换,粒度是程序
- 虚拟内存,由OS执行,程序设计需要有局部性(指令和数据访问在时间和空间上较为集中),低粒度,以页为单位,按需从硬盘调入程序和数据
- 有效存储访问时间(effective memory access time, EAT)
- 数据结构,通过页表实现:
- 驻留位(resident bit, 1表示在内存中,0表示在硬盘中)
- 保护位(设置权限,包括只读、读写、可执行等)
- 修改位(dirty bit, 数据是否被修改过,换出时是否需要写回硬盘)
- 访问位(access/used bit, 是否被访问,长时间未访问的数据在内存不足时优先被换出)
- 锁定位(lock bit, 标记需要常驻内存的数据,如OS进程和对时间敏感的进程) - 算法,在页表中查找所需数据的物理地址,如存在则直接读取;如不存在,先判断是否有空余内存,如无,先换出内存上的数据(未被修改直接free,被修改过则写回硬盘),再从硬盘读取数据
6. 页面置换算法
- 物理内存不足时,需要换出内存中的数据
- 置换算法选择将哪些数据换出,目标是尽可能减少换入换出
- 最优页面置换算法,换出未来等待最长时间才会被访问的数据,是一种理想情况,作为其他评价其他算法的标杆
- 先进先出算法(FIFO),换出在内存中存在时间最长的数据,但存在时间久的数据有可能会被频繁访问,导致缺页较多,造成Belady现象
- 最近最久未使用算法(Least Recently Used, LRU),换出距离上一次被访问间隔时间最久的数据,缺页较少,但需要记录每个数据的访问时间,开销较大
- 时钟算法(Clock),在环形链表中记录frame的access bit,数据被访问时bit置为1,所有bit定期归零。发生缺页时,在环形链表中寻找access bit为0的frame,若为1则置为0,若为0则换出。相当于近似的LRU,缺页次数稍大,但节省空间
- 二次机会算法,改进clock,(access bit, dirty bit) :
- (1, 1) -> (0, 1) -> (0, 0)
- (1, 0) -> (0, 0)
- 换出(0, 0),被修改过的frame换出开销大,指针经过两次才变为(0, 0)
- 最不常用算法(Least Frequently Used, LFU),将访问次数最少的数据换出
- Belady现象,分配更多物理内存,缺页反而更多的现象,如FIFO
- 全局页面置换算法
- 实现全局动态分配各个程序的内存,保持平衡,效果好于局部算法
- 工作集模型,W(t, Δ),t时刻往前Δ时间段(窗口)内,page的集合,集合size越小,程序局部性越好
- 工作集页置换算法,固定窗口大小,随着时间窗口移动,换出窗口外的页,换出最久未访问的页,限制每个程序的可用内存
- 缺页率页置换算法(Page Fault Frequency, PFF),用缺页发生的时间间隔评价缺页率,间隔短说明内存不足,加载页,间隔长说明内存过多,换出未访问的页
- 抖动(thrashing),内存严重不足时,大量时间耗费在换入换出操作上,程序运行效率大大降低的现象
7. 进程
- 进程(Process)
- 定义:程序的一次执行过程
- 为了支持运行多个程序、多个实例而生
- 记录程序的所有状态信息,包含代码、数据、程序计数器和寄存器中存储的值、其他系统资源(文件、网络)等
- 描述进程的数据结构:进程控制块(Process Control Block, PCB),与进程一 一对应
- PCB保存:
- 进程标识(进程ID、父进程ID、用户ID)
- 状态信息(寄存器,主要有程序计数器PC、栈指针SP)
- 控制信息(调度、通信、存储、IO、树形结构)
- PCB实现方式:链表、索引表
- 生命周期管理:
- 创建(源自系统、用户、进程)
- 运行(OS调度)
- 等待(阻塞,等待数据就绪、其他进程完成)
- 唤醒(由OS或其他进程完成,回到就绪态)
- 结束
- 进程(线程)状态:new、ready、running、blocked、exit
- OS对各状态分别维护队列,并按优先级区分多个队列
- 各就绪态进程分时占用CPU,OS管理时钟
- 进程挂起(suspend):将内存中的数据换出到磁盘中;就绪或阻塞时可挂起,优先挂起阻塞进程;相反的过程:进程解挂/**(activate)
- 线程(Thread)
- 进程中的更小的运行单位
- 进程分配资源,实现代码、数据、文件的共享;线程将执行过程独立出来,是CPU调度单位,各线程有各自的寄存器、堆栈
- 描述线程的数据结构:线程控制块(Thread Control Block,TCB)
- 线程机制提高并发性能,但数据共享导致线程间容易发生互相干扰,安全性差。需要性能时使用线程(如科学计算),需要安全时使用进程(如浏览器)
- 线程实现方式:
- 用户线程,由用户线程库管理调度,自定义调度算法,OS只管理进程层面;
- 内核线程,由OS管理,粒度小,但产生用户态到内核态切换的开销,如Windows;轻量级进程,每个进程拥有多个轻量级进程,各自对应一个内核线程,如Solaris/Linux
- 上下文切换:停止当前进程/线程,开始其他进程/线程时,需要在PCB/TCB中保存当前线程的信息,读取下一个线程的信息,这些信息称为上下文
- 上下文具体包括:寄存器信息(程序计数器PC:程序执行阶段,栈指针SP:调用关系和局部变量位置),CPU状态
- 进程控制
- 创建、加载和执行进程: Windows - CreateProcess(); Linux - fork()复制进程,exec()加载新程序,取代当前进程。优化方式:vfork() 轻量级fork(),Copy On Write只复制元数据,其他数据只在写操作时进行复制
- 等待和终止进程:父进程的wait()和子进程的exit()配合完成子进程资源的回收;子进程调用exit()后,资源完成回收前的状态称为Zombie态;root进程也会定期进行资源回收
8. CPU调度
- 调度(Schedule),进程、线程切换
- 抢占,线程运行完成前被打断,OS发展:不可抢占 -> 只支持用户态抢占 -> 用户态和内核态都可抢占
- CPU忙与闲(I/O操作)
- 调度原则:选择哪一个线程来执行?
- 评价指标:CPU忙时百分比、吞吐量、完成时间(从初始化到结束的时间)、等待时间(处于就绪态的总时间)、响应时间(从发起请求到第一次响应的时间)
- 目标:低响应时间、高吞吐量,两者很难兼顾
- 公平,每个进程占用同等的CPU时间,但会牺牲效率
- 调度算法:
- 基本调度算法
- FCFS,First Come First Served,先来先服务,简单,等待时间波动大
- SPN,Shortest Process Next,短进程优先 == SJF,Shortest Job First,短作业优先,不可抢占
- SRT,Shortest Remaining Time,短剩余时间优先,可抢占;增加长进程等待时间(饥饿);需要根据历史执行时间估算未来执行时间
- HRRN,Highest Response Ratio Next,最高响应比优先,R=(waiting time+service time)/service time,R最高者优先;防止无限期推迟,也需要预估执行时间
- Round Robin,轮循,按时间片轮流执行,提高公平,增加了上下文切换开销,时间片大小很重要,过大则变成FCFS,过小则切换开销太大,一般根据经验决定,使得上下文切换开销<1%
- Multilevel Feedback Queues,多级反馈队列, 将进程放置在多个队列中,高优先级队列时间片小,低优先级队列时间片大,等待时间长的任务提高优先级(I/O密集型,即前台交互任务,保证公平性),服务时间长的任务降低优先级(CPU密集型,即后台运算任务,提高效率)
- Fair Share Scheduling,公平共享调度,用户层面的公平,适用于多用户共享服务器的场景
- 实时调度算法(嵌入式)
- 实时系统:强调deadline,平均性能相对不重要
- 强实时系统:重要任务在规定时间必须完成
- 弱实时系统:重要任务优先级更高,尽量完成
- RM,Rate Monotonic,速率单调调度,静态优先级,周期越短优先级越高
- EDF,Earliest Deadline First,最早期限调度,动态优先级,deadline越早优先级越高
- 多处理器调度算法(多核)
- 追求负载平衡(load balance)
- 优先级反转
- 低优先级任务占用了某共享资源,高优先级任务不能及时执行
- 解决优先级反转
- 优先级继承,与高优先级任务共享资源的低优先级任务,优先级被提升
- 优先级天花板,将资源的优先级设置为可以锁定该资源的任务的最高优先级,任务的优先级取决于所占用资源的优先级
- 低优先级任务占用了某共享资源,高优先级任务不能及时执行
- 基本调度算法
9. 同步
- 解决进程间的交互产生的各种问题
- 为什么需要合作:共享资源、并行提高效率、模块化
- 如果没有同步互斥机制,则一系列指令执行中被打断,会导致结果不符合预期
- 需要保证任何交替执行方式都能得到正确结果,反之,则形成Race Condition 竞态条件:结果依赖于执行顺序
- 一些概念:
- Atomic Operation 原子操作,不可被打断的操作,要么完整执行,要么不执行,不会发生部分执行
- Critical Section 临界区,访问共享资源的代码区域
- Mutual Exclusion 互斥,保证只有一个进程处于临界区,不允许多个进程访问同一共享资源
- Dead Lock 死锁,进程之间互相等待,无法执行的情况
- Starvation 饥饿,一个进程被调度器忽略,无限期等待,无法执行
- Lock 锁,获得锁,获得控制权,释放锁,失去控制权
- Busy-waiting 忙等待,进程在等待进入临界区时,循环执行无意义操作,浪费系统资源
- Progress,希望进入临界区的进程总是能够进入临界区
- 互斥实现方式
- 硬件方式,禁用中断,进入临界区后禁用中断,离开后开启中断,临界区代码无法被停止,适用于临界区较小的情况,只适用于单处理器
- 软件方式,开销较大
- 两个进程互斥,Peterson算法,解决进程 i 和进程 j 之间的互斥
- 进入临界区
flag[i] = true; turn = j; while (flag[j] && turn == j);
- 1
- 2
- 3
- 离开临界区
flag[i] = false;
- 1
- 进入临界区
- 多个进程互斥,Eisenberg and McGuire算法(循环),Bakery算法(排队取号,同号比ID)
- 需要共享数据(flag、turn),存在忙等待,需要硬件支持(原子性LOAD、STORE)
- 两个进程互斥,Peterson算法,解决进程 i 和进程 j 之间的互斥
- 更高级的抽象,主流方式
- 硬件提供原子操作指令,通过特殊的内存访问电路实现
- Test-and-Set指令,从内存读取值,返回该值,并将内存值设为1
- Exchange指令,交换内存中的两个值
- 锁的实现
class Lock { int lock = 0; } Lock::Acquire() { // test-and-set while (test-and-set(lock)); // exchange key = 1; while (key == 1) exchange(lock, key); } Lock::Release() { lock = 0; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 优点:适用于多处理器,多进程,多临界区,简单
- 需要考虑:忙等待,随机进入临界区导致饥饿,优先级反转
- 硬件提供原子操作指令,通过特殊的内存访问电路实现
10. 信号量
- 信号量和管程是比锁更高级的抽象,也是同步互斥的解决方式
- 锁只解决了互斥,为了让多个线程进入临界区(比如只在临界区进行读操作,不必要求互斥),需要解决同步
-
信号量 Semaphore,Dijkstra提出
- 用一个整形(sem)表示,提供两个原子操作,P()和V()
- P(),表示有线程需要进入临界区,
sem--; if (sem < 0) wait; else progress;
- V(),表示有线程离开临界区,
sem++; if (sem <= 0) wakeup a thread in waiting list
// sem <= 0意味着有线程在等待进入临界区,唤醒常用FIFO方式 - sem初值设为1,就相当于锁了;初值设为更大的值,可以实现条件同步
- 调度约束,sem初值设为0,Thread A在某处P(),Thread B在某处V(),确保Thread A在P()之后的语句一定在Thread B的V()执行之后才会开始执行
- Producer - Buffer - Consumer:buffer用于存放产品,buffer大小为n;生产者向buffer中放入产品,消费者从buffer中取走产品;
mutex
控制同一时间只有一个生产者或消费者可以对buffer操作,fullBuffers
和emptyBuffers
实现buffer满时阻塞生产者、空时阻塞消费者
- 信号量的实现,
P()
和V()
的实现使用禁用中断、test-and-set等原子指令,需要等待时将线程放入等待队列使之挂起 - 信号量的一些问题:开发难度较大,忘记释放信号量,语句顺序错误导致死锁
-
管程 Monitor
- 比信号量抽象程度更高的机制
- 作为编程语言的特性而诞生,用来简化并发编程,而非为OS设计
- 包含了一系列变量(锁和条件变量)和方法的模块
- 条件变量
Condition
,numWaiting
表示等待队列中的线程数量,wait()
使线程等待,signal()
唤醒线程
- 条件变量
- 用管程实现生产者-消费者模型
-
count
表示buffer中产品的数量 - 条件变量
notFull
、notEmpty
表示是否为满或空的状态,notFull
可以理解为okToProduce
-
- Hansen vs. Hoare
- Hansen方式,
signal()
之后继续执行完release()
再切换线程,易于实现,实际OS采用此方式 - Hoare方式,
signal()
之后立刻切换线程,直观,但实现困难,教科书一般按此方式
- Hansen方式,
- 一些经典同步问题:
-
读者 - 写者问题(读者优先,信号量实现):
- 读者读取数据,写者修改数据
- 同一时间允许多个读者,或者一个写者
- 读者优先:同时有读者和写者需要操作时,读者优先操作
-
Rcount
初值为0,表示读者个数 - 信号量
CountMutex
初值为1,用于对count
修改的互斥 - 信号量
WriteMutex
初值为1,用于读者和写者、写者之间的互斥 -
sem_wait()
为P()
,sem_post()
为V()
-
读者 - 写者问题(写者优先,管程实现):
- 写者优先:只有在没有写者等待时,读者才能操作
-
signal()
只唤醒一个线程 -
broadcast()
唤醒所有线程
-
读者 - 写者问题(读者优先,信号量实现):
-
哲学家就餐问题
- 圆桌,5个哲学家,5个叉子,每个叉子放在两人之间,哲学家吃饭需要拿起自己左右两个叉子,吃完后放下叉子
- 基于信号量的解决方式:
- 对每个哲学家设置状态
state[5]
,THINKING、HUNGRY or EATING - 信号量
mutex
,确保对state
的访问是互斥的,初值为1 - 信号量
s[5]
,吃完饭后唤醒邻居,进行同步,初值为0 - 实现如下:
- 对每个哲学家设置状态
void philosopher(int i) { // i为哲学家编号,0-4
while (true) {
think();
take_forks(i);
eat();
put_forks(i);
}
}
void take_forks(int i) {
P(mutex); // 进入临界区
state[i] = HUNGRY; // 自身状态设置为饥饿
test_take_left_right_forks(i); // 试图拿叉子
V(mutex); // 退出临界区
P(s[i]); // 拿到叉子后会进行V(),这里就不会阻塞;如果没拿到叉子,就会阻塞
}
void test_take_left_right_forks(int i) {
if (state[i] == HUNGRY && // 自己饥饿,且邻居都没有在吃饭时,即有叉子可用,自己才可以吃饭
state[LEFT] != EATING &&
state[RIGHT] != EATING ) {
state[i] == EATING; // 拿到了叉子
V(s[i]); // 通知第i人可以吃饭
}
}
void put_forks(int i) {
state[i] = THINKING; // 表示放下叉子
test_take_left_right_forks(LEFT); // 看邻居能否吃饭
test_take_left_right_forks(RIGHT);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
11. 死锁
- 死锁一般是由于双方各占用了资源,又需要对方占用的资源,但都不释放资源
- 可以用一个系统模型来描述死锁问题
- 资源的特征
- 某一时刻只能有一个进程使用,且不可被删除
- 一个进程获取资源,然后释放资源,以供其他进程使用
- 资源既可以是物理资源(CPU、内存、I/O通道),也可以是虚拟资源(数据结构、文件、信号量)
-
资源分配图 Resource Allocation Graph,一组顶点V和边E的集合
- 顶点包括进程P和资源R
- 边,请求P -> R,分配R -> P
- 例子如下,黑点表示资源个数;形成了环路,存在死锁
- 形成环路,但没有死锁
- 死锁出现的必要条件
- 互斥:一个资源同时只能有一个进程使用
- 持有并等待:进程持有资源,并在等待其他进程持有的资源
- 无抢占:资源只能被进程使用完后主动释放
- 循环等待:存在一个环,每个进程在等待下一个进程持有的资源
- 死锁解决方法,约束力度逐渐减弱;实际OS中往往选择忽略死锁问题,因为解决开销过大,影响了性能
- 死锁预防 Deadlock Prevention,改变资源申请方式,确保不出现死锁,比如对资源类型排序,进程只能按顺序申请资源,防止出现循环等待;资源利用率低,容易饥饿
-
死锁避免 Deadlock Avoidance,在进程申请资源时,判断是否会出现死锁,如果是则拒绝分配资源;要求进程声明所需资源最大数量,如果超过该数量则拒绝;还通过算法检测申请资源后是否会形成环形等待
- 银行家算法 Banker’s Algorithm,类比客户申请贷款
- 数据结构:
- Max(总需求量):n x m矩阵,n为进程数量,m为资源种类数量,Max[i, j] = k表示进程Pi最多请求k个Rj资源
- Allocation(已分配量):n x m矩阵,表示进程已分配的资源数量
- Need(未来需要量):n x m矩阵,表示进程还需要的资源数量
- Need[i, j] = Max[i, j] - Allocation[i, j]
- Available(剩余空闲量):长度为m的向量,表示每种资源可用数量
- 算法
- 当某个进程发出对资源的请求时,假设将资源分配给它,并更新以上变量,然后进行下一步寻找安全序列
- 找出线程执行的序列:找到一个未结束的进程,并且其Need ≤ Available,然后将其资源全部回收,更新Available,并将该进程标记为已结束;继续找下一个进程,直至所有进程都能正常结束,说明能够找出安全序列,则允许请求;如果找不到安全序列,则拒绝资源请求
-
死锁检测 Deadlock Detection,允许死锁出现,通过检测算法发现死锁并恢复
- 等待图 wait-for graph,表示进程等待关系,判断是否存在环
- 定期执行类似银行家算法的死锁检测,检测到死锁后执行死锁恢复
- 开销很大,O(m x n2),“所需资源最大数量”这一信息难以获取,所以实际很少使用,多用于OS调试
-
死锁恢复 Recovery from Deadlock
- reboot,终止所有死锁进程,逐个终止死锁进程(先终止优先级低的、运行时间短的、占用资源多的进程)
-
进程间通信 Inter-Process Communication, IPC
- 概述
- 通信模型,建立通信链路,进行操作send(message),receive(message)
- 直接及间接通信:直接,通过共享区域,进程间直接收发消息;间接,通过内核kernel转发,进程与消息队列之间收发消息
- 阻塞与非阻塞:阻塞方式,同步,收发消息开始后让进程阻塞,直至通信完成;非阻塞方式,异步
- 通信链路缓冲:收发速度不一致时提高效率,有限容量缓冲
- 信号 Signal,短小的bit,不能用于传递数据;不同的信号对应有handler,kernel发送消息给进程,并跳转到handler的stack
- 管道 Pipe,用于传递字节流,使得多个程序组合起来实现更负责的功能;shell对其子进程的输入输出重定向,串联成管道,kernel中的一个buffer
- 消息队列 Message queue,无继承关系的进程间,结构化的数据结构
- 共享内存 Shared memory,两个进程的共享区域,直接通信,快速,需要设置同步互斥;通过将同一块物理内存分别映射到两个进程的page中
- Socket机制,详见网络原理课程
- 概述
12. 文件系统
- 文件系统:持久性存储的系统抽象
- 需要解决数据的组织、控制、导航、访问和检索
- 文件:一个单元的相关数据的在文件系统中的抽象
- 文件系统提供的功能:
- OS视角:管理文件块(一块数据归属于哪一个文件)、管理空闲空间、分配算法
- 用户视角:如何定位一个文件、文件名关联到文件、分层目录
- 保护,文件的“互斥”
- 文件元数据信息(名称、大小、修改时间等等)保存在文件头中
- 文件描述符,open(filename)返回的一个int值,表示已打开文件表的一个index,对应到一个文件
- 打开的文件需要的元数据:文件指针表示读写位置,打开计数表示打开一个文件的进程数量,为0时从文件表中移除文件,文件磁盘位置,访问权限
- 块(逻辑单元,比如4KB大小)< - > 扇区(物理单元),读写是以一个扇区为单位的,即使只读写一个字节
- 顺序访问、随机访问、基于内容访问(数据库)
- OS不关心复杂的文件结构,只看做字节流
- 对不同用户(owner、同组用户、所有用户)设置访问权限,读、写、执行
- 对文件共享(多个程序都可以访问同一个文件)的支持,互斥,不同粒度的锁
- 目录是一张表<filename, pointer to file header> ,构成目录和文件的树形结构,hash表实现
- 通过路径逐级解析找到文件,通过缓存当前工作目录来加快这一过程
- 挂载,跨越不同的文件系统进行访问,一个目录对应一个文件系统,UNIX
- 文件别名,多个文件名指向同一个文件,硬链接(多个文件项,删除时减少计数,为0时才真正删除),软链接(快捷方式)
- 避免形成路径的循环(你中有我,我中有你)
- 文件系统类别,for Windows - FAT、NTFS,for UNIX - ext2/3
- 日志文件系统,用于断电后快速恢复,避免读写操作意外中断
- 分布式文件系统,基于网络,延迟 - Google,高吞吐、高容错的访问文件
- 虚拟文件系统,用文件的方式管理内核,UNIX - proc
- 虚拟文件系统层,屏蔽掉底层文件系统,为应用提供统一API
- 卷控制块 superblock,每个文件系统只有一个,记录文件系统信息,块、块大小、空余块
- 文件控制块 vnode,每个文件一个,文件的元数据
- 目录 dentry,每个目录一个,树形数据结构
- 常用数据会缓存到内存中,预先读取,延迟写入,以页为粒度缓存,减少对硬盘的读写次数
- 打开文件的数据结构,将被打开文件的文件控制块读入内存,放入表中,偏移量offset表示需要读写文件的何处,查找到文件对应的磁盘位置后将数据返回给程序;对文件的锁,强制或建议
- 对存储空间的分配,时间空间效率,类似于内存分配,页表
- 连续存储,类比数组,高效的顺序和随机访问,碎片、增长不方便,适合只读
- 链式存储,类比链表,容易增大缩小,没有碎片,不能随机访问,缺失一环会丢失信息
- 索引存储,建立索引表,容易增大缩小,没有碎片,可以随机访问,小文件冗余信息多,大文件需要扩展索引块,链式、多级
- 空闲空间管理,用bitmap表示,先将硬盘上的bit置为1,再分配空间,以保证一致性;也可以通过链表实现
- 多磁盘管理 RAID,冗余磁盘阵列,通过多磁盘提高速度、可靠性;OS实现或硬件实现
- RAID 0,将数据均匀防止在多个硬盘上,并行访问,成倍提高速度
- RAID 1,镜像硬盘,提高可靠性
- disk 1 - 4并行,disk 5 作为parity disk,存储disk 1 - 4的奇偶校验数据,1 - 4任何一个磁盘故障,可以据此恢复;将parity disk平均放置在disk 1 - 5上,平衡了读写负载,即为RAID 5
- 磁盘调度,在OS层面组织请求顺序,减少磁盘读写
- 在磁盘上定位数据时,先确定在哪个磁道(同心圆)上,再确定扇区,读取数据;找磁道的时间为寻道时间,占大头;目标是减少寻道时间,尽量按顺序寻道,避免磁头往复移动,
- 电梯算法,磁头走到头再回头,如此往复,类比电梯;变种C-Scan,只单方向扫描
- N-Step算法,解决“磁头粘着”(磁头在小区域内往复运动),将请求队列分成N个子队列,子队列内Scan;N为2,FSCAN,当前队列,和下一次队列
(完)
下一篇: DRDS分布式SQL引擎—执行计划介绍