<操作系统真相还原>阅读记录

内存分段原因

若加载程序运行,不管其是内核程序,还是用户程序,程序中的地址若都是绝对物理地址,那该程序必须放
在内存中固定的地方,于是,两个编译出来地址相同的用户程序还真没法同时运行,只能运行一个。于是伟大的
计算机前辈们用分段的方式解决了这一问题,让 CPU 采用“段基址+段内偏移地址”的方式来访问任意内存 。 这
样的好处是程序可以重定位了,尽管程序指令中给的是绝对物理地址,但终究可以同时运行多个程序了 。

CPU 工作原理

CPU 的工作原理。 控制单元要取下一条待运行的指令,该指令
的地址在程序计数器 PC 中,在 x86CPU 上,程序计数器就是 CS: ip 。 于是读取 ip 寄存器后,将此地址送上地址
总线, CPU 根据此地址便得到了指令,并将其存入到指令寄存器皿中 。 这时候轮到指令译码器上场了,它根据
指令格式检查指令寄存器中的指令,先确定操作码是什么,再检查操作数类型,若是在内存中,就将相应操作数
从内存中取回放入自己的存储单元,若操作数是在寄存器中就直接用了,免了取操作数这一过程。 操作码有了,
操作数也齐了,操作控制器给运算单元下令,开工,于是运算单元便真正开始执行指令了 。 ip 寄存器的值被加上
当前指令的大小,于是 ip 又指向了下一条指令的地址。接着控制单元又要取下一条指令了,流程回到了本段开
头, CPU 便开始了日复一 日的循环,由于 CPU 特别不容易坏,所以唯一它停下来的条件就是停电。

寄存器为什么快呢?

原因是寄存器是使用触发器实现的,这也是一种
存储电路,工作速度极快,是纳秒级别的,

cs:ip

cs 寄存器用来存代码段段基址, E 寄存器
用来存储代码段段内偏移地址,同 cs 寄存器一样都是 16 位宽。

8086

8086 是 Intel 历史上第一个 x86 的CPU ,也就是自那以后的CPU 称为 286 、 386 、 486 、 586……即使是现在的奔腾也属于 x86
体系,道理是不变的,而且,用最简单的 8086CPU 学习,这才更容易理解和看透 CPU 运行机制。” 一番话彻底
打消了我的疑虑,自那以后我才理解 x86 中的 x 原来是个变量,它指代 Intel 所有 86 系列的产品 。
8086 的地址总线是 20 位宽

寄存器

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

保护模式下地址总线宽度是 32 位,段基址需要用 32 位地址来表示。
段界限表示段边界的扩展最值,即最大扩展到多少或最小扩展到多少。扩展方向只有上下两种。对于
数据段和代码段,段的扩展方向是向上,即地址越来越高,此时的段界限用来表示段内偏移的最大值。对
于战段,段的扩展方向是向下,即地址越来越低, 此时的段界限用来表示段内偏移的最小值

实模式下内存访问是采取“段基址:段内偏移地址”的形式,段基址要乘以 16 后再加上段内偏移地址。
实模式下寄存器都是 16 位的,如果段基址和段内偏移地址都为 16 位的最大值,即 OxFFFF: OxFF霄,最大
地址是 OxFFFFO呻xFFFF ,即 OxlOFFEF 。 由于实模式下的地址线是 20 位,最大寻址空间是 lMB ,即 OxOOOOO~
侃FFFFF 。 超出 IMB 内存的部分在逻辑上也是正常的,但物理内存中却没有与之对应的部分。为了让“段
基址:段内偏移地址”策略继续可用, CPU 采取的做法是将超过 1 岛也的部分自动回绕到 0 地址,继续从 0
地址开始映射。 相当于把地址对 l岛也求模 。

段描述符

这里写图片描述

段界限用 20 个二进制位来
表示。只不过此段界限只是个单位量,它的单位要么是字节,要么是 4阻,这是由描述符中的 G 位来指
定的。最终段的边界是此段界限值*单位,故段的大小要么是 2 的 20 次方等于山田,要么是 2 的 32 次方
( 4阳等于 2 的 12 次方, 12+20=32 ) 等于 4GB 。

