Linux知识点 – 基础IO(三)
文章目录
- Linux知识点 -- 基础IO(三)
- 一、理解文件系统
- 1.磁盘文件
- 2.文件系统的存储结构
- 3.inode与文件名的关系
- 二、软硬链接
- 1、软链接
- 2.硬链接
- 三、动静态库
- 1.库
- 2.生成静态库
- 3.静态库的使用
- 4.生成动态库
- 5.使用动态库
- 6.为什么要有库
一、理解文件系统
1.磁盘文件
磁盘是永久性存储介质,如:SSD,U盘,光盘,磁带等;磁盘是外设,是我们计算机中唯一的机械设备,因此读取速度很慢;
-
磁盘物理结构:
磁盘中有盘片、磁头、伺服系统、音圈马达等;
磁盘面上会存储数据,由于计算机只认识二进制,因此用磁极来表示0和1,向磁盘写入,本质就是改变磁盘上的正负极; -
磁盘存储结构:
磁盘中的每一个盘面都分为多个不同的磁道,每个磁道又分为不同的扇区,磁盘的最小存储单元是一个扇区大小是512byte; -
如何将数据写入指定的扇区?
这是磁盘的CHS寻址:
(1)在哪一个面上;
(2)在哪一个磁道上;
(3)在哪一个扇区上; -
磁盘的抽象结构:
我们将磁盘的磁道展开成为线性结构:
这样来看磁盘的结构就像是一个数组,访问一个扇区,只需要知道这个扇区的数组下标就好;
我们平常所说的磁盘分区,就是将磁盘分为几个不同的数组:
将数据存储到该磁盘就变成了将数据存储到该数组;
找到磁盘特定扇区的位置就变成了找到数组特定的位置;
对磁盘的管理就变成了对数组的管理;
2.文件系统的存储结构
其中一个磁盘分区展开后的结构如下:
其中Blokck group就是用来存储文件的;
Linux在存储文件的时候,将内容和属性分开存储;
虽然磁盘的基本单位是扇区(512字节),但是操作系统(文件系统)和磁盘进行IO的基本单位是4KB(block的大小),因此,磁盘又被称为块设备;
这样设计的原因是:
- IO基本单位设计的太小的话,会导致效率变低;
- 如果OS使用和磁盘一样的基本单位,如果磁盘的基本单位更改了,那么操作系统也需要更改,所以需要对硬件和软件进行解耦;
在Blokck group中,有如下几个分区:
- Super Block:存储整个分区的文件系统的属性信息;并不是每个快组都有SB模块,而是分发在分区中某几个快组中,为的是进行备份,防止丢失;
- Group Descriptor Table(GDT):块组描述符,描述各个block的信息,如大小,使用情况,有多少inode等;
- Block Bitmap:块组位图,其中的比特位和特定的block是一一对应的,如果该位为1,就表示该block已被占用,否则表示可用;
- Inode Bitmap:inode位图,其中的比特位和特定的inode是一一对应的,如果该位为1,就表示该inode已被占用,否则表示可用;
- Inode Table:inode是一个大小为128字节的空间,保存的是对应文件的属性,是该块组内,所有文件的inode空间的集合;需要标识唯一性,每一个inode块,都需要有一个inode编号,一般而言一个文件一个inode,一个inode编号;
- Data blocks:多个4KB(扇区 * 8)大小的集合,保存的都是特定文件的内容;
格式化:将块组分割成上面的内容,并且写入相关的管理数据,每一个块组都如此操作,整个分区就被写入了文件系统信息;
一个文件可能对应多个block,找到文件,只需找到对应的inode编号,就能找到该文件的inode属性集合,文件内容所占的块组编号也在inode信息中的blocks数组中;
如果文件特别大,一个块组中放不下,需要存放到其他块组中,那么,inode中的blocks数组前m个block存放的是这个文件的数据,数组后面的block存放的是剩下数据存放的block块号,相当于一个间接寻址
3.inode与文件名的关系
- Linux系统寻找文件的过程:
由文件inode编号 -> 找到特定的bg块组 -> 对应的inode -> 属性 -> 内容;
那我们是如何得到文件的inode编号的?依托于目录结构;
Linux中,文件属性里面,没有文件名这样的说法;
- (1)一个目录下,可以保存很多文件,但是这些文件没有重复的文件名;
- (2)目录也是文件,有自己的inode,有自己的data block;
在Linux系统中存储文件,用户根据文件名创建和区分文件,但是写入系统时,系统只会为文件分配唯一的inode编号,然后建立文件名和inode编号的映射关系表,是一个KV结构,用户提供文件名,OS提供inode编号,所以用户能够通过文件名搜索到文件;
目录的权限:
- 在目录中创建文件必须有写权限,是因为创建文件必须向目录写入该文件的文件名和inode的映射关系;
- 显示文件名和属性必须有目录的读权限,因为要读取到文件的inode,才能读取属性
一个块组中,inode和data blocks的数量都是一定的,存在inode还有,但是block没了;或者inode没了,但是block还有的情况;
二、软硬链接
1、软链接
-
创建软链接:
ln:创建链接;
-s:软链接;
ls -i指令能够看出文件的inode:
可以看到,软链接是有独立的inode的,是一个独立的文件; -
软链接的作用:
在bin/exe目录下的可执行程序test,在现在的目录下建立软链接:
直接运行软链接:
可以看出运行成功;
软链接的文件内容,是指向对应文件的路径;可以理解为win下的快捷方式;
2.硬链接
-
创建硬链接:
可以看出,硬链接文件没有自己独立的inode,这也是软硬链接的本质区别; -
硬链接的作用:
从上图可以看出,硬链接和原文件属性中的这个数字都为2,这个属性是硬链接数;
硬链接就相当于给文件起别名,建立了文件名和指定inode的关系;
当我们删除硬链接指向的文件时,硬链接inode还存在,属性中的硬链接数从2变成了1;
硬链接没有独立的inode,不是一个独立的文件,创建硬链接,不是真正的创建新文件,就是在指定目录下,建立了文件名和指定inode的映射关系;
硬链接数就相当于一个引用计数,每创建一个硬链接时,引用计数就加一;当我们删除文件时,并不是把这个文件inode删除了,而是将这个文件的inode引用计数减一,当引用计数为0的时候,才真正删除;
- 硬链接的用途:
新建一个普通文件,硬链接数是1;
新建一个目录,硬链接数是2;
这是因为目录内部的 . 文件也影射了该目录的inode,两个硬链接,一个是自己的目录名链接inode,另一个是目录内部的 . 文件链接inode;
在test目录下再创建一个目录,可以发现test的硬链接数变为了3,这是因为ss目录中的 … 文件映射上级路径test的inode;
三、动静态库
1.库
- 库中没有main函数;
- 如果我只把.o和.h文件给别人,是可以使用的;
- 静态库后缀为:.a;
- 动态库后缀为:.so;
2.生成静态库
生成的.o文件给别人是直接可以使用的,但是很麻烦;
静态库的生成过程就是把.o文件打包的过程;
-
ar:归档文件;
-
-r:替换;
-
-c:创建;
-
libhello.a:静态库文件,必须以lib开头,.a结尾;
-
使用makefile实现自动打包:
-
库的发布:
库的发布文件夹里需要有两个子文件夹:
-inlcude:保存库的独有头文件;
-lib:保存对应的库文件;
在makefile中完成自动化创建:
3.静态库的使用
-
(1)拷贝库文件到系统目录下:
gcc头文件的默认搜索路径是:/usr/include
gcc头库件的默认搜索路径是:/lib64 or /usr/lib64
这时main函数中不会报错,但是编译时会报错,因为这是我们自己提供的第三方库,必须要告知C语言我们需要连接哪个库;
-l库名:指定链接库(没有空格),真正的库名是去掉前缀lib和后缀的;
运行结果:
但是这种方法会污染官方库,不建议使用; -
(2)让GCC在指定目录下搜索头文件和库:
-I 目录:在指令目录下搜索头文件;
-L 目录:在指定目录下搜索库文件;
由于该目录下可能有多个库文件,因此需要指定库文件;
4.生成动态库
-fPIC:生成一个与位置无关的目标二进制文件;
-shared:制作动态库,而不是可执行程序;
- 用makefile实现:
动静态库一起生成,并发布output文件夹;
打包库:
5.使用动态库
从上面看出,直接使用gcc指定头文件和库文件的方法来加载动态库,能够编译过,但是无法运行程序,使用ldd命令查看动态链接时,发现libhello.so动态库链接找不到;
ldd 可执行程序:查看该程序的动态链接;
我们发现gcc是默认动态链接的,但是没有链接上;
前面静态库是因为只有静态库,gcc只能静态链接:
使用静态库时就没有链接libhello.so;
-static:摒弃默认优先使用动态库的原则,直接使用静态库的方案;
在上面我们已经制定了动态库的路径,动态库还是无法链接,这是因为我们是给gcc指定的链接,这和动态库的加载过程有关;
-
动态库的加载过程:
动态库是一个独立的库文件,动态库和可执行程序,是可以分配加载的;
静态库的加载是直接将库代码加载到了内存里面,直接进入进程的代码区;
动态库是可以分批加载的,在将可执行程序加载到内存后,可执行程序中是有对应的库链接信息的;
在编译链接时,需要加载库时,就可以加载动态库了,是分开进行的,这时再将动态库代码加载到内存中,在页表建立库代码与共享区的联系;
在代码区函数跳转访问库函数的时候,直接跳转到共享区,根据页表找到内存中的库代码去执行;
-
方法一:添加环境变量
我们上面指定动态库路径是给gcc指定的,动态库是运行时加载的,这时就与gcc没有关系了,需要给系统说;
$LD_LIBRARY_PATH是系统环境变量,加载库的路径;
库的绝对路径:
导入环境变量:
运行可执行程序:
这种方法的缺点是:退出登陆后,环境变量会丢失,因为这是内存级的环境变量,只能做一个临时方案; -
方法二:修改配置文件
这个路径中包含自定义搜索库路径的永久解决方案;
直接创建文件,然后将目录放进该文件中;
更新系统权限:
运行:
-
方法三:在系统目录下建立软链接
运行:
6.为什么要有库
- 站在使用者的角度:
库的存在,可以大大减少我们的开发周期,提高软件本身的质量; - 站在开发库的角度:
简单,且能够保证代码安全;