8. 理解文件系统
8.1. 认识硬件 --- 磁盘
唯一的机械设备,也是一个外设
注意:
- 磁头是一面一个,磁头和盘面不接触
- 在软件设计上,设计者会有意识地将相关数据放在一起
- 一般来说,运动越少,效率越高;运动越多,效率越低
8.2. 磁盘的存储构成
注意:
- 磁盘被访问的最基础单位是扇区 ---- 512字节
- 我们可以把磁盘看作无数个扇区构成的存储介质
- 把数据存到磁盘上,第一个解决的问题是定位那一面(哪一个磁头),哪一个磁道,哪一个扇区
8.3. 磁盘的逻辑结构
磁盘是圆面,我们可以将它划分成线性结构
通过扇区编号,可以找到对应的CHS(C:磁道,H:磁头,S:扇区)
9. inode
文件 = 文件内容 + 文件属性
文件内容 ---- 储存在数据块
文件属性 ---- 储存在inode
代码
- Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成
- 超级块(Super Block):存放文件系统本身的结构信息。(记录的文件系统的基本信息:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息) Super Block的信息被破坏,可以说整个文件系统结构就被破坏了 , 所以一般,不止一个分区存放 Super Block
- Group Descriptor Table:块组描述符,描述块组属性信息(会有inode 和 Data group的整体的使用率)
- 块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
- inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
- inode Table 节点表:存放文件属性 如 文件大小,所有者,最近修改时间等
- Block group 数据区:存放文件内容
注意:
- inode : 单个文件的所有属性,128字节,一般而言,一个文件,一个inode
- 在linux系统中,识别一个文件,用的是inode编号
- linux的文件在磁盘中存储,是将内容和属性分开存储的**(inode属性中,不包括文件的名称)**
- 在一个分区被使用之前,都必须提前将部分文件系统的属性信息提前设置进对应的分区中,方便我们后续继续使用这个分区或者分组)【这种做法叫格式化】
- 文件的删除其实只是修改 Block Bitmap 和 inode Bitmap ,数据块的内容没有动
- 对于目录,目录也是文件,也有自己的 inode,目录也有自己的属性,目录的数据块存放的是, 该目录下的文件名和其对应的inode编号的映射关系
10. 理解软硬链接
- 认识硬链接
我们看到,真正找到磁盘上文件的并不是文件名,而是inode。 其实在linux中可以让多个文件名对应于同一个
- ln + 已存在的文件名 + 新建的文件名
代码
不难发现 test.c 和 tmp.c是同一个文件,因为它们 inode编号一样
注意:
- 如果要删除文件,在目录中将对应的文件删除,将硬连接数-1,如果为0,则将对应的文件释放
- 硬链接不是一个独立的文件,因为他没有独立的inode
- 不能硬链接目录(操作系统采用的除外,如 . .. 目录)
- 硬链接应用场景
通常用来进行路径定位,可以进行目录间的切换
- 软链接个数
这里 test 的硬链接个数为2,是因为 test 和 test目录下的 . 文件是同一个inode
- 认识软链接
软连接是一个独立的文件,具有独立的 inode,也有独立的数据块,它的数据块存放的是指向文件的路径(相当于Windows的快捷方式)
- ln -s + 目标文件名 + 新建的文件名
代码
- unlink + 新建文件名
删除软链接
注意:
linux中,我们的每一个进程,打开的每一个文件都要有自己的inode属性和自己的页缓冲区
11. 动态库和静态库
(一)理解动静态库
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中(程序运行的时候将不再需要静态库 )
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间
静态库 代码
生成静态库
ar -rc libmymath.a add.o sub.o
将多个 .o 文件进行打包
gcc + main.c(有main函数) + -I[大写i] (头文件的所在路径) -L(库所在的路径) -l[小写l] (具体哪一个库,即库的名称,库的名称是去掉 lib 和 . 后缀)
库搜索路径 :
- 从左到右搜索-L指定的目录。
- 由环境变量指定的目录 (LIBRARY_PATH)
- 由系统指定的目录 (/lib , /usr/lib)
动态库 代码
生成动态库
gcc -shared -o libmymath.so add.o sub.o
gcc + main.c(有main函数) + -I[大写i] (头文件的所在路径) -L(库所在的路径) -l[小写l] (具体哪一个库,即库的名称,库的名称是去掉 lib 和 . 后缀)
- shared: 表示生成共享库格式
- fPIC:产生位置无关码(起始位置 + 位置偏移量 = 在进程地址空间的位置)
- 库名规则:libxxx.so
注意:
- 第三方库使用的时候,一定要用 gcc -l(告诉编译器链接哪一个动态库)
- 如果系统只提供静态链接,gcc 则只能对其进行静态链接
- 如果系统中需要连接多个库,则 gcc 可以链接多个库
(二)解决加载器问题
- 动态库在运行时,是要被加载的(静态库没有),所以存在编译器知道动态库的地址,但是加载器不知道的问题
- 动态库在系统中加载后,会被所有进程共享
- 系统在运行时,一定会存在多个动态库,需要被操作系统管理起来,即先描述,再组织,对于系统来说,所有库的加载情况是非常清楚的
解决加载找不到动态库的方法:
- 拷贝到系统默认的库路径/ib64/usr/lib64/
- 在系统默认的库路径/lib64usr/lib64/下建立软连接
- 将自己的库所在的路径,添加到系统的环境变量ldlibrarypath中
- /etc/ldso.conf.d建立自己的动态库路径的配置文件,然后重新ldconfig即可
(三)程序加载问题
- 程序没有加载前的地址
程序编译好,内部有地址的概念,我们称为逻辑地址,逻辑地址和加载后的进程地址结构类似
- 程序加载后的地址
注意:
PC所指向的可能是数据也可能是地址(该地址是虚拟地址)
(四)共享库加载问题
动态库会被加载到共享库中,而共享库很大,具体映射到哪里?
动态库被加载到固定地址空间的位置是不可能的,要使得库在虚拟内存中被任意加载,不采取绝对编址,只表示每个函数在库中的偏移量即可(fPIC 直接用偏移量进行对库中函数进行编址)