Linux——磁盘文件的理解 + inode详解 + 软硬链接 +动静态库

文章详细解释了磁盘文件的管理,包括CHS和LBA地址的概念,以及inode在文件系统中的作用。讨论了软硬链接的区别,强调了动态链接与地址无关性的特点。此外,还介绍了静态库和动态库的生成及使用场景,动态库在运行时的查找策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

磁盘文件的理解

之前讲述的都是内存级的文件,但是系统中最多的还是磁盘级的文件,大量的文件自然也需要被管理,那么如何管理呢?
我们先看几张磁盘的实物图才能更好的了解

在这里插入图片描述
如图所示,读写磁盘的时候,磁头找的是某一个面(哪一个磁头)的某一个磁道(距离圆心的半径)的某一个扇区(磁道上的一段,盘面旋转决定的)。
只要我们能找到磁盘上的盘面,柱面(磁道),然后找到扇区也就是找到一个存储单元就能读写磁盘
注:每个扇区的大小都是512字节,无论距离圆形的半径是多少
用同样的方法,我们也就能找到所有的基本单元了。
在这里插入图片描述
由于他们的英文名称,所以在物理上找某个扇区的地址叫做CHS地址
但毕竟磁盘还是物理结构,我们需要将其逻辑抽象一下。
在这里插入图片描述
如图所示,如果我们将这个磁带全部拉出来的话,就变成了一条直线,同理我们也可以把磁盘的一块块扇区拼接起来,变成一条长方形。
在这里插入图片描述
因此,定位一个扇区,只需要找到对应数组的下标就可以了,在内存中这个地址叫做LBA地址(Logic Block Address )逻辑地址
理解:内存中有个数据想向磁盘中写入的话,在内存中他只知道LBA地址,所以首先将LBA地址映射转化成CHS地址,转化成CHS地址之后,然后将内存数据配合CHS地址写入到磁盘中,至此写入就完成了。
但是磁盘的结构是硬件层面的,我们要学习的是如何管理这些磁盘上的文件。
虽然磁盘存储的基本单位扇区的大小是512字节,但是文件系统访问读写磁盘的基本单位是4kb,为什么要这样呢?

  1. 提高IO的效率
  2. 不要软件操作系统的设计和硬件磁盘具有强相关性,使得他们之间接耦合。
    万一以后硬件的一个扇区的大小设置成1024个字节,此时操作系统难道也要跟着变吗?所以操作系统不管硬件的扇区大小是多少,他只需要保持4kb的吞吐量就可以了,接耦合有利于软硬件的发展。

inode

在这里插入图片描述
在这里插入图片描述
如上所示,最左边的就是文件的inode 编号
问题:创建一个文件时,OS做了什么?
当文件在目录被创建时,Linux文件系统会根据文件名为其分配一个唯一的inode编号,然后修改inode Bitmap,在inode Table找到对应的节点然后向他里面写入对应的属性号。找到还未使用的数据块,修改Block Bitmap,将文件名和inode编号的映射关系写入到目录的数据快中!!
删除了一个文件,OS做了什么?
先找到文件名与inode对应的条目,然后找到inode编号,再根据inode编号找到对应的inode Bitmap和blockBitmap由1置0,此时就完成了文件的删除,再在文件的所在目录中将文件名与inode对应的映射关系清除,所以在linux当中文件的数据并没有被真正的删除
我知道了自己所处的目录,就能知道目录的inode吗?
不能,目录的inode在其根目录的内容中,最终得找到根目录

软硬链接

什么是软链接
软连接时第一个独立文件,拥有自己的独立inode和inode编号,主要作为Linux下的快捷方式,因为有的可执行文件或头文件或者是动静态库所在路径十分的深,如果直接运行或者是使用的话路径过深会非常麻烦,所以使用快捷方式非常容易
在这里插入图片描述
什么是硬链接?
硬链接不是一个独立的文件,他和目标文件使用的是同一个inode,就是单纯的在linux指定的目录下,给指定的文件新增文件名和inode编号的映射关系。
可以理解为一个“指针”概念,表示有几个文件名和我的inode建立的关系,有什么用呢?路径间切换
在这里插入图片描述
为什么新建目录的硬链接数是2?但是文件确实1呢?

