磁盘文件管理与库

磁盘抽象为一个一维数组(LBA寻址)后,需要再理解抽象的分区:

前置知识:

硬件从磁盘读文件,是以1个扇区为基本单位(512字节)交换数据的->磁盘读取基本单位为扇区

操作系统从磁盘读文件,以1到4kb为一个基本单位读-(其实内存,文件也被操作系统划分为了块为单位的交换单位)>避免频繁IO,增速

实现:磁盘默认支持LBA/CHS(两者能互转)。块号*块内扇区数+几个多余的扇区就能精确定位磁盘某扇区,找它要数据。--》再次将磁盘抽象为块为单位的数组

一。操作系统对大磁盘的管理

磁盘大容量直接管理麻烦,一个部分崩了影响大->将磁盘拆分多个分区管理  管理一个分区的方法用于多个,管理一个等于管理好多个

分区很大,管理麻烦,一个部分崩了影响大->将分区拆为多个组管理,管理一个组,方法适用于多个组

---->分治思想:开分身管理 独立部分

分治好处:

  • 性能优化:不同类型文件放不同地方,集中管理,方便找(对机械硬盘(HDD),将高负载分区(如数据库分区)放在磁盘外圈(转速更快)可提升读写速度(SSD因无机械结构影响较小),且节省资源:不需要频繁刷新的就不频繁刷了

  • 数据安全:1.一部分数据丢了或者部分损坏,只需要针对这独立的部分,不需要整体搞 2.重要的放一个分区,提升需要权限,病毒不易入侵

图为磁盘->多分区->多组结构,BootBlock为磁盘第一个扇区(对磁盘是第0号扇区),装了系统启动引导部分,各种表项,mbr等。

磁盘每个组的管理方式:

前提:文件在磁盘的组织方式:内容+属性(inode结构体),其中inode不包含文件名,一般inode占128或256字节。内含blocks数组,根据内数寻找文件对应的块。inode编号并不是随机分配的,其结构隐含了 所属块组 和 组内偏移量 的信息,根据inode编号找到对应Inode位置


组的划分:

1.inodeTable:多个块存储inode

2.DataBlocks:以块为基本单位(多为4kb)存储文件的内容,大部分组的空间分配给它

----->文件属性和内容分开存储

3.InodeBitmap:记录inodeTable使用信息,申请inode创建,看bitmap,空闲的直接置1,在inodeTable某个256字节(inode大小)为基础的位置创建。

提问:系统不是以块或者扇区为单位吗,怎么做到一个位一个位写的:整个块位单位读出写入。

4.BlockBitmap:记录数据块使用信息

5.GDT:块组描述符,管理inode和datablock总共数量记录和已经使用信息,让进来文件判断能不能在这个组存

6.SuperBlock:如图,并且不是每个组一个,多个组几个,用于备份,防止文件系统挂掉

向磁盘申请一个文件写入字符串步骤:

删除一个文件步骤:

inodebitmap:位置置0,blockbitmap:位置置0.

---->文件删了可能恢复,根据删除记录删

格式化本质:

inodebitmap,blockbitmap清零,gdt,superblock初始化。

三.理解文件,目录,几个问题

一.怎么初始化组的大小的

1.文件系统内inode和block数量固定:组内数量同样固定,inode与block以一定比例固定数量,如一个inode10个块

2.一个分区一套inode(一个分区一个文件系统),不能跨文件系统

3.分配inode只要知道起始inode(记录在gdt内),分配block只需要知道起始block

4.分配了组的inode,block数量,inodetable,blockgroup,inodebitmap,blockbitmap,gdt,superblock大小都已经确定,整个组就确定了

二.如何分配一个inode的编号的

去inodebitmap找个空位置,其顺序+其组的inode起始值-->局部到全局

三.如何分配块号的

同上。

四.inode与块关联

inode里记录块的全局编号,这样使文件可以分多组存大文件

五.怎么通过inode编号找inode与其对应块

inode编号去每个组的区间找,属于的区间的起始inode编号找到inodebitmap对应位置,再找到inodetable找inode,再找记录的blocks找块的绝对编号,再找到块。

六,怎么删文件

先inode查找,找到bitmap置0,找到Inode找blocksbitmap置0,更新GDT

七.如何新增

八.inode和block的真实映射

inode里的block数组固定的吗多大?不会不够吗?

----------------------------------------------目录操作-----------------------------------------------------------------------

九.怎么从文件名找inode编号

linux中目录也有inode编号,可见目录也是文件,内部存了文件名与inode编号的映射关系。

引申:目录的rwx

目录没有r权限不能知道其里面有什么文件:因为没有目录的读权限,不能读里面存的文件名等。

没有w权限不能向里面加文件:不能向目录文件内写入映射关系。

没有x权限不能进入目录:本质是打不开目录文件。

十.为什么不用文件名而用inode编号?(同用户进程有uid,pid)

字符比较查找麻烦,效率低!

十一.ll干了什么

十二。为什么任何一个文件都要有路径(同为什么进程要有cwd)

为了做路径解析,任何文件都有文件名,其Inode在上层路径(除了根目录,根目录的文件名与inode映射是写死的)

十三.如何找接近的路径的文件

全部路径再解析太麻烦--->内存级路径缓存

缓存结构:多叉树 struct struct_dentry

符合系统文件结构,下一次访问相关路径只需要先在路径缓存中找,找到部分再去磁盘找剩下的。

(这也是find指令找第一次慢,后来就快了的原因)

相关操作:LRU最近最少访问的路径去除

-----------------------------------------进程文件整体认知----------------------------------------------------------------

十四,

file含path加载dentry加载inode

---------------------------------------------------------------------------------------------------------------------------------

如何确定在哪个分区

挂载的本质:通过修改 dentry 和 vfsmount 的关联关系,实现路径解析的“重定向”

四.软硬链接

一.软链接

本质:一个独立文件,保存对应文件的路径,等同于快捷方式

作用:1.便捷访问(重定位):在/usr/bin路径下建立自己程序的软连接,就可以不限路径访问。或者在浅层路径建立深层路径文件的软链接,在浅层就能访问,不用进深层。

二.硬链接

本质:对应文件在文件夹内对文件名(不同)和inode(同)的新映射

补充:

1.硬链接数:权限后的数字是指Inode与文件名映射数量,只有映射数量归0,系统才去删文件。

2.   .和..

.和..其实是对当前路径/上层路径的硬链接(也是为什么空文件夹的数字是2,文件是1),作用是方便定位此层路径的文件,且快速访问简化路径操作(避免拼写长路径)。可以通过目录的数字看内部第一层有多少目录

作用:1..和..简化路径操作

2.方便备份,不需要拷贝

注意:不允许对目录硬链接:避免环状路径(系统的.和..被特殊处理了)

为什么只有硬链接不允许链接目录?

因为软链接在查找时不会混淆,权限第一位是l,硬链接显示就是一个目录,易混淆(权限字段的第一位字符代表文件类型(而非权限))

五.动态库和静态库(续)

1.打包静态库步骤

1.创建.o目标文件

2.用ar -rc lib库名.a ...o  ..o

2.安装步骤:

一。将库放到默认目录

1.头文件放到/usr/include目录下,gcc默认从这找

2.把静态库放到/lib64目录下,gcc默认找库路径

3.将自己的文件和使用的头文件方法所在库文件文件名-l链接(因为是第三方库,gcc,g++不会自动找库)

结果:

或者

二。指明目录给gcc找库

三。指明目录给gcc找头文件

-I目录

附加:为什么不同平台需要用不同版本的库?

答:因为不同平台的.o文件不一样(有的程序底层系统调用,如printf等),库又是由.o来的。

1.打包动态库步骤

一。将.c文件以创建与位置无关码的方式编译为.o

二。将.o文件用gcc -shared选项创建.so动态库

2.使用步骤

1.向系统告知.so动态库位置(因为之前g++ -L 是向编译器指定,但动态库的使用是动态加载的,系统不知道,直接和你的程序链接会显示找不到)

1.将.so库拷到/lib64系统默认寻找位置

2.修改系统的环境变量,让系统在指定目录找你的库 LD_LIBRARY_PATH

  • 临时变量 → 直接用 export

  • 永久变量 → 写入配置文件并 source 或重新登录。

3.系统路径建立软链接

为什么是软链接:

  • 避免硬链接,因为动态库可能需要更新,软链接更易于维护。

4.

提示:编译时,编译器默认先找动态库,没有再找动态库,要指定可以gcc编译命令中加-static。

ldd 名 可以查可执行程序的动态库

3.为什么要打包使用动态静态库

  • 简化依赖管理:将多个目标文件(object files)打包成一个库文件,便于管理和分发

  • 减少文件数量:避免项目目录被大量零散的.o文件污染

  • 链接效率(静态库):链接器处理一个库文件比处理多个.o文件更高效

  • 独立更新(动态库):可以更新动态库而不需要重新编译依赖它的程序

  • 减小可执行文件体积(动态库):特别是使用动态库时

六.程序调用动态库从磁盘到虚拟地址空间的加载

问题:

1.1问:动态库和程序的elf是不同的两个部分(虚拟地址不统一),怎么找到库函数的?

1.2问:代码区直接库名:偏移量,库和节都载入内存,将库名替换为物理地址不就能找到了吗,为什么不用?

答:1.代码区载入,是不允许修改的(编译器编译完就加了权限,保护代码区)。2.可以通过在elf添加GOT节,存储库名和偏移量,GOT载入内存,库载入内存时就将GOT条目中库名改为实际物理地址,这样,执行时代码区指令去GOT找指令,就能找到函数实际位置了。---(本质是操作系统给内存指定位置写入库位置,GOT就相当于操作系统与编译器两个模块的约定,编译器负责创建外壳,操作系统负责修改实际内容,不涉及代码区了)

2问.既然动态库给操作系统管理,给多个程序用,怎么判断什么时候移出内存呢?

答:有引用计数,同文件inode与文件名间的引用,引用归零才移除。

加载并使用动态库节与库的交互

(无懒加载时)磁盘先加载程序,读取并选择载入elfheader,headertable,sectionheadertable,根据headertable将程序LOAD段的节载入内存,

根据这些信息,根据节的大小等初始化虚拟地址空间(即在mmstruct内创建并初始化vm_area_struct。start,end等)

然后在页表内创建虚拟地址与物理地址间的映射,载入权限。

然后载入程序所需要的动态库,内存中创建libso结构体,将动态库放入共享库区域。将程序的节内GOT存的动态库名:偏移量为动态库的实际地址:偏移量。

开始运行,pc载入header内 存的entry虚拟地址,给MMU硬件翻译为物理地址,载入指令执行,一直到载入需要动态库的指令,如GOT虚拟地址:偏移量时,翻译为物理地址去内存找GOT的内容,找到库函数位置,加载运行。

更有逻辑版:

1. 加载ELF文件到内存

  1. 读取文件头信息

    • 内核读取ELF文件的Header,确认文件类型(如ET_EXEC/ET_DYN)。

    • 解析Program Header Table,获取需要加载的段(如LOAD段)的信息(虚拟地址、文件偏移、权限等)。

  2. 物理内存分配

    • 根据Program Header的指示,将程序的代码段(.text)、数据段(.data等)从磁盘加载到物理内存。


2. 构建进程虚拟地址空间

  1. 初始化内存描述符(mm_struct)

    • 为进程创建mm_struct结构,管理虚拟内存空间。

  2. 创建虚拟内存区域(VMA)

    • 根据每个LOAD段的虚拟地址范围(vaddr)、大小和权限(读/写/执行),在mm_struct中创建对应的vm_area_struct结构。

    • 示例:代码段VMA(vm_start=0x400000, vm_end=0x401000, vm_prot=RX)。

  3. 建立页表映射

    • 将VMA的虚拟地址映射到已加载的物理内存页,设置页表项(PTE)的权限(如PTE_PRESENT | PTE_USER)。


3. 动态链接库处理

  1. 加载依赖库

    • 解析程序的.dynamic段,获取依赖的动态库列表(如libc.so)。

    • 通过dlopen机制将动态库加载到共享库区域(如0x7ffff7a00000),并为每个库创建libso结构体管理其内存和符号表。

  2. 重定位全局偏移表(GOT)

    • 修改程序.got.plt段中的占位符条目,将动态库名:偏移量替换为动态库的实际虚拟地址(如0x7ffff7b00000 + 0x1234)。

    • 延迟绑定(Lazy Binding):首次调用库函数时,通过PLT触发动态链接器(ld.so)解析实际地址。


4. 启动程序执行

  1. 设置入口点(Entry Point)

    • 将进程的PC寄存器设置为ELF头中的e_entry(如0x400520),即程序的主函数入口(如_start)。

  2. MMU地址翻译

    • CPU访问虚拟地址时,MMU通过页表将其转换为物理地址:

      • 例如PC=0x400520 → 查页表 → 物理地址0x12345000

  3. 动态库函数调用

    • 当执行到call printf@plt时:

      • 跳转到.plt段的桩代码,通过.got.plt查找printf的地址。

      • 若未绑定,触发动态链接器解析printflibc.so中的实际地址(如0x7ffff7b01234)并更新GOT。

      • 最终跳转到0x7ffff7b01234执行函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值