操作系统
信号量¶
- 信号量可以是负值,每P一次就会减1。
I/O层次结构¶
1. 场景联想记忆法¶
- 第一层:用户层 I/O 软件 = “大老板”
- 角色: 只负责提需求,完全不管细节。
- 动作: 老板说“给我打印这份文件”(调用
printf等库函数)。老板还会把任务丢给秘书排队(SPOOLing 技术就是在用户层实现的)。 - 第二层:设备独立性软件 = “部门经理”
- 角色: 承上启下,做统一的管理、安保和资源分配。他不干具体脏活,但要负责统筹。
- 动作:
- 查字典(映射): 老板说用“1号打印机”(逻辑设备),经理去查表,发现“1号”对应的是走廊那台惠普打印机(物理设备)。这叫逻辑设备到物理设备的映射。
- 安保(保护): 检查老板有没有权限用这台打印机。
- 分配资源与缓冲: 安排缓冲区,统一管理所有设备的接口。
- 第三层:设备驱动程序 = “专业操作员”
- 角色: 懂具体机器的技术骨干,跟具体的硬件(比如那台惠普打印机)直接打交道。
- 动作: 经理让他打印,他得把指令翻译成机器听得懂的话。比如“把纸张送入、加热到180度、控制墨盒移动”。这就是计算具体的物理地址(如磁盘的柱面/磁道),并向硬件的寄存器发送控制命令。换个牌子的打印机,就得换个懂行的操作员(所以驱动程序是与设备直接相关的)。
- 第四层:中断处理程序 = “机器报警器”
- 角色: 最底层,只负责听硬件的动静。
- 动作: 机器打印完了,“滴”一声响(硬件中断),它就负责接收入这个信号,然后层层往上汇报“活儿干完了,把操作员唤醒吧”。
2. 考试“抓题眼”速记表¶
在做单选题时,只要看到题干里出现特定的关键词(题眼),就可以直接秒杀对应层次,不需要每次都从头推导:
| 看到这些关键词... | 直接选这个层次! |
|---|---|
库函数、SPOOLing、系统调用接口(向用户提供) |
用户层 I/O 软件 |
逻辑设备、映射、保护、统一接口、分配、缓冲 |
设备独立性软件 (与设备无关的软件) |
寄存器、硬件细节、逻辑地址转物理地址、启动设备 |
设备驱动程序 |
唤醒阻塞进程、保存被中断进程的CPU环境 |
中断处理程序 |
页¶
PDBR¶
虚拟地址是给程序(软件)看的,而 PDBR 是给 MMU(硬件)指路的。硬件找硬件,只能用物理地址。
一个操作系统中存在多个进程,因此也会有多个顶级页表,因此进程切换时需要更改PDBR中的内容。
主存物理地址与虚拟地址¶
- 主存物理地址(Physical Address)是指计算机内存(主存)中实际存储单元的硬件地址。
在采用了虚拟存储管理的计算机中,CPU 运行程序时产生的是虚拟地址(逻辑地址),这个虚拟地址需要经过转换,才能变成实际去内存里拿数据的物理地址。
物理地址的构成为:物理地址 = 实页号(也叫物理页号)+ 页内地址。
- 虚拟地址(对CPU)
虚拟地址的组成:页目录号 + 页号 + 页内地址。(由二级页表举例)
每一个“页目录项”里面存放的是某一张具体的二级页表在物理内存中的起始地址。
计算虚拟空间(虚拟存储空间)的大小,唯一需要看的指标就是虚拟地址的总位数。
虚拟地址的前面部分(在图中叫“虚拟号”,通常也叫“虚拟页号”)的位数,直接决定了整个虚拟存储空间里总共包含多少个虚拟页。
页面置换算法¶

计算文件最大大小¶
算文件系统能支持的最大文件长度时,应当是用簇大小 / 地址项长度,而不是 / 索引节点大小。
用户级线程与内核级线程¶
用户级线程由线程库管理,是“黑户”;内核级线程由操作系统管理,并有自己的”户口本“(TCB)存放在内核空间。
前者切换快,后者切换慢。详细请看强化/3。
“被唤醒”(Wakeup)指的是进程从阻塞态(等待态)转换到就绪态。
死锁¶