在这里插入图片描述
可以看到当我们进入test2目录之后,其表示当前路径的.与test2的inode编号是相同的,所以说硬链接数可以用于路径间切换,一般还可以用来估算一下目录中有多少层目录。

动静态库

什么是静态库?
程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库。
优点:

  1. 性能高,程序运行时无需加载额外的库文件
    缺点
  • 占用空间大, 每个程序内部都要保留一份printf(), scanf(), strlen() 等函数还有一些辅助数据结构,所占据的内存十分大。
  • 代码的耦合度太高,如果其中的一个地方错了改动之后需要用户重新下载一遍
  • 代码重复,代码的复用率不高
    所以有了动态库
    程序在运行的时候才去链接动态库的代码,将文件根据功能还分成若干模块,降低了代码的耦合度,并且所有的程序共享使用库的代码。

理解动态链接与地址无关

理解动态链接产生与地址无关码
当进程加载到动态链接时,链接器会将库中所有的符号地址都替换成相对地址,静态链接是将库中所有的符号地址替换成绝对地址,相对地址是相对于库中某个特定基地址的偏移量,然后在运行时,进程会将这个基地址设置为虚拟地址空间中(共享区)的某个地址,从而使得动态链接库的相对地址能够正常的映射到实际的虚拟地址中。动态链接的符号地址都是相对于基地址的偏移量,所以说动态链接与地址无关,基地址是指动态链接在内存中被加载的起始地址然后再将这个基地址映射到进程的虚拟地址空间中。假设动态库的基地址为B,某个符号在动态链接库的地址为A,该符号在进程的虚拟地址空间中的地址是V,那么在运行时,进程会将动态链接库中符号地址A替换成相对地址A-B,然后将基地址B设置为V-A,从而使得动态链接库中的相对地址能够正确映射到实际的虚拟地址空间中。

生成动静态库

.o文件是一种目标文件,它是编译器将源代码编译后生成的中间文件,其中包含了已编译的机器代码,符号表和调试信息
如果我们把多个.o文件给他人,其他人链接能够使用吗?
先记录下下面的源码
mymath.h

#pragma once
#include<stdio.h>
#include<assert.h>
//[from, to] -->累加 -> result ->return
extern int addToVal(int from, int to);

myprintf.h

#pragma  once
#include<stdio.h>
#include<time.h>
extern void Print(const char *msg);

mymath.c

#include"mymath.h"
int addToVal(int from, int  to)
{
  assert(from <= to);
  int sum = 0;
  for(int i = from; i <= to; i++)
  {
    sum += from;
  }
  return sum;
}

myprintf.c

#include"myprintf.h"

 void Print(const char *msg)
{
  printf("%s : %lld\n", msg, (long long)time(NULL));
}

在这里插入图片描述
实验验证,多个.o文件与一个源文件链接在一起,会形成可执行文件,因为链接多个.o文件时,链接器会解析符号表,将不同的.o文件中定义和引用的符号进行匹配,从而形成可执行文件或共享库文件。
但是.o文件太多了,用起来特别不方便,所以我们将.o文件打包形成一个库。

生成静态库:
 10 libmymath.a:mymath_s.o myprintf_s.o
 11     ar -rc libmymath.a mymath_s.o myprintf_s.o
 12 mymath_s.o:mymath.c
 13     gcc -c mymath.c -o mymath_s.o
 14 myprintf_s.o:myprintf.c
 15     gcc -c myprintf.c -o myprintf_s.o
 ar是gnu归档工具,rc表示(replace and create)

生成动态库:
  3 libmymath.so:mymath.o myprintf.o                                                                    
  4     gcc -shared -o libmymath.so mymath.o myprintf.o
  5 mymath.o:mymath.c
  6     gcc -fPIC -c mymath.c -o mymath.o 
  7 myprintf.o:myprintf.c
  8     gcc -fPIC -c myprintf.c -o myprintf.o
  shared:表示生成共享库格式
  FPIC:产生位置无关码
  库名规则:libXXX.so

