非常重要,但是我投入不太够,也忘得差不多了,重新系统学一遍吧。内容还是王道考研
前言
操作系统是什么?
管理整个软硬件资源,合理组织调度资源;给用户和其它软件提供接口和环境;是计算机最基本的系统软件。
.bat 批处理文件,依次执行很多命令。
操作系统四个特征
并发
多个事件在同一时间间隔内发生,宏观上同时,微观上交替发生。
并行:多个事件同时发生。
共享
互斥共享(一个时间段只允许一个进程访问)和同时共享(一个时间段,多个进程同时访问)。
虚拟
空分复用技术:一个物理实体变成若干个逻辑对映体,比如虚拟内存,认为自己独占了全部内存。
时分复用技术:虚拟处理器。(切换的够快,很多进程可以同时运行)
异步
多进程并行,你不知道具体的执行顺序。
发展
纸袋机(只能读一个纸带)->外围机(一次读多个纸带)->多道批处理系统(并发运行多个纸带)->分时操作系统(切成时间片,供多用户使用)->实时操作系统(可以对任务紧急程度安排)
运行机制
c文件->预处理编译汇编链接后->二进制代码->cpu
内核:操作系统最核心部分,最接近硬件。(docker只需要linux内核就能跑了),因此一般只有管理者可以用。
内核态用户态:管理当前状态。看能否执行内核指令
中断和异常
- 异常:是 CPU 执行指令时检测到的同步错误或特殊条件,来自内部问题
- Trap:有意触发的异常,让内核干事(比如调试/断点)
- Fault:可恢复的错误,如缺页异常等。执行完成后重新执行后续指令
- Abort:不可恢复错误,强制终止程序
- 中断:外部硬件或软件的信号,告诉CPU优先处理某个事
- 时钟中断:调度时候,告诉该去哪里干活了
- I/O中断:通知CPU磁盘数据准备好了
系统调用
直接请求操作系统的服务,比如(存储分配,I/O操作,文件系统等)
如果在高级语言,一般是传入系统调用参数->执行trap指令(用户态)->执行相应的处理系统调用(核心态)->返回程序。
操作系统体系结构
典型的大内核(宏内核/单内核):Linux,UNIX,优点性能高,但是内核代码庞大冗杂
典型的微内核:Windows NT,内核少,但是需要频繁的核心态和用户态之间切换。
分层结构:类似计网分层调用,这能一层层往下调用。好处是调试很方便,扩展层方便,坏处效率低了。
模块化:一个模块负责一个工作。好处逻辑清晰,易于维护,相互调用效率高。坏处不易调试。
外核:可以直接给进程分配资源(不许你内存啥的),好处是效率明显高了,坏处是降低了一致性。
操作系统引导
主存:RAM和ROM(BIOS),ROM是自启程序。
- CPU从特定主存地址开始,读取指令,执行ROM中引导程序(先硬件自检,再开机)
- 从磁盘第一块,主引导记录读取内存,执行磁盘引导程序,扫描分区表
- 再从活动分区,读取分区引导记录,执行程序
- 再找到操作系统的初始化程序并执行,完成开机
虚拟机
有两类,我们一般用的VMware是第二类,其本质是在宿主操作系统之上再分配。这样好处是方便用迁移性很好,坏处是性能很差,每一次内核请求都要通过HostOS作为中介。
对于第一类,迁移性比较差,但是性能好,可以支持更多的硬件资源。
对于虚拟机对内核的请求,会通过虚拟机管理程序后,再根据实际情况,完成请求。对内表现完成了内核操作。
进程管理
进程
基本理解
- 程序:是静态的,是存放在磁盘里面的可执行文件,是一系列指令集合。
- 进程:是动态的,程序的一次执行过程(统一程序多次执行对应多个进程),是资源调度和分配的一个单位
- 程序段(程序代码),数据段(过程中产生数据),PCB(进程控制模块,有PID,磁盘占用等)
进程的状态
PCB的state来记录状态
创建态:蒸菜被创建,os为分配资源,初始化PCB
就绪态:具备运行条件,但还没运行。可能是没有CPU资源或者没调度到
运行态:占有CPU,可以在CPU上运行
阻塞态(等待态):因为某些事不得不停止,等待被唤醒。
终止态:进程从操作系统撤销,开始回收资源,撤销PCB
组织方式:优先级队列,依次执行。
进程通信
两个进程之间产生进程交互。进程是分配系统资源的单位,因此各进程拥有的内存地址空间独立,需要通过操作系统完成通信。
共享存储
对于共享存储区,多个进程共享这一片区域。但需要保证对空间访问是互斥的,不要读写覆盖了。
消息传递
- 直接通信: 指明要发送的进程ID给内核,内核维护一个消息队列,传递的消息都从这里通过
- 写好消息头(发送进程ID,接收ID,长度格式),消息体
- 间接通信:以信箱作为媒介,把消息发送到指定的信箱。接收方通过访问信箱读取
- 这样可以多个进程往一个信箱里塞消息,也可以多个从一个接收
- 管道通信:单向通信,内存中开辟一个序列,循环队列读和写。
- 若要双向通信需要设置两个管道
- 若管道写满了,则写进程被堵塞。
- 可以有多个写或多个读(看具体实现方案)
线程
现代CPU调度的基本单位(最小单位),可以在不同的CPU里面
- 有线程ID,线程控制模块(TCB)
- 也具有就绪,阻塞,运行状态
- 同一进程间不同线程共享进程资源,且切换时系统损耗比较小
实现方式
- 用户级:手动维护线程,通过应用程序完成,在外部看起来一个进程。
- 好处:不需要频繁请求系统调用,请求中断之类的。效率高
- 坏处:线程被堵塞后,整个进程也会被堵塞,并发不够。且不可能在多核上运行。
- 系统级:操作系统调度 。
- 好处:一个线程堵塞后,别的还可以运行,并发能力强。
- 缺点:线程切换要系统调用,管理线程麻烦。开销大
- 多线程模型:(此时又有多种实现方式),优势缺点和上面类似
- 一对一:一个用户线程对应一个系统线程
- 多对一:多个用户线程对应一个系统线程
- 多对多:多个用户线程对应多个系统线程
线程状态转化
就绪,阻塞,运行三种之间相互转化。TCB来控制,会记录线程切换时候要记录和保存的数据。
调度
由于资源有限,且事情没有办法同时处理。此时确定的某种规则来决定处理顺序。
- 调度器:在创建新进程,进程退出,运行进程受阻,IO中断和调度时候触发
- 闲逛进程:优先度最低,没有其他就绪进程时候就可以运行一下。
三个调度层次:高级中级低级
- 高级调度:作业调度,决定哪些作业(Job)可以进入系统,成为进程。
- 发生频率最低,发生在从外存到内存(作业的),让进程 无->创建态->就绪态
- 中级调度:内存调度,决定进程在内存和外存(硬盘之类的)之间的转化。
- 发生频率中,发生在从外存到内存(进程的),完成挂起态和非挂起态直接转化
- 低级调度:进程调度,决定哪个进程可以获取CPU资源,执行任务。
- 发生频率高,发生在内存->CPU,就绪态->运行态转化
七状态模型,其中挂起是中级调度时候,内存不够被Swap到外存的,优化系统使用。
进程调度时机(低级调度)
- 主动放弃:进程正常终止,异常终止,主动阻塞(等待IO)
- 被动放弃:时间片用完,被更高优先级的插入了
由此,可以对应进程的调度方式
- 非剥夺式调度方式:只允许进程主动放弃,直到进程终止或者主动放弃。这种比较简单,但是无法处理紧急任务,早期的批处理系统才用。
- 剥夺调度方式:外部可以暂停或终止正在进行的进程,这样就可以比如按照时间片轮转方式进行。
那什么时候不可以进程调度?
- 处理中断的过程中
- 进程在操作系统内核程序临界区
- 临界资源:一个时间段只允许一个进程使用的资源,需要互斥访问
- 临界区:访问临界资源的那段代码
- 可以看到,如果内核不尽快释放临界资源,那么很有可能会影响内核其它资源的工作。
- 原子操作过程中
调度算法
Cpu利用率:忙碌时间/总时间
周转时间:作业提交到处理完成之间的时间,越小越好
带权周转时间:周转时间/实际运行时间,>=1且 越小越好
等待时间:处于等待处理时间之和,一般来说一个作业需要被CPU服务多久是固定的,调度只是影响作业/进程的等待时间
响应时间:用户从提出请求到首次得到响应的时间。
响应比:(等待时间+运行时间)/运行时间
批处理系统,一次处理一个任务或者用户
- 先来先服务(FCFS First come First serve)
- 按照来的顺序调度
- 简单公平,但平均等待时间长(因为可能短的等长的)
- 最短作业调度(SJF Shortest Job First)
- 谁最短谁先执行。
- 抢占式就允许正在进行的中断,交给更短的;非抢占式是结束时候才换
- 好处:平均等待时间短
- 坏处:1.需要知道某个作业要运行多久 2.长作业一直等待得不到调度(饥饿)
- 谁最短谁先执行。
- 高响应比优先(HRRN Highest Response Ratio Next)
- 响应比最高的先(响应比高一般意味着被用的少)
- 优点:兼顾长短
- 缺点:动态计算响应比开销
后面有了分时操作系统,提出了新的方案
- 时间片调度(RR Round Robin)
- 每个进程固定时间,到时间就切换
- 优点:非常公平
- 缺点:过短导致切换花费大量时间,过长导致等待长,反应慢。无法区分优先度
- 优先级调度(Priority Scheduling)
- 优先级队列,谁优先级高,谁先执行。
- 抢占式:允许中断;非抢占式:不可以中断。
- 优先级一般系统>用户,前台>后台,更偏好I/O密集型(因为I/O和CPU可以并行)
- 好处:灵活 坏处:优先级低的可能很难调度
- 优先级队列,谁优先级高,谁先执行。
- 多级反馈调度(MFQ,multiplevel feedback queue)
- 来的时候就最高优先级,如果时间片用完了就下一级。
- 最高级一般时间片短一点,越往下时间片会大一些。
- 如果一个进程长时间没有被调度,那么会提高优先级,保证会被调度到。
进程同步与互斥
进程同步:保证多个进程工作遵循一定先后顺序(防止脏读幻读之类的)
进程互斥:对于临界资源访问需要互斥进行,一段时间内只能有一个进程进行访问
进程互斥
软件实现方法:
- 单标志法:用一个共享变量决定哪个进程可以进入临界区。在访问完临界区后,会把临界区访问权限给另一个进程。也就是说,一个进程的进入临界区权限只可以由上一个赋予。
- 问题:必须按顺序走,即使一个不需要也会轮到他。即使另一个需要也轮不到他
- 双标执法:用flag标志每个进程是否想进入临界区,如果对方想进入就先卡着不进去。当对方不想进入就进去。(先等待再设置)
- 问题:线程安全问题,多进程会有并发问题。
- 修复:双标志后检查法。先设置变量,再循环等待
- 问题:死锁
- Peterson算法:有冲突时候让别人先来
- 问题:活锁
硬件实现方法:
- 中断屏蔽方法:暂时关闭 CPU 的中断响应能力,在这临界区期间不会被打断
- 优势:简单,效率高
- 缺点:不适用于多处理机;只适用于内核进程(因为权限很大)
- TestAndSet:检查锁是否被使用,如果没被使用就把锁设置为true
- 优势:简单,可以应对多处理机
- 缺点:无法进入临界区进程就会一直循环调用,占用资源
- Swap:记录临界区是否上锁(在old变量上),如果old为false那么重上锁,整体思路优缺点和TestAndSet差不多
锁:
- 自旋锁:一直自旋等待
- 好处:不需要切换进程上下文,在上锁时间短时候代价抵,效率高。适合多核不适合单核。
- 坏处:浪费CPU,且只可以对应一个资源。
信号量:
- 整形信号量:用一个整数变量表示某个资源的个数
- P:申请一个资源
- V:释放一个资源
int S = 1 ;
void wait (int S) { // wait原语(不允许中断的),进入进入区
while (S <= 0); // 一直循环等待(占用资源)
S = S - 1; // 资源足够,占用资源
}
void signal (int S) { // signal 原语 退出区
S = S + 1; // 使用完资源后,在退出区释放资源
}
- 记录形信号量:用记录形数据结构表示
- 相比于上面的,当资源不可用时候,操作系统就会挂起,放在等待队列里面。其他线程调用 V() 时,操作系统会从等待队列中唤醒一个线程,再去执行。这样就节省了CPU资源
- 实现方法就是记录一个资源数,每有一个就-1,当不足时候挂起放在队列里面。当唤醒时候,判断当前资源数如果是负数则唤醒一个资源。
此时,就可以也可以用信号量完成同步操作。
例如对于下图,保证同步的方法就是,对于每一个依赖关系,都添加一对信号量。比如S1S2,你可以让S1执行完成之后,才V(a)释放资源,这样S2才可以开始。于是保证了同步
生产者-消费者
生成者进程会把产品生成好放在缓冲区,消费者会去里面消耗。
显而易见,对于缓冲区的访问,各个进程必须互斥的进行。不然可能多个读同一个或者多个写同一个问题。
注意必须要先消耗empty和full资源,再去申请锁。即:实现互斥操作的P操作一定要在实现同步的P操作之后。不然可能会死锁
多生产消费者
每次需要检查两个资源,一是缓冲区是否有剩余,二是是否是自己对于的资源。
管程
为什么引入?方便使用。只能够通过特定的入口才可以访问数据,一次只允许一个线程执行内部过程
特性 | 管程(Monitor) | 信号量(Semaphore) |
---|---|---|
设计目标 | 封装共享资源,简化同步逻辑 | 提供更底层的同步原语 |
互斥实现 | 自动(编译器/语言内置锁) | 需手动用 P() /V() 实现 |
条件同步 | 内置 wait() /signal() | 需用多个信号量组合实现(复杂) |
易用性 | 高(避免死锁、优先级反转{低优先级阻塞高优先级}等问题) | 低(容易用错,比如 P() /V() 顺序错误) |
典型语言 | Java (synchronized +wait/notify )、C# | C/C++、操作系统原语 |
死锁
银行家问题,吃不到饭。多个进程都在等待对方释放资源。
原因:资源有限,顺序不当
预防策略
- 破坏互斥条件:如SPOOLing技术,把物理上独占的资源改成逻辑上共享的资源
- 问题:很多时候没有办法破坏互斥条件
- 破坏不剥夺条件
- 不剥夺条件:进程所获得资源在没有使用完前,不可以强行夺走
- 问题:实现复杂,申请保存有开销,可能导致饥饿
- 破坏请求和保持条件:
- 即一次申请全部想要的资源,不再对别的请求了。
- 资源利用率低,也有可能导致一些进程饥饿
银行家算法
核心就算避免死锁。原理是,假设分配资源之后,寻找是否存在一个安全的分配序列,如果找不到,则说明当前分配不安全,就不允许分配。
- 判断是否超过之前所要的请求,若超过就认为出错
- 判断系统能否满足这一次要求
- 判断分配后,是否会进入不安全状态
- 若可以就分配,不行就不分配,让那个进程阻塞等待
死锁检测与解除
- 检测:资源分配算法(RAG)
- 构建分配图:(进程节点P和资源节点R连接)
- 简化图:找出不阻塞的进程然后去除,直至无法简化。如果还有边那就说明有死锁
内存
基本知识
物理逻辑内存
物理内存:实际存在的RAM硬件地址
逻辑内存:进程看到的地址空间
地址绑定
地址绑定:逻辑地址转化为物理地址
- 编译时绑定:程序在编译期间就绑定物理地址。
- 问题:灵活性非常差
- 加载时绑定:加载到内存的时候绑定地址,
- 问题:如果程序被换出内存,地址可能变化了
- 运行时绑定:运行时候,通过MMU动态转化为物理地址
- 高效,动态支持(分页交换等),支持虚拟内存
地址保护
内存保护:防止进程访问其它进程或者操作系统内存
- 方案一:基址寄存器(Base Register)和界限寄存器(Limit Register)
- 记录起始地址和最大地址,每次访问时候判断是否合法
- MMU(Memory Management Unit) 负责地址转换和保护。
- 使用 页表(Page Table) 或 段表(Segment Table) 实现更精细的保护
覆盖和交换
为什么?因为早期计算机内存不足。
内存覆盖:程序划分为多个模块,按需加载到内存,同一时间只保留必要的模块。但是需要程序员手动管理,比较麻烦。
内存交换:当内存不足时候,把整个进程暂时交换到磁盘交换区(Swap),等需要的时候再换回内存,提高了多进程的并发性。现在一般也就linux用
技术演进:覆盖->交换->虚拟内存(分页/分段)->现代混合管理(Swap+分页)
内存管理
内部碎片:分配给进程的内存区域,有没有利用上的
外部碎片:由于内存分配太零散,块都太小难以利用上
连续分配
单一连续分配
内存分为系统区和用户区,且内存只有一个程序(用户进程独占)
优点:简单,没有外部碎片 缺点:只有一个进程利用率很低,有内部碎片
固定分区技术
把内存分成若干大小的分区
优点:无外部碎片 缺点:程序过大都不满足,且会有内部碎片利用率低
动态分区分配
进程装入内存时候,动态建立分区 。
优点:彻底没有内部碎片了(要多少给多少) 缺点:外部碎片很多
动态分配算法
- 首次适应算法:第一个找到的块
- 最佳适应算法:优先使用最小满足的
- 问题:会留下很多很小的无法利用的块
- 最坏适应算法:优先使用最大的
- 问题:大进程没得用了
- 邻近适应算法:每次从上次结束的位置去找
非连续分配
分页存储
物理内存和进程的地址空间都被划分为大小相同的页,操作系统通过页表记录页到物理页框的映射关系。
问题一:怎么找?通过偏移量找到事哪一页,然后计算页内偏移量找到。二进制的话mod直接取后面多少位就行,很方便 。每一个进程都有自己一个独立的页表,页表一般放在内存某个区域,寄存器存着可以快速找到。
快表(TLB)
由于页表放在内存中,直接访问性能下降(地址转化需要多次访存,因此加入了Cache)
要访问页的时候,先去快表查一下,没查到再去内存的页表去找,然后再去内存里面去找。这里也就有了局部性,如果你多次访问都在一个页表里面,那Cache找的就快。
两级页表
为什么需要?占用太大了。一个很小占用的进程如果维护一个很大的页表就很亏。那就相当于再给全部的页表再加一个页表,并加一个标记表示当前页块(二级页表)是否存在,如果不存在就报缺页异常,然后读取过来。
分段
把程序的逻辑地址空间分成多个段,那这个时候,逻辑地址就是<段号 ,段内偏移>
为了记录这些信息呢,系统就会维护一个段表
优点 | 缺点 | |
分页 | 内存利用率高,不会有外部碎片 | 不方便按照逻辑模块实现信息共享保护 |
分段 | 很方便按照逻辑实现信息共享 | 可能会有外部碎片,对于很长的连续空间分配很不方便 |
分段好处就是便于权限管理,比如这一块代码段就不可写之类的。
段页式管理
先分段,再分页
特性 | 分段(Segmentation) | 分页(Paging) | 段页式(Seg-Paging) |
---|---|---|---|
内存划分 | 按逻辑模块(可变长) | 按固定页(4KB等) | 先分段,再分页 |
碎片问题 | 外部碎片严重 | 仅内部碎片 | 无外部碎片 |
硬件依赖 | 段寄存器 + 段表 | MMU + 页表 | 段寄存器 + 页表 |
现代应用 | 基本淘汰 | 主流(Linux/Windows) | x86保护模式(实际被扁平化) |
虚拟内存
让进程以为自己独占了整个内存。实际上有些东西,需要时候才会去调用,不常用就换下去了。
核心几个技术:请求分页,页面置换,分页存储
- 页表机制
- 状态位:表示页面是否在内存
- 访问字段:记录最近被访问了几次
- 修改位:表示页面在内存时候是否被修改过
- 外存地址:页面在外存中存放的位置
- 缺页中断机构:
- 找到页表项后检查页面是否在内存中,不存在就缺页中断
- 缺页中断处理中,需要把目标页调入内存,有必要时候换出页面
- 地址变换机构:
- 找到页表项检查页面是否在内存
- 不存在就调页
- 存在就换出页面
- 页面调入内存后,需要修改对应表项数据
页面置换算法
- 最佳置换算法:选择以后永远不适用或者最长实现内不再被使用
- 问题:你怎么知道?
- 先入先出算法FIFO:先入先出
- 缺页率高,哪怕经常用也换出去了,性能差
- 最近最久(LRU):最近最久没有使用的
- 性能好,但实现难(?)开销大(?)
- 时钟置换算法:循环队列,如果一个被用了置为1,要替换时候把1变成0,找到第一个为0的换出去
- 优化:把修改过的先换出去
页面分配算法
- 驻留集:给进程分配的总大小。一般小于进程总大小,过大浪费,过小频繁调度卡死了。
- 页面分配策略(两两组合)
- 固定分配:固定给空间不准变
可变分配:每个开始有一定块,根据实际情况变大变小
局部置换:缺页时候只可以从自己进程里面换
全局置换:可以把外部的页给进程,也可以把进程的页给出去
- 固定分配:固定给空间不准变
- 抖动现象:一个块刚换出去就要换进来。一般就是因为给的页少了
- 何时调入页面:
- 预留策略:进程开始运行之前,选择哪些块进去
- 请求调页策略:发现缺页就调页
- 从何处调页:
- 交换区:连续存储更快 文件区:采用离散的存储方式,速度慢
- 交换区够大就用,交换出来就放着
- 交换区不够大就每次从文件区读,修改回去的放在交换区
- 工作集:某短时间内,进程实际访问页面的集合。一般比驻留集比这个大一点(不然工作者呢你一直换出去换进来)
内存映射文件
用来让程序员更方便使用文件的。
特性 | 内存映射文件(mmap) | 传统I/O(read/write) |
---|---|---|
数据拷贝 | 无(直接操作页缓存) | 需从内核缓冲区拷贝到用户缓冲区 |
内存占用 | 按需加载(缺页中断) | 需预先分配缓冲区 |
随机访问 | 直接通过指针跳转 | 需lseek() +read() |
共享性 | 支持多进程共享 | 需额外机制(如管道、共享内存) |
适用场景 | 大文件、频繁随机访问、共享需求 | 小文件、简单顺序读写 |
文件管理
基本概念
是啥?存储在外存的命名数据集合,可以是文本程序图像,有文件名文件类型文件大小等
结构:无结构(流式文件)文本文件,有结构(记录文件)数据库文件,excel,索引文件(快速定位文件的)
存储方式:一个个块
逻辑结构
- 无结构文件:一系列二进制流或字符流组成,成为流式文件。如Windows的txt
- 有结构文件:由一组相似的记录,就像数据库一样。
- 顺序文件:逻辑上顺序相连(可以是链表或者数组)
- 索引文件:通过索引表,指到每一项的位置
- 索引顺序文件:通过索引表,指向每一个文件位置
- 多级索引:给索引建索引
文件目录
- 单级目录结构
- 所有文件放在同一个目录下,简单但容易发生命名冲突
- 两级目录结构
- 为每个用户创建单独目录,实现多用户
- 树形目录结构
- 和现代的比较像了,但是文件共享不方便
- 无环图目录
- 允许文件和目录被多个目录引用,分为软链接硬链接。当一个文件被引用为0就彻底被删除。
- 好处支持了文件共享,坏处需要避免循环引用
文件的物理结构
- 连续分配:记录起始块号和长度
- 支持顺序访问和随机访问
- 问题:我需要每个文件在磁盘上有一组连续的块,不方便文件扩展,碎片也多
- 链接分配
- 隐式链接:块之间像链表连接,目录中只标注起始和结束块
- 方便扩展文件,不会有碎片
- 只支持顺序访问,随机访问效率低
- 显示链接:把文件所有的块索引全部存在一张表(文件分配表FAT中),记录下一个块和是否结束
- 此时,要查找第几块就只需要fat表(在内存中)跳转几次之后,再去找对应的块即可,随机访问速度快了很多
- 缺点:存储文件分配表需要一定内存
- 隐式链接:块之间像链表连接,目录中只标注起始和结束块
- 索引分配
- 为每一个文件有一个索引表,索引表内顺序
- 支持随机访问,也很容易扩展,也需要占用一点空间
- 如果文件太大,一个索引表不够(?)
- 连接索引,多层索引(类似多级页表),混合索引
文件存储空间管理
- 空闲表法:起始空闲块和空闲块数
- 空闲链表法:记录指向下一个文件的头
- 位示图法:用bit表示是否被分配,然后存一个大表
- 成组链接:大块指小块
文件基本操作
- 创建文件
- 文件系统为新文件分配一个inode,并更新目录项
- 打开文件
- 检查用户是否有权限,返回一个文件描述符,便于操作
- 读取文件
- 根据文件描述符找到文件inode,然后定位数据块,复制到内存
- 写入文件
- 根据文件描述符找到文件inode,分配或更新数据块,然后再写入磁盘
- 关闭文件
- 操作系统刷新文件缓冲区,把数据写入磁盘
- 删除文件
- 删除文件目录项,释放inode和数据块
inode:index node(索引节点),每个文件或目录有唯一一个inode,记录存储了文件的元数据(文件大小,权限,所有者等),以及指向文件数据块指针。
所以inode是文件真实身份,文件名只是起一个别名,便于用户识别
文件共享
特性 | 硬链接 | 软链接 |
---|---|---|
本质 | 同一inode的多个文件名指向 | 存储目标路径的独立文件 |
跨文件系统 | 不支持 | 支持 |
链接目标类型 | 仅限文件 | 文件或目录 |
inode号 | 与原始文件相同 | 独立于原始文件 |
目标删除后 | 仍可访问(直到引用计数归零) | 链接失效(悬空) |
性能 | 更高(直接访问inode) | 略低(需路径解析) |
创建命令(Linux) | ln source target | ln -s source target |
文件保护
- 口令保护
- 加密文件,需要输入口令对比
- 好处:开销小 坏处:不太安全
- 加密保护:需要提供正确密码才可以正确的解密
- 比如说给文件所有数据异或,你只有知道密码才正确解密
- 缺点:加密解密都要一定时间
- 访问控制:记录每个用户可以对文件有哪些操作
文件系统目录结构
虚拟文件系统
比如说你的硬盘,U盘,机械硬盘可能文件系统都不一样,此时用虚拟文件系统VFS屏蔽这些差异,可以方便的使用
挂载:文件系统挂载到操作系统中(U盘)。1.VFS中注册挂载表,2.提供函数列表 3.挂载在某个父目录下
I/O设备管理
鼠标键盘啥的
- 机械部件:鼠标键盘显示屏,执行具体的I/O操作
- 电子部件:CPU和I/O设备的中介,用来控制和转换输出
控制方式