限制用户申请资源顺序的只有死锁预防,如通过顺序分配资源来破坏循环等待。
内部碎片和外部碎片¶
各内存管理方式的碎片情况¶
连续分配方式¶
- 单一连续分配:有内部碎片,无外部碎片。(整个内存只给一个进程用)
- 固定分区分配:有内部碎片,无外部碎片。(按固定大小分区,进程装不满分区)
- 动态分区分配:无内部碎片,有外部碎片。(要多少切多少,绝不浪费内部空间,但切剩下的缝隙形成外部碎片)
非连续分配方式 (离散分配)¶
- 分页式存储:有内部碎片,无外部碎片。(物理内存被切成固定大小的页框。进程的最后一页通常装不满)
- 分段式存储:无内部碎片,有外部碎片。(每个段的大小是动态按需分配的,本质和动态分区一样)
- 段页式存储:有内部碎片,无外部碎片。(先分段,再把段打碎成页。底层分配单位依然是固定大小的“页”,所以每个段的最后一页都可能产生内部碎片)
重点易混淆:动态分区分配的 4 种算法¶
所有动态分区分配算法都只会产生外部碎片。但它们产生外部碎片的特征不同,是选择题高频考点:
- 首次适应算法 (First Fit)
- 特征:每次从低地址开始找。
- 碎片分布:低地址产生大量微小外部碎片。
- 考点:保留了高地址的大块连续空间,综合性能最好,最容易满足大进程需求。
- 循环首次适应算法 (Next Fit / 邻近适应)
- 特征:接着上次找的位置往下找(雨露均沾)。
- 碎片分布:外部碎片均匀分布在整个内存中。
- 考点:查找速度最快(不用每次跨越低地址碎片),但会破坏高地址的大块空间。
- 最佳适应算法 (Best Fit)
- 特征:每次挑“能装下且最小”的空闲块。
- 碎片分布:产生最多、最细碎的外部碎片。(因为每次剩下的边角料都极其微小)。
- 最坏适应算法 (Worst Fit / 最大适应)
- 特征:每次挑最大的空闲块切。
- 碎片分布:产生最少的外部碎片。(因为切完剩下的部分通常还很大,别人还能用)。
- 考点:最容易破坏大块连续空间,导致后续大进程无法运行。
影响请求分页系统平均访存时间的因素¶
缺页率、内存访问时间、执行缺页处理程序的CPU时间以及磁盘读写时间,因为请求分页系统由于修改过的页是需要写回外存的,因此磁盘读写时间也是会影响到请求分页系统的平均访存时间。
父进程与子进程¶
在操作系统中,当一个进程通过系统调用(如 UNIX/Linux 中的 fork())创建了另一个新进程时,创建者被称为父进程,被创建的新进程被称为子进程。它们之间形成了一种树型的层次结构,主要关系体现在以下四个核心维度:
资源分配与独立性¶
- 资源继承与写时复制(Copy-on-Write, COW): 子进程在创建时,会获得父进程数据空间、堆、栈的副本。在现代操作系统中,为了提高效率,通常采用写时复制技术——父子进程最初共享同一块物理内存,只有当其中一个进程试图修改数据时,操作系统才会为子进程分配独立的物理内存页并复制数据。
- 独立的地址空间: 尽管子进程是父进程的“克隆体”,但创建完成后,二者拥有各自独立的虚拟地址空间。一个进程对其变量的修改对另一个进程是不可见的。
- 共享文件描述符: 子进程会继承父进程打开的所有文件描述符。这意味着父子进程共享相同的文件偏移量(文件指针),如果子进程读取了共享文件,父进程的文件指针也会随之前移。
进程控制块(PCB)的关联¶
- 每个进程都有自己独一无二的进程标识符(PID)和独立的 PCB,用于被操作系统独立调度。
- 子进程的 PCB 中会记录其父进程的 PID(即 PPID)。通过这种记录机制,操作系统能够维护进程家族树的拓扑结构。
并发执行与同步¶
- 并发调度: 任意两个进程都是可以并发执行的。 父子进程是两个独立的调度实体,它们在系统中并发运行,共同竞争 CPU 时间片。至于
fork()之后是父进程先执行还是子进程先执行,完全由操作系统的调度算法决定。 - 同步等待: 父进程可以使用
wait()或waitpid()系统调用主动阻塞自己,等待子进程执行完毕并收集其退出状态。
生命周期与异常状态¶
父子进程的生命周期并不是完全绑定的,结束顺序的不同会引发不同的特殊进程状态:
- 僵尸进程(Zombie Process): 如果子进程先结束,但父进程没有调用
wait()来读取子进程的退出状态,子进程的系统资源会被释放,但它的 PCB 依然会保留在内存中,占用一个 PID,此时子进程就变成了僵尸进程。 - 孤儿进程(Orphan Process): 如果父进程先结束,而子进程还在运行,这个子进程就会成为孤儿进程。为了防止孤儿进程结束后变成无人回收的僵尸进程,操作系统通常会安排
init进程(PID 为 1 的系统初始进程)收养它,负责在它退出时回收资源。
多级页表¶
各级页表(或页目录)项中存放的都是目标对象(次级页表或最终数据页)的物理页框号。MMU 提取该页框号后,若在末尾拼上全零,可获得该对象所在页的起始物理地址;若在最后一级拼上页内偏移量,即可获得最终数据的实际物理地址。
程序查询方式¶
程序查询方式适用于低速设备,因为低俗设备查询不用高速设备那样频繁,每次处理相同的时间高速设备耽误CPU的时间反而会更多。
中断响应和处理¶
对于要求画出CPU运行过程的图