全局描述符表 GDT 只是一片内存区域,里面每隔 8 字节便是一个表项,即段描述符。

80286 拥有 24 位地址线,其寻址空间是 16MB 。
当时有一些 ISA 设备要用到地址 15~但以上的内存作为缓冲区,也就是此缓冲区为山田大小,所以硬件系统
就把这部分内存保留下来,操作系统不可以用此段内存空间。保留的这部分内存区域就像不可以访问的黑洞,
这就成了内存空洞 memory_hole 。现在虽然很少很少能碰到这些老 ISA 设备了,但为了兼容,这部分空间还是
保留下来,只不过是通过 BIOS 选项的方式由用户自己选择是否开启。

在我们的机器上即使只有 512MB 的内存,每个进程自己的内存空间也是 4GB ,

分页

CPU 允许在描述符表中已注册的段不在内存中存在,这就是它提供给软件使用的策略,我们利用它实现段式内存管理。如果该描述符中的 P 位为 1 ,表示该段在内存中存在。访问过该段后, CPU 将段描述符中的 A 位置 l ,若b币丘来刚访问过该段。相反,如果 P 位为 0,说明内存中并不存在该段,这时候 CPU 将会抛出个 NP (段不存在)异常,转而去执行中断描述符表中 NP 异常对应的中断处理程序,此中断处理程序是操作系统负责提供的,该程序的工作是将相应的段从外存(比如硬盘)中载入到内存,并将段描述符的 P 位置 1 ,中断处理函数结束后返回, CPU 重复执行这个检查,继续查看该段描述符的 P 位,此时已经为 1 了,在检查通过后,将段描述符的 A 位置 1 。

查找页表的工作也是由硬件完成的
这里写图片描述

分页机制的思想是:通过映射,可以使连续的线性地址与任意物理内存地址相关联,逻辑上连续的线性地址其对应的物理地址可以不连续 。
页大小是4kb
这里写图片描述

分页机制建立在分段机制之上,与其脱离不了干系,即使在分页机制下的进程也要先经过逻辑上的分段才行,每加载一个进程,操作系统按照进程中各段的起始范围,在进程自 己的 4GB 虚拟地址空间中寻找可用空间分配内存段,此虚拟地址空间可以是页表,也可以是操作系统维护的某种数据结构,总之此阶段的分配是逻辑上的,并没有真正写入物理内存。在分页机制下,分配情形如图 5-8 中所示的虚拟地址空间中的代码段和数据段。代码段和数据段在逻辑上被拆分成以页为单位的小内存块。这时的虚拟地址虚如其名,不能存放任何数据。接着操作系统开始为这些虚拟内存页分配真实的物理内存页,它查找物理内存中可用的页,然后在页表中登记这些物理页地址,这样就完成了虚拟页到物理页的映射,每个进程都以为自己独享 4GB 地址空间。

虚拟地址的高 20 位可用来定位一个物理页,低 12 位可用来在该物理页内寻址。

这里写图片描述

这里写图片描述

这里写图片描述

二级页表地址转换原理是将 32 位虚拟地址拆分成高 10 位、中间 ro-位、低 12 位三部分,它们
的作用是:高 10 位作为页衰的索引,用于在页目录表中定位一个页目录项 PDE ,页目录项中有页表物理地址,
也就是定位到了某个页表。中间 10 位作为物理页的索引,用于在页表内定位到某个页表项 PTE ,页表项中有分
配的物理页地址,也就是定位到了某个物理页。低 12 位作为页内偏移量用于在已经定位到的物理页内寻址。

页表的设计是要根据内存分布情况来决定的,我们也学习 Linux 的作法,在用户进程 4GB 虚拟地址空间的高 3GB 以上的部分划分给操作系统, 0~3GB 是用户进程自己的虚拟空间 。为了实现共享操作系统,让所有用
户进程 3GB~4GB 的虚拟地址空间都指向同一个操作系统,也就是所有进程的虚拟地址 3GB~4GB 本质上都是指向的同一片物理页地址,这片物理页上是操作系统的实体代码。