同时实现动静态库的代码

1 .PHONY:all
  2 all:libmymath.so libmymath.a
  3 libmymath.so:mymath.o myprintf.o
  4     gcc -shared -o libmymath.so mymath.o myprintf.o
  5 mymath.o:mymath.c
  6     gcc -fPIC -c mymath.c -o mymath.o 
  7 myprintf.o:myprintf.c
  8     gcc -fPIC -c myprintf.c -o myprintf.o
  9 
 10 libmymath.a:mymath_s.o myprintf_s.o
 11     ar -rc libmymath.a mymath_s.o myprintf_s.o
 12 mymath_s.o:mymath.c
 13     gcc -c mymath.c -o mymath_s.o
 14 myprintf_s.o:myprintf.c
 15     gcc -c myprintf.c -o myprintf_s.o
 16 
 17 
 18 
 19 .PHONY:lib
 20 lib: 
 21     mkdir -p lib-static/lib 
 22     mkdir -p lib-dyl/lib 
 23     mkdir -p lib-dyl/include
 24     cp *.so lib-dyl/lib 
 25     cp *.h lib-dyl/include
 26     mkdir -p lib-static/include
 27     cp *.a lib-static/lib                                                                           
 28     cp *.h lib-static/include
 29 .PHONY:clean
 30 clean:
 31     rm -rf *.o *.a lib *.so lib*

在这里插入图片描述

使用静态库

在这里插入图片描述
此时如果包含头文件然后编译运行mytest一定是错误的
在这里插入图片描述
因为库文件的搜索路径

  1. 在当前路径下查找文件,此时mytest.c包含的头文件是在lib-static中,所以不行
  2. 由环境变量指定的目录(LIBRARY_PATH)
  3. 系统默认目录
    /usr/lib
    /usr/local/lib

系统的头文件如下所示
在这里插入图片描述
系统库安装位置
在这里插入图片描述
由于我们的头文件和库文件都不在系统路径下,所以此时编译器找不到,所以一定是会报错的。

在这里插入图片描述
如上所示:如果我们此时把头文件和库文件都拷贝进系统目录的话再次编译,此时还是会报错,因为我们从来没有使用过第三方库,一直使用的都是C/C++库,所以如果我们要使用第三方库的话我们必须链接,链接名 需要去掉前缀和后缀,然后就可以了。
但是我们并不推荐安装库,会污染系统的标准库
我们还可以指定路径寻找头文件
在这里插入图片描述

使用动态库

我们一开始直接使用和上述最后一种的方法
在这里插入图片描述
为什么静态库运行的时候没有出现上述找不到库的问题?因为静态链接形成可执行程序之后,已经把需要的代码拷贝进我的代码中!运行时,不需要依赖系统中的库——不需要运行时查找
为什么动态库会有这个问题呢?
因为程序和动态库是分开加载的!
如何解决?
想办法让进程找到动态库即可!

  1. 动态库拷贝到系统路劲下 /lib64 --安装(不推荐!)
  2. 通过环境变量的方式,—程序运行的时候,会在环境变量中查找自己需要的动态库路劲 – LD_LIBRARY_PATH
  3. 系统配置文件来做
    通过导入环境变量的方式

在这里插入图片描述
这样导入环境变量只在当前shell有效,重启之后就无效了,因为shell本身启动的时候就是在配置文件里导入,说明这个指令本身是内存级别的。
通过配置文件
在这里插入图片描述
就是在/etc/ld.so.conf.d/这个路径下写一个文件然后将对应的库路径写入就可以了。因为系统寻找库路径的时候不仅会在默认路径下还会扫描这个路径(试验完记得删除!)
在系统中建立软链接查找动态库
在这里插入图片描述
此时不用指明库的路径和名称直接就可以编译运行

动态链接再次理解!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天少点debug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值