Linux 文件系统、动静态库

个人主页仍有未知等待探索-CSDN博客

专题分栏:Linux

目录

一、文件系统

1、了解磁盘的存储结构

1.基本知识

2.磁盘中盘片为什么高速旋转?

3.磁头为什么要左右摇摆?

4.如何找到一个指定位置的扇区?

5.文件在磁盘中怎么存储?

2、磁盘存储的抽象

1.为什么OS不直接用CHS?

2.逻辑抽象

3.管理磁盘文件

[Block group里面的属性](#Block group里面的属性)

查找文件的顺序

[关于inode number](#关于inode number)

关于目录

为什么目录文件中不允许有同名文件?

[如何拿到inode number的?](#如何拿到inode number的?)

怎么确认inode到底再哪个分区?

怎么删除一个文件?

回收站

分区格式化

目录通常是谁提供的?

Linux内核在被使用的时候,一定存在大量的解析完毕的路径,要不要对访问的路径做管理?

二、动静态库

1、软硬连接

1.软连接

2.硬连接

3.软硬连接的区别

4.软连接有什么用?

5.硬连接有什么用?

6.定位一个文件,只有两种方式?

7.磁盘中文件的引用计数

8.文件系统会不会有环路问题

9.文本文件写入和二进制文件写入

2、动静态库

1.标准库

2.动静态库

3.静态库

什么是静态库:

生成静态库:

为什么要有静态库和动态库呢?

形成可执行程序(浅):

1、第一种方式:

2、第二种方式:

形成可执行程序(深):

第一种方式:

系统里面的标准库为什么编译的时候就不需要再带上标准库了呢?

如何将第三方库设置成标准库?

第二种方式:

4.动态库

生成动态库

生成可执行程序:

这样直接进行运行是有问题的。

为什么静态库这样写就没有问题呢?

那如何将代码运行的时候查找默认的动态库位置改变呢?

[动态库加载 --- 可执行程序和地址空间](#动态库加载 --- 可执行程序和地址空间)

理解

当可执行程序编译成功后,没有加载运行,二进制代码中有"地址"吗?

ELF格式的可执行程序

关于编址

程序的加载

动态库的加载


一、文件系统

  • 没有被打开的文件,在磁盘中存放。 --- 磁盘文件
  • 打开一个文件 ---> 需要找到文件 ---> 在磁盘中寻找 ---> 通过文件路径 + 文件名。
  • 计算机只认识二进制。
  • 什么是0,1?计算机规定出来的,在物理上会有不同的表现形式。

1、了解磁盘的存储结构

1.基本知识

  • 磁盘的本质是机械设备,外设,慢,但是性价比高。
  • 盘片:可读可写可擦除,一片两面都可以写,一面一个磁头。
  • 磁盘读写的基本单位是扇区:512字节。
  • 1片磁盘 = n 个磁道,1个磁道 = n 个扇区。

2.磁盘中盘片为什么高速旋转?

定位扇区。

3.磁头为什么要左右摇摆?

定位磁道。

4.如何找到一个指定位置的扇区?

  1. 找到指定磁头。 --- H(Head,磁头)
  2. 找到指定磁道。 --- C(Cylinder,柱面)
  3. 找到指定扇区。 --- S(Sector,扇区)

5.文件在磁盘中怎么存储?

其实就是文件在磁盘中占据几个扇区的问题。

2、磁盘存储的抽象

1.为什么OS不直接用CHS?

  1. 耦合度太高。
  2. 不便于实现内核进行磁盘管理。

2.逻辑抽象

  • 把磁盘想成矩形,每个小格子就是一个扇区。
  • os和磁盘交互的时候,基本单位是4KB == 8 * sector(8个连续的扇区 --- 块大小)。


  • 文件 = 很多的块构成 = 很多个LBA地址。
  • 块号 * 8(8个连续的扇区) = 下标。
  • 所以只要知道一个起始块号,和磁盘的总大小,有多少块,每个块的块号,如果转换到对应的多个CSH地址,就全都知道了。
  • LBA:逻辑区块地址。
    所以,对整个的磁盘管理好,只需要把其中一个分区管理好就行。

3.管理磁盘文件

  • 文件 = 内容(数据) + 属性(数据)。
  • 在Linux中,文件的内容和属性单独存放。

分区->写入文件系统(格式化)->挂在到指定的目录下->进入该目录->在指定的分区中进行文件操作。

一个文件在访问前,都是先有目录的!!!

Block group里面的属性

1、Data blocks --- 数据区

存放文件内容。

2、Block Bitmap --- 块位图

记录Data Blocks中哪个数据块已经被占用。

比特位的位置表示块号,比特位的内容表示该块是否被占用。

通常用于创建,删除文件等等。

3、inode Bitmap --- inode位图

记录inode table里面指定位置是否存储了文件。

比特位的位置表示第几个inode(inode number),比特位的内容表示该inode是否被占用。

4、inode Table --- inode表

存放文件的属性,如文件大小,所有者,最近修改时间等。

Linux中文件的属性是一个大小固定的集合体 --- 128字节。

一个文件一个inode。

inode内部,不包含文件名!在内核层面。每一个文件,都要有inode number!我们通过inode号标识一个文件。

5、GDT(Group Descriptor Table) --- 块组描述符

描述块组属性信息,如块内剩余内存的大小,块组的inode范围等。

6、Super Block --- 超级块

存放文件系统本身的结构信息。

记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。

Super Block的信息被破坏,可以说整个文件系统结构就被破坏了。但不是只有一个,在整个文件系统中,可能有多个,但是信息要保持一致。

查找文件的顺序
  1. **确定分区:**确定分区(根据路径)。
  2. **获取inode number:**获得inode编号(根据文件名和inode的映射关系)。
  3. **确定分组:**根据分区里面每个组的inode范围,确定具体分组。
  4. **相对inode number:**inode编号 - 该组的起始inode编号,得到相对的inode编号。
  5. **判断inode number的合法性:**再根据相对的inode编号查找inode Bitmap,查看文件编号是否合法,为1就合法。
  6. **查找数据块:**如果合法,查找inode table表,找到inode对应的数据。
  7. **读取数据:**使用从inode中获取的数据块信息,文件系统会从磁盘上读取相应的数据块,并将数据返回给请求者。
关于inode number

我们寻找文件的时候,都必须先得到文件的inode号。

  • inode编号是以分区为单位。

  • inode号不能跨分区访问,因为不同区之前,inode号可能重复。

  • super block和GDT记录的都是inode number范围,start_inode, end_inode。区别就是super block记录的是整个分组的inode number范围,GDT记录的是这一个分组的inode number范围。

  • 查找inode number是再目录文件的data blocks里面,找到文件名和inode numer的映射关系。

关于目录
  • 目录也是文件,也有自己的属性和内容。
  • 目录也有inode number。根目录的inode number是系统默认的。
  • 目录文件的内容中:文件名和inode number的映射关系。
  • 目录的 r 权限:本质是是否允许我们读取目录的内容。
  • 目录的 w 权限:新建文件的时候,本质上,最后一定要向当前所处目录内容中写入,文件名和inode的映射关系。
为什么目录文件中不允许有同名文件?

因为文件系统要建立文件名和inode编号的映射关系。

如何拿到inode number的?

普通文件:根据文件名和inode的映射关系。

目录文件:根据上级目录文件的内容查找。如果上级目录的inode number也不知道,就继续往上面进行查找,直到到根目录为止。(根目录的inode numer由系统默认的,逆向的路径解析。)

怎么确认inode到底再哪个分区?

1、文件在哪个目录里,就在哪个分区下。

2、就是根据路径可以判断在哪个分区下。

怎么删除一个文件?

先根据文件名得到inode number,然后只需要把对应的位图inode bitmap,由1置0。

回收站

回收站的本质其实是一个目录。

分区格式化

在每一个分区内部分组,然后写入文件系统的管理数据 ---> 在磁盘中写入文件系统!

目录通常是谁提供的?

内核文件系统提前写入并组织好,然后进程提供的。

Linux内核在被使用的时候,一定存在大量的解析完毕的路径,要不要对访问的路径做管理?

要,先描述,再组织。

一个文件一个struct dentry --- 路径解析信息。

二、动静态库

1、软硬连接

1.软连接

ln -s filename1 filename2 (后者连接前者。)

类似于Windows里面的快捷方式。

这是两个单独的文件,因为inode不同。

2.硬连接

ln filename1 filename2

3.软硬连接的区别

**1、**软连接是一个独立的文件,因为有独立的inode number。

软连接的内容:目标文件所对应的路径字符串。(类似于windows中的快捷方式)

2、硬链接不是一个独立的文件 --- 因为inode number一样。

硬链接就是一个文件名和inode的映射关系,建立硬链接,就是在指定目录下,添加一个新的文件名和inode映射关系。

3、属性中有一列硬连接数(第三列)。

磁盘级的引用计数,有多少个文件名字符串通过inode number指向我,引用计数就是几。

目录文件中目录个数 = 引用计数 - 2。

4.软连接有什么用?

就是快捷方式。

5.硬连接有什么用?

1、构建Linux的路径结构,让我们可以使用. ..来进行路径定位(文件系统默认的,用户没有权限构建. ..的路径结构)。

2、一般用硬链接来做文件备份。

6.定位一个文件,只有两种方式?

1、通过路径。

2、直接找到目标文件的inode。

7.磁盘中文件的引用计数

任何一个目录,刚开始新建的时候,引用计数一定是2,任何一个目录,新建一个目录,就会让A目录的引用计数自动+1,一个目录内部有几个目录:A引用计数-2。

Linux系统中,不允许给目录建立硬链接。

8.文件系统会不会有环路问题

不会,因为文件名是固定的。所有的系统指令在设定时候,几乎就能知道. ..是干什么的。

9.文本文件写入和二进制文件写入

文本写入和二进制写入本质上是一样的,文本写入只是语言层转换的。

2、动静态库

1.标准库

C语言标准库:libc.so.6

C++标准库:libstdc++.so.6

2.动静态库

Linux: .so(动态库) .a(静态库)

Windows:.dll(动态库) .lib(静态库)


头文件是一个手册,提供函数的声明,告诉用户怎么用。

.o提供实现,我们只需要补上一个main,调用头文件提供的方法,然后和.o进行连接,就能形成可执行程序。

只需要math.o和main.c就可以生成可执行程序。

3.静态库

什么是静态库:

本质就是把.o文件打包。

生成静态库:

意思就是将所有的.o为后缀的文件进行打包,生成静态库mylibc.a。

-rc(replace and create):存在了就覆盖,不存在就创建。

生成的库文件的名字是:myc(去掉前缀lib,去掉后缀.a)

为什么要有静态库和动态库呢?

提高开发效率。

形成可执行程序(浅):
1、第一种方式:
2、第二种方式:
形成可执行程序(深):
第一种方式:

这种方式不推荐!!!

系统里面的标准库为什么编译的时候就不需要再带上标准库了呢?

因为系统会默认的去特定的位置去寻找标准库。

如何将第三方库设置成标准库?

只需要将第三方库下载到本地的/lib64;将第三方库的头文件下载到本地的/usr/include。

这样就可以正常编译了。

加上-l(小写L)选项,后面跟着第三方库的名称(去掉前缀,后缀剩下的部分)。

例如:静态库(libmyc.a),它的名称就是myc。

cpp 复制代码
// 1、gcc main.c -lmyc
// 2、gcc main.c -l myc
第二种方式:

通过自己指定库的路径的方式,来对库进行查找。

要多加两个选项来进行定位:-L(大写i)+库文件路径;-I+头文件路径。

cpp 复制代码
gcc main.c -I /usr/include -L /lib64 -l myc

4.动态库

当gcc编译的时候,没有指明-static时,默认生成的就是动态连接,如果没有动态库(有静态库),就生成静态连接。

生成动态库

先生成.o文件。

// -fPIC的作用是,产生位置无关码。

cpp 复制代码
gcc -fPIC -c *.c

然后再将.o文件打包生成动态库。

cpp 复制代码
gcc -shared *.o libmyc.so
生成可执行程序:
这样直接进行运行是有问题的。

--- 因为在运行的时候,操作系统不知道库文件的位置。

1、生成动态库。

2、进行编译。(gcc中选项 -I (大写i),指定用户自定义头文件路径,-L 指定用户自定库文件路径,-l(小写L)执行确定的第三方库名称。)

3、运行。

为什么静态库这样写就没有问题呢?

因为动态库要在程序运行的时候,要找到动态库加载并运行。静态库没有这种问题的原因是编译期间,已经将库中的代码拷贝到我们的可执行程序内部了,加载和库就没有什么关系了。

那如何将代码运行的时候查找默认的动态库位置改变呢?

1、直接将第三方动态库拷贝到默认的动态库位置(/lib64)。这种方式简单粗暴的方法,不建议。--- 安装到系统

2、在默认的标准库中建立一个软连接和第三方库连接上。--- 建立软连接

3、修改环境变量:LD_LIBRARY_PATH --- 动态库的搜索路径。但是环境变量是内存级别的,当关掉xshell之后,环境变量将恢复默认。--- 命令行导入环境变量

4、直接在.bashrc中修改LD_LIBRARY_PATH的路径,这样每次重启环境变量LD_LIBRARY_PATH则会变成你修改后的值。---修改配置文件.bashrc,让环境变量永久生效。

5、/etc/ld.so.conf.d配置文件,在该路径底下创建一个.conf的配置文件,里面写入第三方动态库的路径。然后再让该配置文件生效:ldconfig指令。--- /etc/ld.so.conf.d新增动态库搜索的配置文件,ldconfig

动态库加载 --- 可执行程序和地址空间
理解
  • 动态库会在磁盘中加载到内存中,然后会在mm_struct中的共享区开辟一段空间,将物理内存和mm_struct根据页表进行映射。
  • 可执行程序也会加载到内存中,和mm_struct中的正文区通过页表进行映射。
  • 当可执行程序调用一个函数的时候,就会去共享区进行寻找其函数定义。
  • 如果有两个可执行程序,动态库不需要加载两份。
当可执行程序编译成功后,没有加载运行,二进制代码中有"地址"吗?

包含了地址。

ELF格式的可执行程序
  • 所谓的二进制是有自己的固定格式的。
  • ELF可执行程序的头部,可执行程序的属性。
  • 可执行程序编译之后,会变成很多行汇编语句,每条汇编语句都有他自己的地址。
  • ELF + 加载器 ---> 会得到各个区域(代码区等等)的起始地址和结束地址,也会得到main函数的起始地址。
关于编址
  • 编址范围:从00000...0000 ~ FFF...FFFFF。
  • 地址的种类:绝对编址 --- 平坦模式;相对编址。
程序的加载

1、进程创建阶段,初始化地址空间,让cpu知道main函数的入口地址。

2、加载-> 每一行代码和数据,就都有了物理地址,自己的虚拟地址也知道,就可以开始构建映射了。

动态库的加载
  • 库被映射到虚拟地址空间的什么位置,不重要。因为映射完了,函数虚拟地址相当于是偏移量。 --- 与地址无关
  • 库函数调用,其实也是在地址空间内来回的跳转。
  • 怎么知道库有没有被加载?系统中有一个描述库的结构体的链表。

谢谢大家!!!

相关推荐
danplus26 分钟前
node发送邮件:如何实现Node.js发信功能?
服务器·node.js·外贸开发信·邮件群发·蜂邮edm邮件营销·邮件接口·营销邮件
小黑爱编程35 分钟前
【LInux】HTTPS是如何实现安全传输的
linux·安全·https
BeyondESH40 分钟前
Linux线程同步—竞态条件和互斥锁(C语言)
linux·服务器·c++
wn53141 分钟前
【Go - 类型断言】
服务器·开发语言·后端·golang
hanniuniu1342 分钟前
详细解读,F5服务器负载均衡的技术优势
运维·服务器·负载均衡
鱼饼6号1 小时前
Prometheus 上手指南
linux·运维·centos·prometheus
Asher Gu1 小时前
Linux系统编程入门 | 模拟实现 ls -l 命令
linux
PatrickYao04221 小时前
记一次安装discuz时遇到的错误
服务器
c无序1 小时前
【Linux进程控制】进程程序替换
linux
小宋10213 小时前
玩转RabbitMQ声明队列交换机、消息转换器
服务器·分布式·rabbitmq