上图是一个很好的案例,并且不要忘了中断屏蔽字,注意哪个不会被响应。
系统调用¶
系统调用都需要切换到内核态。调用库函数不一定切换到内核态。比如 rand() 只是库函数,通常一直在用户态执行;而 fork()、read()、write() 这类系统调用会进入内核态。
关于磁盘和操作系统的顺序¶
- 系统启动过程中,操作系统是怎么被启动的
- 激活CPU(激活CPU读取ROM中的BIOS)
- 硬件自检(检测是否存在硬件问题)
- 加载系统盘(加载含有操作系统的硬盘)
- 记载主引导记录(通过主引导记录获取操作系统)
-
加载分区引导记录(通过分区引导记录获取操作系统所在分区)
-
把一块硬盘制作成启动盘,需要做什么
- 低级格式化或物理格式化(划分磁道、扇区等物理结构)
- 分区(把磁盘划分成若干分区,建立分区表)
- 高级格式化或逻辑格式化(在分区中建立文件系统)
- 安装操作系统
文件系统的管理依赖操作系统,但磁盘上存在文件系统不代表磁盘上已经安装操作系统。
关键是区分两个概念:
- 文件系统的磁盘结构:根目录、空闲块位示图、文件分配表等,存放在磁盘上。
- 文件系统的管理程序:属于操作系统的一部分,负责创建、读取、修改和管理这些结构。
举个例子:
假设电脑已经安装了 Windows,现在插入一块新硬盘。
- Windows 中的格式化程序对新硬盘进行逻辑格式化。
- 逻辑格式化建立根目录、空闲空间管理结构等。
- 新硬盘因此具有了文件系统,但新硬盘上并没有安装 Windows。
- 后续 Windows 可以通过文件系统驱动程序管理这块硬盘中的文件。
多道程序系统¶
并行才是真正的同时。
不必实现虚拟存储管理,可以单独划区域给不同的进程。
影响缺页率的因素¶
页置换算法、工作集大小、进程的数量。
页缓冲队列不会影响缺页率,只降低缺页中断的处理代价。因为他设计出来是为了减少IO次数。可以简单理解为页面被置换出去以后,物理页框先别急着彻底清空,而是放到一个“缓冲队列”里待命。被淘汰的页面内容暂时还留在页框里。通过延迟/成批写回修改页以及复用尚未被覆盖的页面,减少磁盘 I/O。但是,这个队列里的页如果被访问算缺页,因为页表项中它的有效位是被置为0的。
单重中断与多重中断¶
单重中断中开关中断都是硬件自动完成的;多重中断在刚开始硬件检测到中断后,也会关中断来保存断点和跳转到中断服务程序入口,然后中断服务程序内操作系统保存完现场后会开中断,当所有中断服务程序结束后返回后,还是由硬件开中断。
中断引发的进程状态切换¶
-
中断处理程序是操作系统内核代码,本身不是进程,因此执行它不代表着发生进程切换;
-
进程等待 I/O 时由运行态变为阻塞态,I/O 完成中断使其由阻塞态变为就绪态;若进程 Q 正在执行且时间片尚未用完,此时发生了与 Q 无关的中断,执行中断处理程序本身不会导致 Q 的进程状态改变;只有中断处理后触发了进程调度,Q 的状态才可能发生变化。
进程调度与进程切换¶
调度没有真正换进程,只是选出来,真正换是进程切换
虚拟空间大小¶
虚拟空间大小由系统地址结构决定,如:一个32位的系统可以支持最大4GB的虚拟地址空间。而非由内存、硬盘大小决定。
MMU¶
MMU(Memory Management Unit,内存管理单元)是CPU中的硬件部件,主要负责虚拟地址到物理地址的转换以及存储保护。
分页系统中:虚拟地址 = 虚页号 + 页内偏移
MMU利用虚页号查询TLB或页表,将虚页号转换成页框号:物理地址 = 页框号 + 页内偏移。其中页内偏移在地址转换过程中保持不变。
页表由操作系统建立和维护,地址转换由MMU硬件完成。
TLB可以看作MMU中的页表项高速缓存:
-
TLB命中 -> 直接获得页框号
-
TLB未命中 -> 查询页表
-
TLB未命中不等于缺页。
若页表项表明页面不在主存,则MMU产生缺页异常,由操作系统执行缺页异常处理程序,将页面调入主存并修改页表。
因此:OS负责内存管理,MMU负责地址转换和权限检查(当前页面是否可读、可写、可执行)。
同一进程下的线程¶
线程共享“进程拥有的资源”,但执行现场(PC、寄存器、栈)各自独立。若线程 T 调用 open() ,打开文件后形成的文件描述符会加入进程 P 的文件描述符表,不是放进“线程 T 专属的文件表”,因此为同一进程下的线程共享。如果是变量,则会放到对应线程的对应栈空间中,因此是独立的。
SCAN和C-SCAN¶
详见操作系统强化第六章
影响进程正常运行所需的最少页框数的因素¶