一个页表表示的内存容量是 4MB

内核

拿 Linux 来说,它的系统调用是先往 eax寄存器中写入系统调用号,然后通过 Ox80 中断来实现的

汇编指令与机器指令几乎是一对一的,即一名汇编代码只对应一句具体的机器码,不会有更多对应的
选择,所以可以认为汇编指令就是机器指令。 C 语言的编译过程是先将 C 语言代码转换成汇编代码,然后
再将汇编代码转换成机器指令。所以用 C 语言写出来的程序,最终可以转换成对应的一句或多句汇编指
令。

生成 C 语言程序的过程是这样的。

先将源程序编译成目标文件(由 c 代码变成汇编代码后,再由汇编代码生成二进制的目标文件),再将目标文件链接成二进制可执行文件。

人们常说的汇编语言比 C 语言快,并不是汇编语言本身有多快(它也要变成机器指令后才能上 CPU 运行),而是汇编语言对应的机器指令是一对一的,简单,直接,可依赖,而 C 语言生成的机器指令是一对多的,复杂,间接,略冗余。

BIOS 调用 mbr, mbr 的地址是 Ox7cOO, mbr 调用 loader, loader 的地址是 Ox900 。这两个地址是固定的,

这里写图片描述

elf 格式的二进制文件

程序中段的大小和数量是不固定的,节的大小和数量也不固定,因此需要为它们专门找个数据结构来描述它们,这个描述结构就是程序头表( program header table )和节头表( section header table )
由于程序中段和节的数量不固定,程序头表和节头表的大小自然也就不固定了,而且各表在程序文件中的存储顺序自然也要有个先后,故这两个表在文件中的位直也不会固定。因此,必须要在一个固定的位置,用 一 个固定大小的数据结构来描述程序头表和节头表的大小及位置信息,这个数据结构便是 ELF header ,它位于文件最开始的部分,并具有固定大小,
ELF header 是个用来描述各种“头”的“头飞程序头表和节头表中的元素也是程序头和节头,
这里写图片描述

TLB

处理器准备了一个高速缓存,可以匹配高速的处理器速率和低速的内
存访问速度,它专门用来存放虚拟地址页框与物理地址页框的映射关系,这个调整缓存就是 TLB ,即Translation Lookaside Buffer,俗称快表
这里写图片描述

movsb、 movsw 、 movsd 这三条指令是将 DS: [E]SI
指向的地址处的 l 、 2 或 4 个字节搬到 ES :胆]DI 指向的地址处, 16 位环境下源地址指针用 SI 寄存器,目的地址指针用 DI 寄存器, 32 位环境下源地址则用 ESI ,目的地址则用 EDI

rep 指令 即指令是repeat重复的意思,该指令是按照民x 寄存器中指定的次数重复执行后面的指定的指令,每执行一次, ecx 自减 1 ,直到 ecx 等于 0 时为止,

这里写图片描述

特权级

这里写图片描述

内核

这里写图片描述

对于 in 指令,如果源操作是 8 位寄存器,目的操作数必须是al 如果源操作数是 16 位寄存器,目的操作数必须是ax。

中断

外部中断是指来自 CPU 外部的中断,而外部的中断源必须是某个硬件,
这里写图片描述

在 CPU 上运行的程序都是串行的,所有任务,包括中断
处理程序都是一个接一个在 CPU 上运行的,所有任务都共享
同一个 CPU, CPU 在各个任务间不断切换才实现了并发。

操作系统是中断驱动的,中断发生后会执行相应的中断处理程序,我们希望 CPU 中断响应的时间越
短越好,这样便能响应更多设备的中断。但是中断处理程序还是需要完整执行的,不能光为了提高中断响
应效率而只执行部分中断处理程序 。 于是,把中断处理程序分为上半部和下半部两部分,把中断处理程序
中需要立即执行的部分(分分钟不能耽误的部分)划分到上半部,这部分是要限时执行的,所以通常情况
下只完成中断应答或硬件复位等重要紧迫的工作。而中断处理程序中那些不紧急的部分则被推迟到下半部
中去完成。

