文章目录
- 1. 软硬链接
- 💥硬链接(Hard Link)
- 💥软链接(Symbolic Link)
- 2. 动静态库
- 💥静态库(Static Libraries)
- 💥动态库(Dynamic Libraries)
1. 软硬链接
在Linux系统中,文件链接分为两种类型:硬链接(Hard Link)和软链接(也称为符号链接或Symbolic Link)。它们都是用于创建文件的额外入口点,但是工作原理和使用场景有所不同。
💥硬链接(Hard Link)
- 定义:硬链接是一个指向原始文件的直接链接。硬链接是一个与原始文件共享inode的文件,它们在文件系统中实际上是同一个文件。
每个文件在Linux系统中都有一个inode,它包含了文件的所有元数据信息,如权限、所有者等,但不包含文件名。当创建一个硬链接时,实际上是为同一个inode创建了一个新的文件名,这意味着两个或多个硬链接实际上指向的是同一个文件数据。
- 特性:
- 硬链接不能跨文件系统。
- 不能对目录创建硬链接(除了
ln
命令的特殊选项外,这通常是为了安全考虑)。 - 对一个文件的修改会反映在所有的硬链接上
- 如果原始文件被删除,只要还有一个硬链接存在,文件的数据就不会被删除。
删除原始文件后,硬链接仍然可以访问原始文件的内容,这是因为Linux系统在创建硬链接时,会使用引用计数的方式,记录一个文件共有多少个硬链接,当删除一个文件时,只有将其所有的硬链接都删除,这个文件才算真正的删除,否则都可以通过任意一个硬链接访问文件内容。
- 创建方法:使用
ln
命令。例如,要为文件file.txt
创建一个名为file-hard.link
的硬链接,可以执行命令ln file.txt file-hard.link
。(注意文件的后缀可以随意选择)
最开始创建file.txt文件,只有它自己,所以引用计数为1,后来为它新建了一个硬链接file-hard.link,引用计数就变为2,因为它们两个本质上是同一个文件,所以file-hard.link的引用计数也为2,与file.txt保持一致。
关于引用计数:
新建一个目录文件,它一开始的硬链接引用计数就是2,除了它自己以外,它里面还包含一个隐藏文件.
,这个其实也是该目录的硬链接,如下图所示:
而上图中的
..
文件则是指向上一级目录的硬链接,每个目录在创建的时候都会自动创建这两个文件。
💥软链接(Symbolic Link)
- 定义:软链接是一个指向原始文件或目录的特殊文件,它包含了另一个文件或目录的路径名。通过访问软链接,可以间接访问源文件。
这意味着软链接形成的文件是不同于原始文件的,它们有着不同的inode编号,与硬链接不同
-
特性:
- 软链接可以跨文件系统。
- 可以对文件或目录创建软链接。
- 如果原始文件或目录被移动或删除,软链接将失效(变为“断开”状态)。
- 原始文件的权限信息、时间戳等软链接都是不保留的,它仅仅保存了原始文件的路径名。
-
创建方法:使用
ln -s
命令。例如,要为文件sfile.txt
创建一个名为sfile-soft.link
的软链接,可以执行命令ln -s sfile.txt sfile-soft.link
。
使用场景:
- 硬链接适合于需要确保文件不会因为原文件的删除而丢失的情况,尤其是在同一文件系统内。减少磁盘空间占用以及创建备份文件等场景。
- 软链接则更灵活,适用于需要跨文件系统链接或者链接到可能经常变动的位置的情况。此外,软链接可以更容易地识别和管理,因为它们本质上是文本形式的路径。
总结起来,软连接有独立的inode,软连接内容上保存的是目标文件的路径,当原始文件删除后,软链接将会失效;例如windows的快捷方式。而硬链接不是独立的文件,没有独立的inode,本质是一组文件名与目标文件的映射关系(别名),删除原始文件后,因为引用计数的存在,本质目标文件并没被删除,硬链接仍然可以访问原始文件的内容。
2. 动静态库
在Linux系统中,库文件分为静态库(Static Libraries)和动态库(Dynamic Libraries)。这两种类型的库各有优缺点,适用于不同的场景。
💥静态库(Static Libraries)
定义:
- 静态库是在编译阶段就被链接到目标代码中的库。这意味着当一个程序被编译时,它会包含它所依赖的静态库的副本。
优点:
- 程序运行时不需要外部库文件,因为所有需要的代码都已经被整合进可执行文件中。
- 可以确保程序总是使用特定版本的库,避免了所谓的“DLL地狱”问题(在Windows环境中常见,指的是由于不同应用程序可能依赖于不同版本的同一个库而导致的问题)。
缺点:
- 每个使用该库的程序都会有一个库的副本,这会导致磁盘空间的浪费。
- 如果静态库需要更新,那么所有使用这个库的应用程序都需要重新编译和链接。
创建静态库
- 编写源代码并将其编译为目标文件(.o):
gcc -c mystdio.c -o mystdio.o
- 创建静态库文件可以使用
ar
命令:ar rc libmystdio.a mystdio.o
libmystdio.a
是静态库文件的名称mystdio.o
是要打包进库文件的目标文件rc
表示replace create
静态库通常具有
.a
扩展名。
使用静态库
方法一:使用带路径的库进行链接
gcc myprogram.c -I/path/to/include -L/path/to/library -lmylib -o myprogram
-
-I/path/to/include
指定头文件所在的路径 -
-L/path/to/library
指定库文件所在的路径 -
-lmylib
指定要链接的库文件的名称(省略了前缀lib
和文件扩展名.a
) -
使用静态库进行链接时,可以使用
-l
和-L
选项指定库文件的位置和名称,-l
指定库的名字
gcc在查动静态库时不会在当前目录下查,所以我们需要指定路径
方法二:将库安装到系统中直接使用
- 首先,编译源代码生成静态库文件(通常以
.a
为文件扩展名)。可以使用gcc
或g++
命令进行编译。例如,编译一个名为libexample.a
的静态库文件可以使用以下命令:
gcc -c example.c # 编译源代码生成目标文件
ar rc libexample.a example.o # 使用ar工具将目标文件打包成静态库
- 将生成的静态库文件复制到系统目录中,例如
/lib64/
。可以使用cp
命令进行复制,例如:
sudo cp libexample.a /lib64/ # 将静态库文件复制到系统目录
sudo cp libexample.h /usr/include/ #将静态库头文件复制到系统目录
- 更新库缓存。在某些Linux发行版中,需要手动更新库缓存以使系统可以找到新安装的库。可以使用以下命令更新库缓存:
sudo ldconfig # 更新库缓存
- 确认安装成功。可以使用
ls
命令检查静态库文件是否已经复制到系统目录中,例如:
ls /lib64/libexample.a # 检查静态库文件
- 在编译和链接其他程序时,可以使用
-l
选项指定使用安装的静态库。例如,使用gcc
命令编译一个名为example_program.c
的程序,并链接使用静态库libexample.a
可以使用以下命令:
gcc example_program.c -o example_program -lexample
链接库时需要省去前缀lib和后缀.a,动态库也是一样,直接 -lexample即可
这样,就可以将静态库安装到系统中,并且可以直接在其他程序中使用该库。
💥动态库(Dynamic Libraries)
定义:
- 动态库(也称为共享库)是在程序运行时被加载的。这意味着多个程序可以共享同一份库文件,从而节省内存和磁盘空间。
优点:
- 节省磁盘空间和内存,因为多个程序可以共享同一个库文件。
- 库更新时不需要重新编译或重新链接应用程序,只需替换库文件即可(前提是API没有改变)。
缺点:
- 程序运行时需要有相应的动态库存在,否则可能会导致程序无法启动。
创建动态库
- 动态库通常具有
.so
(shared object) 扩展名。 - 创建动态库可以使用
gcc
编译器的-fPIC
和-shared
选项。
例如,编译一个名为libexample.so
的动态库文件可以使用以下命令:
gcc -fPIC -c example.c #编译.o文件
gcc -shared -o libexample.so example.o # 编译源代码生成动态库
-fPIC:形成与位置无关码
使用动态库
方法一:使用带路径的库进行链接
这里与静态库一致
gcc myprogram.c -I/path/to/include -L/path/to/library -lmylib -o myprogram
-
-I/path/to/include
指定头文件所在的路径 -
-L/path/to/library
指定库文件所在的路径 -
-lmylib
指定要链接的库文件的名称(省略了前缀lib
和文件扩展名.a
) -
使用动态库进行链接时,可以使用
-l
和-L
选项指定库文件的位置和名称,-l
指定库的名字 -
可以使用
ldd
命令查看一个程序所依赖的动态库列表,例如:ldd myprogram
。
gcc在查动静态库时不会在当前目录下查,所以我们需要指定路径
注意链接动态库形成可执行程序后,运行可执行文件时,系统是需要找到动态库的位置,也就是运行可执行程序时,动态库是需要加载的,所以如何让系统找到动态库?
- 将动态库文件复制到系统目录中,系统会默认在该目录下寻找
- 在系统路径建立动态库文件的软链接
- Linux系统中除了会在系统路径下寻找动态库,还会在环境变量中寻找,该环境变量为
LD_LIBRARY_PATH
- ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新
方法二:将库安装到系统中直接使用
- 将生成的动态库文件复制到系统目录中,例如
/lib64
。可以使用cp
命令进行复制,例如:
sudo cp libexample.so /lib64/ # 将动态库文件复制到系统目录
sudo cp libexample.h /usr/include/ #将静态库头文件复制到系统目录
- 更新库缓存。在某些Linux发行版中,需要手动更新库缓存以使系统可以找到新安装的库。可以使用以下命令更新库缓存:
sudo ldconfig # 更新库缓存
- 确认安装成功。可以使用
ls
命令检查动态库文件是否已经复制到系统目录中,例如:
ls /lib64/libexample.so # 检查动态库文件
- 在编译和运行其他程序时,可以使用
-l
选项指定使用安装的动态库。例如,使用gcc
命令编译一个名为example_program.c
的程序,并链接使用动态库libexample.so
可以使用以下命令:
gcc example_program.c -o example_program -lexample
这样,就可以将动态库安装到系统中,并且可以直接在其他程序中使用该库。此外,编译形成可执行程序后,如果删除了需要的动态库,程序也是不可以运行的。
原理上理解动态库:
进程在链接动态库时,操作系统会先将动态库加载到内存中,然后将动态库在内存中的地址通过页表映射到进程地址空间的共享区,这样进程在执行库方法的时候就是在自己的地址空间中跳转运行的。当另一个程序也要使用同一个动态库时就不需要再重复加载该动态库到内存里了,只需要通过进程的页表进行映射之前加载到内存的动态库即可,多个程序也是这样。
总结
如果同时提供动态库与静态库,gcc/g++默认使用动态库;如果要使用静态库必须使用静态链接-static
来指明;如果使用动态链接但是只有静态库,那么gcc/g++只能选择静态库进行链接。
选择使用静态库还是动态库取决于具体的需求。如果关注程序的独立性和稳定性,且不介意较大的程序大小,可以选择静态库;如果更关心资源的有效利用,并且可以保证目标系统上有适当的动态库版本,那么使用动态库是更好的选择。