这是因为进程运行所需的最少页框数,取决于执行一条指令时可能同时涉及的最大页面数。
假设某条指令执行时,需要访问:
- 指令本身所在的页面:1 个页框
- 第一个操作数所在的页面:1 个页框
- 第二个操作数所在的页面:1 个页框
那么至少需要 3 个页框,才能保证这条指令正常执行。
但是,如果支持间接寻址,还可能需要额外访问存放有效地址的页面。
VFS¶
1. 概念及作用¶
VFS(Virtual File System,虚拟文件系统)是操作系统内核中的软件层,位于系统调用接口与具体文件系统之间。
主要作用:
- 提供统一接口:使用户程序能够通过统一的接口(如 open、read、write、close)访问不同类型的文件系统。
- 屏蔽底层差异:用户程序无需关心底层是 FAT32、NTFS 还是 ext4。
- 支持多种文件系统:可以同时管理不同类型的文件系统,包括本地文件系统和网络文件系统。
- 提供统一的文件对象抽象:例如文件对象、索引节点对象、目录项对象等。

内存映射文件¶
- 将磁盘文件的全部或部分内容映射到进程的虚拟地址空间,使进程能够像访问普通内存一样访问文件。
- 建立映射时通常不需要将整个文件装入内存,而是通过请求调页机制按需调入。
- 映射建立后,可以不使用 read/write 系统调用,直接通过内存访问指令读写文件内容。
- 内存映射文件不能消除磁盘 I/O,但可以减少系统调用及内核与用户空间之间的数据复制。
- 多个进程可以映射同一个文件,实现数据共享;共享映射与私有映射的修改行为不同。
- 内存映射文件依赖虚拟内存管理,仍然需要页表、MMU 和缺页异常处理机制。
- 内存映射 I/O(MMIO)是将设备寄存器与主存统一编址,不同于内存映射文件。
最后记住两个区别:
- 内存映射文件:文件 → 虚拟地址空间,目的是方便、高效地访问文件。
- 内存映射 I/O:设备寄存器 → 统一地址空间,目的是通过访存指令访问设备。
文件大小和蔟¶
-
规定一个蔟进用于存放一个文件的内容,不能由多个文件共享。
-
文件所占用的空间必须是蔟大小的整数倍。
- 文件的实际大小不一定是簇大小的整数倍,最后一个簇未使用的部分会造成内部碎片。
文件访问控制¶
文件访问权限的位表示(408)¶
- 权限位图:每种权限用 1 位表示,1 表示允许,0 表示不允许,多种权限可以同时拥有。
- 计算公式:总位数 = 用户类别数 × 权限种类数。
- 注意区分:若多个状态互斥,只需用足够的二进制位进行编码;若多种权限可以同时拥有,按题目规定分别设置权限位。
因为一个用户对文件的访问权限可以同时拥有多种,并不是多选一。
SPOOLing 技术(假脱机技术)¶
核心思想¶
SPOOLing 利用磁盘空间和软件模拟脱机输入/输出,将独占设备改造成逻辑上的共享设备。
- 本质:虚拟设备技术。
- 典型应用:共享打印机。
- 主要作用:提高设备利用率,减少进程等待慢速 I/O 设备的时间。
- 注意:物理设备仍然是独占设备,只是在逻辑上实现共享。
SPOOLing 系统的组成¶
| 组成 | 所在位置 | 作用 |
|---|---|---|
| 输入井 | 磁盘 | 暂存输入设备送来的数据 |
| 输出井 | 磁盘 | 暂存用户进程提交的输出数据 |
| 输入缓冲区 | 内存 | 暂存数据传输过程中的输入数据 |
| 输出缓冲区 | 内存 | 暂存数据传输过程中的输出数据 |
| 输入进程 | 软件 | 控制数据从输入设备进入输入井 |
| 输出进程 | 软件 | 控制数据从输出井送往输出设备 |
| 井管理程序 | 软件 | 管理输入井、输出井及作业 |
井相当于仓库,缓冲区相当于临时周转台,输入/输出进程负责搬运数据。
数据传输过程¶
| 阶段 | 数据流向 |
|---|---|
| 外部输入 | 输入设备 → 输入缓冲区1 → 输入井 |
| 作业读取 | 输入井 → 输入缓冲区2 → 运行作业 |
| 作业输出 | 运行作业 → 输出缓冲区2 → 输出井 |
| 外部输出 | 输出井 → 输出缓冲区1 → 输出设备 |
其中缓冲区1、2对应教材图中的不同传输环节。
- 井:位于磁盘,用于大量数据的暂存和排队。
- 缓冲区:位于内存,用于数据传输时的临时周转。
- 进程与井交互并不意味着绕过内存,实际数据传输仍然需要经过内存。
实现条件¶
- 大容量、高速度的外存:建立输入井和输出井。
- 内存缓冲区:缓冲数据传输,协调不同设备的速度差异。
- SPOOLing 软件:包括输入进程、输出进程和井管理程序。
- 多道程序设计技术的支持:使输入、输出操作与用户程序的执行能够并发进行。
外围机与外围设备¶
- 外围设备:具体的 I/O 硬件,如打印机、读卡机。
- 外围机:传统脱机 I/O 中,专门管理慢速设备和数据转储的辅助计算机。
传统脱机技术需要外围机,而 SPOOLing 利用软件模拟脱机 I/O,无需专门的外围机。
408 易错点¶
- SPOOLing 实现的是逻辑共享,不是物理设备真正同时共享。
- 输入井、输出井位于磁盘,输入/输出缓冲区位于内存。
- SPOOLing 需要多道程序设计技术的支持。
- 进程提交输出任务后,无需等待设备实际完成输出即可继续执行。
- SPOOLing 不需要专门的外围机,但仍需要实际的 I/O 设备。
- 由操作系统调用井管理程序来控制输入/输出井之间的数据传送。
FAT 文件分配表¶
FAT(文件分配表)采用链接分配,将各簇的链接信息集中存放在 FAT 中。
- 目录项:保存文件的起始簇号。
- FAT 表项编号:对应当前簇号。
- FAT 表项内容:保存下一个簇号,最后一个簇对应文件结束标志(EOF)。
- FAT 通常缓存在内存中:查询簇链不一定需要访问磁盘。
示例¶
文件簇链:100 → 106 → 108 → EOF
| 位置 | 存储内容 |
|---|---|
| 目录项 | 起始簇号 100 |
| FAT[100] | 106 |
| FAT[106] | 108 |
| FAT[108] | EOF |
核心公式:FAT[当前簇号] = 下一个簇号。
注意:FAT 表项编号是当前簇号,表项内容是后继簇号,不要混淆。
死锁的必要条件与处理策略¶
死锁的四个必要条件¶
四个条件必须同时成立才可能发生死锁,破坏任意一个即可预防死锁。
| 必要条件 | 含义 | 破坏方法 |
|---|---|---|
| 互斥 | 资源只能被一个进程独占 | 允许资源共享(如 SPOOLing) |
| 请求并保持 | 持有资源的同时申请新资源 | 一次性申请全部资源 |
| 不可剥夺 | 已获得的资源不能被强行收回 | 允许剥夺资源 |
| 循环等待 | 多个进程形成循环等待链 | 资源有序分配法 |
注意:
- 互斥条件通常难以破坏,因为部分资源天然不可共享。
- 资源有序分配法破坏的是循环等待条件。
死锁的四种处理方法¶
| 对比 | 死锁预防 | 死锁避免 | 死锁检测 | 死锁解除 |
|---|---|---|---|---|
| 核心思想 | 破坏必要条件 | 保持安全状态 | 检测是否发生死锁 | 解除已发生的死锁 |
| 典型方法 | 资源有序分配 | 银行家算法 | 资源分配图、检测算法 | 剥夺资源、撤销进程、进程回退 |
| 限制申请顺序 | 可以 | 不要求 | 不要求 | 不涉及 |
| 预知最大资源需求量 | 不要求 | 需要(银行家算法) | 不需要 | 不需要 |
| 分配前安全性检查 | 不需要 | 需要 | 不需要 | 不涉及 |
| 是否允许死锁发生 | 不允许 | 不允许 | 允许 | 发生后解除 |
死锁解除的三种方法¶
- 资源剥夺法:强制收回某些进程的资源,分配给其他进程。
- 撤销进程法:终止部分或全部死锁进程,释放资源。
- 进程回退法:让进程回退到之前保存的安全状态,释放部分资源。
易错点¶
- 死锁预防 ≠ 死锁避免:前者破坏必要条件,后者动态检查安全性。
- 银行家算法 = 试探性分配 + 安全性检查 + 决定是否正式分配。
- 死锁检测需要当前资源分配和请求信息,但不需要预知最大资源需求量。
- 不安全状态 ≠ 死锁状态,只表示存在发生死锁的可能。
- 死锁检测与解除通常配套使用,部分教材将二者合并为一种处理策略。
记忆:预防破条件,避免查安全,检测找死锁,解除靠剥夺、撤销、回退。
页面分配策略与页面置换策略¶
基本概念¶
- 固定分配:进程运行期间,分配的物理页框数量不变。
- 可变分配:进程运行期间,分配的物理页框数量可以动态变化。
- 局部置换:只能从当前进程占有的页框中选择被淘汰页面。
- 全局置换:可从系统范围内选择页框,包括其他进程占有的页框。
核心区别:页面分配看数量,页面置换看范围。
四种组合¶
| 组合 | 是否可行 | 原因 |
|---|---|---|
| 固定分配 + 局部置换 | 可以 | 页框数固定,只替换自己的页面 |
| 固定分配 + 全局置换 | 不可以 | 全局置换可能改变各进程的页框数量 |
| 可变分配 + 局部置换 | 可以 | 页框数可调整,置换仍限于本进程 |
| 可变分配 + 全局置换 | 可以 | 页框数可调整,也能从其他进程获取页框 |
易错点¶
- 固定分配只能搭配局部置换。
- 可变分配既可以搭配局部置换,也可以搭配全局置换。
- 局部置换不意味着页框数量固定。
- 固定分配与全局置换不能组合,这是 408 的经典考点。
文件描述符 fd 与打开文件表¶
fd 的本质¶
文件描述符(fd)是进程用于标识已打开文件的非负整数,本质上是进程文件描述符表的索引。
open():打开文件,成功返回 fd,失败返回 -1。read(fd, ...):通过 fd 读取文件数据。write(fd, ...):通过 fd 写入文件数据。close(fd):关闭文件描述符。
fd 不是文件地址,也不是数据缓冲区指针。
fd 与文件表的关系¶
fd → 进程文件描述符表 → 系统打开文件表 → FCB/inode → 文件数据
- 进程文件描述符表:保存 fd 与系统打开文件表项的关联。
- 系统打开文件表:记录文件偏移量、访问模式等。
- FCB/inode:记录文件元数据,用于定位文件数据。
同一进程中不同线程的 fd 归属¶
同一进程内的线程共享文件描述符表,fd 属于进程,而不是某个线程独占。
| 情况 | 结果 |
|---|---|
线程 A 调用 open() 得到 fd = 3 |
线程 B 也可以使用 fd = 3 |
线程 A、B 分别调用 open() 打开同一文件 |
通常得到 fd = 3、4,分别对应不同的打开文件表项 |
- 两次独立
open():建立不同的打开文件表项,文件偏移量独立。 - 多线程使用同一个 fd:共享同一个打开文件表项及文件偏移量。
- 一个线程执行
close(fd):其他线程也不能继续将该 fd 当作原来的有效描述符使用。
408 易错点¶
- 不同进程:通常各有自己的文件描述符表。
- 同一进程的不同线程:共享文件描述符表。
- 多次独立
open()同一文件:文件偏移量独立。 - 多线程使用同一个 fd:文件偏移量共享。
open()返回 fd,而不是数据缓冲区首地址。
记忆:fd 是进程的文件编号;线程共享 fd 表,但每次独立 open 通常创建新的打开文件表项。
管道通信(Pipe)¶
基本特点¶
- 本质:操作系统内核维护的有限容量缓冲区,用于进程间通信(IPC)。
- 先进先出(FIFO):先写入的数据先被读取。
- 半双工通信:一个管道只能单向传输,双向通信通常需要两个管道。
- 字节流通信:不保留消息边界。
- 读出即消费:数据被读取后从管道中移除。
管道读写规则¶
默认阻塞模式下:
| 管道状态 | 读写行为 |
|---|---|
| 管道为空,仍有写端 | 读进程阻塞 |
| 管道已满,仍有读端 | 写进程阻塞 |
| 管道有数据 | 可以读取,读后数据被移除 |
| 管道有空闲空间 | 可以写入 |
| 所有写端关闭且数据读完 | 读操作返回 0(EOF) |
| 所有读端关闭后继续写 | 触发 SIGPIPE 或返回 EPIPE 错误 |
同步与互斥¶
- 同步:管道为空时等待写入,管道满时等待读取。
- 互斥:操作系统协调多个进程对共享管道缓冲区的访问,防止并发操作破坏缓冲区状态。
- 写入原子性:不超过
PIPE_BUF的单次写入具有原子性保证;超过该大小可能与其他进程的写入交错。 - 一个管道可以有多个读进程和写进程对其操作,但是为了避免数据混乱,可以利用互斥锁机制来保证每次只有一个进程对管道进行读/写操作。
记忆:管道是内核缓冲区,FIFO、半双工、读出即消费;空读阻塞、满写阻塞,同时需要同步与互斥。