不可屏蔽中断是通过 NMI 引脚进入 CPU 的,它表示系统中发生了致命的错误,它等同于宣布:计算机的运行到此结束了。

内部中断可分为软中断和异常。
int3 是调试断点指令,其所触发的中断向量号是 3,我们用 gdb 或 bochs 调试程序时,实际上就是调试器 fork 了一个子进程,子进程用于运行被调试的程序。调试器中经常要设置断点,其原理就是父进程修改了子进程的指令,将其用 int3指令替换,从而子进程调用了 int3 指令触发中断。

8259A 的作用是负责所有来自外设的中断,其中就包括来自时钟的中断,

每个独立运行的外部设备都是一个中断源,它们所发出的中断,只有接在中断请求 CIRQ: Interrupt
ReQuest )信号线上才能被 CPU 大神知晓,这也就是大家在开机时,电脑屏幕上会看到的 IRQI … IRQn,
这些都是为外部设备所分配的中断号 。

线程

I. 在用户空间中实现线程

优点
  • 在用户空间中实现线程的好处是可移植性强,由于是用户级的实现,所以在不支持线程的操作系统上也可以写出完美支持线程的用户程序。
  • 线程的调度算法是由用户程序自己实现的,可以根据实现应用情况为某些线程加权调度。
    将线程的寄存器映像装载到 CPU 时,可以在用户空间完成,即不用陷入到内核态,这样就免去了进入内核时的入战及出战操作。
缺点
  • 如果在用户空间中实现线程,用户线程就要肩负起调度器的责任,因此除了要实现进程内的线程调度器外,还要自己在进程内维护线程表,
  • 进程中的某个线程若出现了阻塞(通常是由于系统调用造成的),操作系统不知道进程中存在线程,它以为此进程是传统型进程(单线程进程),因此会将整个进程挂起,即进程中的全部线程都无法运行
  • 如果在用户空间中实现线程,但凡进程中的某个线程开始在处理器上执行后,只要该线程不主动让出处理器,此进程中的其他钱程都没机会运行
  • 最后,线程在用户空间实现,和在内核空间实现相比,只是在内部调度时少了陷入内核的代价,确实相当于提速,但由于整个进程占据处理器的时间片是有限的,这有限的时间片还要再分给内部的线程,所以每个线程执行的时间片非常非常短暂,再加上进程内线程调度器维护线程表、运行调度算法的时间片消耗,反而抵销了内部调度带来的提速 。

    1. 在内核空间中实现线程
优点
  • 相比在用户空间中实现线程,内核提供的线程相当于让进程多占了处理器资源,
  • 另一方面的优点是当进程中的某一线程阻塞后 , 由于线程是由内核空间实现的,操作系统认识线程,所以就只会阻塞这一个线程,此线程所在进程内的其他线程将不受影响,这又相当于提速了 。
缺点
  • 缺点是用户进程需要通过系统调用陷入内核,这多少增加了 一些现场保护的枝操作

内存管理系统

但程序〈进程、内核线程)在运行过程中也有申请内存的需求,这种动态申请内存一般是指在堆中申请内存,操作系统接受申请后,为进程或内核自己在堆中选择一空闲的虚拟地址,并且找个空闲的物理地址作为此虚拟地
址的映射,之后把这个虚拟地址返回给程序。

