一.inode
1.inode的介绍
在Linux操作系统中,‘inode
(索引节点)是文件系统的核心组件之一,用于管理文件和目录的元数据。每个文件和目录在磁盘上都有一个对应的
inode`,它不包含文件的实际数据内容,而是记录了关于文件的关键信息。
简单来说,在Linux系统中,一个文件,有一个inode。每一个inode都有自己的inode编号(inode的设置,是以分区为单位的,不能跨分区)
inode表示文件的所有属性,文件名,并不属于inode内的属性!
inode的组成结构:在Linux文件系统中,‘inode`包含以下信息:
-
·文件类型:标识文件是普通文件、目录、符号链接还是其他类型。
-
权限信息:包括文件的读、写、执行权限,以及文件所有者和所属组的信息。
-
·所有者和组:文件的用户ID (UID)和组ID(GID)。
-
·文件大小:文件的大小,以字节为单位。
-
时间戳:包括文件的创建时间
(ctime)、最后访问时间(atime)和最后修改时间(mtime)。 -
·数据块指针:指向文件数据在磁盘上的实际位置。
2.文件系统与inode
上图为磁盘文件系统图可以帮助我们理解inode在文件系统中的作用。我们假设一个磁盘有512GB的空间,然后我们可以对这512gb进行分区,可能是两百多GB,然后在每个区都有一个启动块(Boot Block)和数干个Block group。其中启动块(Boot Block)包含启动启动过程所需的所有关键数据和指令。而Block group块组又可以分成由多个模块组成的结构。
- 超级块(Super Block):存放文件系统的基本信息。里面包含着整个分区的基本使用情况记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了
- GDT:是一个存储段描述符的表,它在系统引导时被加载到CPU的内部寄存器中。每个描述符定义了一个内存段的起始地址、大小和访问权限等。
- Block Bitmap:Block Bitmap中记录着Data Block中哪个数据块的使用情况,比特位的位置和块号映射起来,表示该快是否被使用。
- inode Bitmap:比特位和inode映射起来,表示inode是否空闲可用。
- inode Table:inode表,单个inode表示文件的所有属性。
- Data Blocks:数据块存放数据(文件内容)的地方,常见的是4kb,文件系统的块大小。
可见在Linux系统中文件的内容和属性是分开存放的。
3.“目录”再理解
根据上面我们谈到的内容,应该怎么理解Linux下的目录呢?首先一切皆文件,目录肯定是个文件,我们又知道文件 = 内容 + 属性。那目录中存放的是什么呢:
该目录下文件名和对应文件inode的映射关系。
接着我们回想在先前讲文件权限时,就能有更深的理解了:
- 目录中没有写(w)权限时,我们无法创建文件的原因是无法在目录这个文件中写入对应的kv结构(文件名 对应文件的inode的映射关系)
- 目录中没有读(r)权限,无法查看到目录下的文件,找不到文件的inode
- 目录中没有修改(x)权限,无法cd进入文件,无法修改目录到环境变量。
- 目录下不能有同名文件,目录中的key值不能重复。
二.软硬链接
1.硬链接
硬链接本质就是在特定目录的数据块中新增文件名和指向的文件的inode编号的映射关系。
上图演示了硬链接的链接方式和删除方式(除了使用unlink删除链接还可以直接删除文件),现在我们可以知道在文件权限右边的数字其实代表的是文件的硬链接数目,只有当文件的硬链接等于0时才会被删除。
我们使用ls -li指令可以看到文件的inode信息,我们观察到硬链接的文件和源文件是同一个inode,因为硬链接不是独立的文件相当于取别名所以没有独立的inode。
Linux系统不支持对目录建立硬链接
虽是这么规定的,但是在上图所示中为什么目录还有不止一个硬链接数呢?其实是隐藏的.(当前目录)和…(上级目录)导致的,依靠这一特性,我们可以在不cd目录进去就可以知道一个目录下有着多少个文件。(硬链接数-2)
2.软连接
软连接是一个独立的文件所以有独立的inode。
软连接数据块存放的是源文件的路径,所以可以理解为文件的快捷方式
三.动静态库
1.静态库
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
下面演示静态库的使用:
首先我们穿件test.h和test.c文件,做一个简单的函数声明和定义,紧接着创建Makefile文件:
所谓生成静态库就是将.c 文件编译成.o文件然后将多个.o文件打包成静态库供用户使用。
接着我们再创建一个目录,并将静态库复制过去,模拟用户的使用。
用户拿到库 = 库 + .h 后,接着要写主函数使用库的功能。
接着我们编译时报错了:gcc不认识找不到这个头文件
这时候我们就要用指令让编译器找到头文件
我们可以看到现在虽然头文件可以编过去,但是函数add他还是不认识,这说明是在链接时发生了问题。
我们再将静态库指定给过去即可,这里要注意一定要写清静态库的名字,不能只给到路径,还有libXXX.a(静态库的名字是去掉前缀和后缀的XXX)。
这样子就可以完成了静态库的链接。对于上面的方法如果觉得太过复杂还有一些方法能解决问题:
- 将自己写的第三方库拷贝到系统库中
- 对头文件建立软连接,把软连接放在系统默认的库下面
2.动态库
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码
关于动态库还有几点需要了解一下:指令ldd能查看链接情况,gcc链接程序默认是动态链接的,如果系统中只提供我们对应的静态库,gcc则只能对该库静态链接,动静态库都有时我们可以指定static来链接静态库如果系统需要链接多个库,则gcc可以链接多个库
动态库链接比静态库链接相比较慢。
我们再创建mymath.c.h,mylog.c.h文件用来演示链接静态库。首先和静态库一样,先gcc汇编成.o文件接着打包即可:
这里需要加的一个fPIC叫与位置无关码,我们后续再讲解。
我们再把.o文件打包即可形成动态库。
接下来我们试着使用Makefile构建自动化的生成动态库:
这样子我们的动静态库就都生成好了
接着我们像上次一样将包装好的库复制到用户文件下,然后用户编写main函数使用动态库:
根据上图可以看到,当我们明明告诉了gcc.h文件库和动态库的位置后,依旧是无法执行可执行程序,原因是我们只是告诉了编译器,你还要告诉加载器–系统,你的动态库在哪里。因为动态库是共享库,在进程运行时是要被加载到物理内存中的。
解决这个问题有几种方法在这个就不一一演示了:
解决加载找不到动态库的方法
1.拷贝到系统默认的库路径 /lib64 /usr/lib64/
2.在系统默认的库路径 /lib64 /usr/lib64/下建立软连接
3.将自己的库所在的路径,添加到系统的环境变量LD LIBRARY PATH中
4. /etc/ld.so.conf.d 建立自己的动态库路径的配置文件,然后重新ldconfig即可
3.动态库的加载过程
经过上面的讲解过后我们知道动态库在系统中加载过后,会被所有进程共享,那这是怎么做到的呢?
上图产生的问题,从而衍生出了fPIC–与位置无关码,可以让代码通过偏移量(相对地址),即使在有多个动态库在共享区时,也能正确的找到。