对于所有任务(包括用户进程、内核〉来说,官们都有各自 4GB 的虚拟地址空间,因此需要为所有任务都维护它们自己的虚拟地址池,即一个任务一个 。

32 位虚拟地址的转换过程。
( I )高 10 位是页目录项 pde 的索引,用于在页目录表中定位 pde ,细节是处理器获取高 10 位后自动
将其乘以 4 ,再加上页目录表的物理地址,这样便得到了 pde 索引对应的 pde 所在的物理地址,然后自动
在该物理地址中,即该 pde 中,获取保存的页表物理地址(为了严谨,说的都有点拗口了)。
(2 )中间 10 位是页表项 pte 的 索引,用于在页表中定位 pte 。细节是处理器获取中间 10 位后自动将
其乘以 4 ,再加上第一步中得到的页表的物理地址,这样便得到了 pte 索引对应的 pte 所在的物理地址,
然后自动在该物理地址(该 pte )中获取保存的普通物理页的物理地址。
(3 )低 12 位是物理页内的偏移量 ,页大小是 4KB, 12 位可寻址的范围正好是 4阻,因此处理器便直
接把低 12 位作为第二步中获取的物理页的偏移量,无需乘以 4c 用物理页的物理地址加上这低 12 位的和
便是这 32 位虚拟地址最终落向的物理地址 。
32 位地址经过以上三步拆分,地址最终落在某个物理页内 。

内核线程

进程与线程的区别是它们是否独自拥有地址空间,也就是是否拥有页表,程序的状态都是通用的

函数在执行前,如果该函数有参数的话,调用者一定会按照调用约定,
离地址先把参数压到战中。

这里写图片描述
这里写图片描述

内部时钟

内部时钟是指处理器中内部元件,如运算器、控制器的工作时序,主要用于控制、同步内部工作过程的步调。内部时钟是由晶体振荡器产生的,简称晶振,它位于主板上,其频率经过分频之后就是主板的外频,处理器和南北桥之间的通信就基于外频。 Intel 处理器将此外频乘以某个倍数(也称为倍频)之后便称为主频 。 处理器取指令、执行指令中所消耗的时钟周期,都是基于主频的 。 内部时钟是由处理器固件结构决定的,在出厂时就设定好啦,无法改变。处理器内部元件的工作速度是最快的,所以内部时钟的时间单位粒度比较精细,通常都是纳秒( ns )级的。

外部时钟

外部时钟是指处理器与外部设备或外部设备之间通信时采用的 一种时序,比如 IO 接口和处理器之间在 AID 转换时的工作时序、两个串口设备之间进行数据传输时也要事先同步时钟等 。 外部设备的速度对于处理器来说就很慢了,所以其时钟的时间单位粒度较大, 一般是毫秒( ms )级或秒 ( s )级的 。

这里写图片描述

信号量

在二元信号量 中, down 操作就是获得锁, up 操作就是释放锁

键盘输入

键盘是个独立的设备,在它内部有个叫作键盘编码器的芯片,通常是 Intel 8048 或兼容芯片,它的作用是:每当键盘上发生按键操作,它就向键盘控制器报告哪个键被按下,按键是否弹起。
这个键盘控制器可并不在键盘内部,它在主机内部的主板上,通常是 Intel 8042 或兼容芯片,它的作用是接收来自键盘编码器的按键信息,将其解码后保存,然后向中断代理发中断,之后处理器执行相应这里写图片描述的中断处理程序读入 8042 处理保存过的按键信息 。

当某个键被按下时, 8048 把这个键对应的数值发送给 8042, 8042
根据这个数值便知道是哪个键被按下了。

一 个键的状态要么是按下,要么是弹起,因此一个键便有两个编码,按键被按下时的编码叫通码,也就是表示按键上的触点接通了内部电路,使硬件产生了一个码,故通码也称为 makecode 。按键在被按住不松手时会持续产生相同的码,直到按键被松开时才终止,因此按键被松开弹起时产生的编码叫断码,也就是电路被断开了,不再持续产生码了,故断码也称为 breakcode 。一个键的扫描码是由通码和断码组成的。

扫描码是硬件提供的编码集, ASCII 是软件中约定的编码集,这两个是不同的编码方案。

大多数’情况下第一套扫描码中的通码和断码都是 1 字节大小 。 您看,表 10-1 中的通码和断码,它们的关系是:断码= Ox80 +通码 。 顺便说一句,. 在第二套键盘扫描码中, 一般的通码是 1 宇节大小,断码是在通码前再加 1 字节的 OxFO ,共 2 字节,我们的 8042 工作之一就是根据第二套扫描码中通码和断码的关系将它们解码,然后按照第一套扫描码中通码和断码的关系转换成第一套扫描码。

这里写图片描述

TSS 任务状态段

TSS 是 Task State Segment 的缩写,即任务状态段,
TSS 就是任务的代表, CPU 用不同的 TSS 区分不同的任务,因此任务切换的本质就是 TSS 的换来换去。
TSS 和其他段一样,本质上是一片存储数据的内存区域, Intel 打算用这片内存区域保存任务的最新状态(也就是任务运行时占用的寄存器组等),因此它也像其他段那样,需要用某个描述符结构来“描述”它,这就是 TSS 描述符, TSS 描述符也要在 GDT 中注册

这里写图片描述

在 CPU 中有一个专门存储 TSS 信息的寄存器,这就是TR 寄存器,它始终指向当前正在运行的任务,因此,“在 CPU 眼里”,任务切换的实质就是 TR 寄存器指向不同的 TSS

这里写图片描述

系统调用

Linux 系统调用是用中断门来实现的,通过软中断指令 int 来主动发起中断信号。
Linux 只占用一个中断向量号,即 Ox80 ,处理器执行指令 int0x80 时便触发了系统调用。

每个进程都拥有独立的虚拟地址空间,本质上就是各个进程都有单独的页表,页表是存储在页表寄存器 CR3 中的, CR3 寄存器只有 1 个,因此,不同的进程在执行前,我们要在 CR3 寄存器中为其换上与之配套的页表,从而实现了虚拟地址空间的隔离。

咱们使用的是二级页表,
一 个虚拟地址在页表中对应两个数据项:页目录项 pde 和页表项 pte 。一个 pde 记录一个页表的物理地址,
该页表的数据占用 1 个物理页框的空间, l 个 pte 是 4 字节,因此页表中包含 1024 个 pte ,每个 pte 记录
的是最终与虚拟地址映射的物理页框 。 如何清除虚拟地址?把整个 pte 清 O ?行是行,简单省事,但显得
有些“粗暴飞只要把 pte 中的 P 位置为 0 就可以了,该位表示 pte 指向的物理页框的数据是否已在该物理
页框中, CPU 只要检测到 P 位为 0 ,就会认为该 pte 无效,根本不会关心 pte 所指向的物理页框的地址是
否属于可访问的物理内存的范围 。 P 位的实际意义是当可用物理内存较少时,可以将 pte 指向的物理页框
中的数据转储到外存上,这样就省出了 4阳的物理内存空间。将物理页中的数据存储到外存的同时,需
要将 pte 的 P 位置为 0 。 这样在下次访问该 pte 对应的虚拟地址时,由于 pte 的 P 位为 O, CPU 会抛出阳gefault
缺页异常,我们可以在处理 pagefault 异常的中断处理程序中将之前保存到外存的页框数据再次载入到物
理内存中,该物理内存可以是原来的物理页,也可以是新的物理页,这取决于实际物理内存的使用情况,
然后把目标物理页地址更新到 pte 中,井将 P 位置为 1 。 pagefault 中断处理程序退出后, CPU 自动会再次
访问引起此 pagefault 的虚拟地址,这次发现 pte 的 P 位为 l ,从而访问正常,这就是 CPU 原生支持的页
式虚拟地址管理策略,话说 Linux 虚拟地址管理也是利用 P 位和 pagefault 异常实现的。

硬盘

分区是逻辑上划分磁盘空间的方式,归根结底是人为地将硬盘上的柱 面扇区划分成不同的分组,每个分组都是单独的分区。各分区都有“描述符”来描述分区本身所在硬盘上的起止界限等信息,在硬盘的MBR 中有个 64 字节“固定大小”的数据结构,这就是著名的分区表,分区表中的每个表项就是一个分区的“描述符”,表项大小是 16 字节,因此 64 字节的分区表总共可容纳 4 个表项,这就是为什么硬盘仅支持 4 个分区的原因。

其实分区表的长度并不是由结构本身限制的,而是由其所在的位
置限制的,它必须存在于 MBR 引导扇区或 EBR 引导扇区中。在这 512 字节中,前 446 字节是硬盘的参数和引导程序,然后才是 64 宇节的分区表,最后是 2 字节的魔数 55础。随着计算机天长地久的发展,还真别小看这个扇区 , 很多程序已经对它有依赖了,尤其是一些引导型程序(如 BIOS ),都会在该扇区的512 字节中的固定位置读取关键数据,

分区表中共 4 个分区,哪个做扩展分区都可以,扩展分区是可选的,但最多只有 1 个,其余的都是主分区。在过去没有扩展分区时,这 4 个分区都是主分区 i 为了兼容 4 个主分区的情况,扩展分区中的第 1 个逻辑分区的编号从 5 开始。

磁盘分区表( Disk Partition Table )简称 DPT ,是由多个分区元信息汇成的表,表中每一个表项都对应一个分区,主要记录各分区的起始扇区地址,大小界限等 。

岛ffiR, MBR (MainBootR四ord )即主引导记录,它是一段引导程序,其所在的扇区称为主引导扇区,该扇区位于 0 盘。道 1 扇区(物理扇区编号从 1 开始,逻辑扇区地址 LBA 从 0 开始),也就是硬盘最开始的扇区,扇区大小为 512 宇节,这 512 字节内容由三部分组成。
( I ) 主引导记录 MBR 。
(2 )磁盘分区表 DPT 。
(3 )结束魔数 55AA ,表示此扇区为主引导扇区,里面包含控制程序。
岛ffiR 引导程序位于主引导扇区中偏移 0~OxlBD 的空间,共计 446 字节大小,这其中包括硬盘参数及部分指令(由 BIOS 跳入执行),它是由分区工具产生的,独立于任何操作系统磁盘分区表位于主引导扇区中偏移 Ox I BE ~ Ox I FD 的空间,总共 64 字节大小,每个分区表项是 16字节,因此磁盘分区表最大支持 4 个分区 。魔数 55AA 作为主引导扇区的有效标志,位于扇区偏移 OxlFE~Ox I FF ,也就是最后 2 个字节。
以上这三部分便是 MBR 的主要结构。

这里写图片描述

文件系统

硬盘是低速设备,其读写单位是扇区,为了避免频繁访问硬盘,操作系统不会有了一扇区数据就去读写一次磁盘,往往等数据积攒到“足够大小”时才一 次性访问硬盘,这足够大小的数据就是块,硬盘读写单位是扇区,因此一 个块是由多个扇区组成的,块大小是扇区大小的整数倍。

块是文件系统的读写单位,因此文件至少要占据一 个块,当文件体积大于 l 个块时,文件肯定被拆分成多个块来存储,

FAT
FAT 称为文件分配表,在此文件系统中存储的文件,其所有的块被用于链式结构来组织,在每个块的最后存储下一个块的地址,从而块与块之间串联到 一 起,这样一来,文件可以不连续存储,文件中的块可以分布在各个零散的空间中,有效地利用了存储 空 间,或者说是提升了磁盘的利用率,相当于节省了空间。通过链式结构来组织文件的弊端是当访问文件中的某个块时,必须
要从头开始遍历块结点,
文件组织形式是对各个文件而言的, FAT 文件系统中每个文件都有这么个单独的链式结构来组织、跟踪文件的所有块。

inode
采用索引结构的文件系统,文件中的块依然可以分散到不连续的零散空间中,保留了磁盘高利用率的优点,更重要的是文件系统为每个文件的所有块建立了一个索引表,索引表就是块姻止知且,每个知且元素就是块的地耻,如且元素下标是文件块的索引,第 n 个知且元素指向文件中的第 n 个块,这样访问任意一个块的时候,只要从知|表中获得块地址就可以了,速度大大提升。包含此索引表的索引结构称为 ir帅,即 ind阻 node,索引结点,用来
索引、跟踪一个文件的所有块。

用索引结构的缺点是索引表本身要占用 一 定的存储空间,文件要是很大时,块就比较多,索引表项就要跟着增多

这里写图片描述

在 Linux 中,目录和文件都用 inode 来表示,因此目录也是文件,只是目录是包含文件的文件 。

( 1 )每个文件都有自己单独的 inode, inode 是文件实体数据块在文件系统上的元信息。
(2 )所有文件的 inode 集中管理,形成 inode 数组,每个 inode 的编号就是在该 inode 数组中的下标。
(3) inode 中的前 12 个直接数据块指针和后 3 个间接块索引表用于指向文件的数据块实体。
(4 )文件系统中并不存在具体称为“目录”的数据结构,同样也没有称为“普通文件”的数据结构,统一用同一种 inode 表示。 inode 表示的文件是普通文件,还是目录文件,取决于 inode 所指向数据块中的实际内容是什么,即数据块中的内容要么是普通文件本身的数据,要么是目录中的目录工页。
(5 )目录项仅存在于 inode 指向的数据块中,有目录项的数据块就是目录,目录项所属的 inode 指向的所有数据块便是目录。
(6 )目录项中记录的是文件名、文件 inode 的编号和文件类型,目录项起到的作用有两个, 一 是粘合文件名及 inode ,使文件名和 inode 关联绑定,二是标识此 inode 所指向的数据块中的数据类型(比如是普通文件,还是目录,当然还有更多的类型)。
(7) inode 是文件的“实质”,但它并不能直接引用,必须通过文件名找到文件名所在的目录项,然后从该目录项中获得 inode 的编号,然后用此编号到 inode 数组中去找相关的 inode ,最终找到文件的数据块。

这里写图片描述

在文件系统的设计中,根目录所在数据块的地址是被“写
死”的,查找任意文件时,都直接到根目录的数据块中找相关的目录项,然后递归查找,最终可以找到任意子目录中的文件。

文件系统是针对各个分区来管理的, inode代表文件,因此各分区都有自己的 inode 数组 。
这里写图片描述

超级块是文件系统元信息的“配置文件”,它是在为分区创建文件系统时
创建的,所有有关文件系统元信息的配置都在超级块中,因此超级块的位置和 空闲块起始地址大小不能再被“配置”了,必须是固定的,它被固定存储在各分区的第 2 个扇区,通常是占用一个扇区的大小,具体大小与实际文件系统类型为准。

这里写图片描述

Windows 系统通常是 fat32 或 ntfs, Linux 系统通常是 ext2 或 ext3 ,

读写文件的本质是 z 先通过文件的 inode 找到文件数据块的扇区地址,随后读写该扇区,从而实现了文件的读写。

inode用于描述文件存储相关信息,文件结构用于描述“文件打开”后,文件读写偏移量等信息。文件与 inode一一对应,一个文件仅有一个 inode , 一个 inode 仅对应一个文件。一个文件可以被多次打开,因 此一个inode 可以有多个文件结构,多个文件结构可以对应同一个 inode.

文件描述符 和 inode关系

这里写图片描述

程序是指在磁盘上存储的文件,是静态的,进程是指程序被加载到内存后,在内存中运行中的程序映像,简而言之,进程就是运行的程序。

fork 就是相当于同一个程序多次加载执行,因此在内存中产生了多个同名进程。

在 Linux 中, init 是用户级进程,它是第一个启动的程序,因此它的 pid
是 1 ,后续的所有进程都是它的孩子,故 init 是所有进程的父进程,所以它还负责所有子进程的资源回收,

管道是用于存储数据的中转站,当某个进程往管道中写入数据后,该数据很快就会被另 一个进程读取,之后可以用新的数据覆盖老数据,继续被别的进程读取,因此管道属于临时存储区,其中的数据在读取后可
被清除。

管道

管道分为两种:匿名管道和命名管道,从概念上就可以知道,这是按照管道是否有名称来划分的。以上说的管道便是匿名管道,它没有名字。由于没有名字,匿名管道在创建之后只能通过内核为其返回的文件描述符来访问,此管道只对创建它的进程及其子进程可见,对其他进程不可见,因此除父子进程之外的其他进程便不知道此管道的存在,故匿名管道只能局限用于父子进程间的通信。
有名管道是专门为解决匿名管道的局限性而生的,在 Linux 中可以通过命令 mkfifo 来创建命名管道,成功创建之后便会在文件系统上存在个管道文件,这使得该管道对任何进程都“可见飞因此多个进程即
使没有父子关系也都通过访问该管道文件进行通信。

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值