在Linux系统中,文件链接分为两种类型:硬链接(Hard Link)和软链接(也称为符号链接或Symbolic Link)。它们都是用于创建文件的额外入口点,但是工作原理和使用场景有所不同。
每个文件在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,除了它自己以外,它里面还包含一个隐藏文件.
,这个其实也是该目录的硬链接,如下图所示:
而上图中的
..
文件则是指向上一级目录的硬链接,每个目录在创建的时候都会自动创建这两个文件。
这意味着软链接形成的文件是不同于原始文件的,它们有着不同的inode编号,与硬链接不同
ln -s
命令。例如,要为文件sfile.txt
创建一个名为sfile-soft.link
的软链接,可以执行命令 ln -s sfile.txt sfile-soft.link
。
使用场景:
总结起来,软连接有独立的inode,软连接内容上保存的是目标文件的路径,当原始文件删除后,软链接将会失效;例如windows的快捷方式。而硬链接不是独立的文件,没有独立的inode,本质是一组文件名与目标文件的映射关系(别名),删除原始文件后,因为引用计数的存在,本质目标文件并没被删除,硬链接仍然可以访问原始文件的内容。
在Linux系统中,库文件分为静态库(Static Libraries)和动态库(Dynamic Libraries)。这两种类型的库各有优缺点,适用于不同的场景。
定义:
优点:
缺点:
创建静态库
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/ #将静态库头文件复制到系统目录
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即可
这样,就可以将静态库安装到系统中,并且可以直接在其他程序中使用该库。
定义:
优点:
缺点:
创建动态库
.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在查动静态库时不会在当前目录下查,所以我们需要指定路径
注意链接动态库形成可执行程序后,运行可执行文件时,系统是需要找到动态库的位置,也就是运行可执行程序时,动态库是需要加载的,所以如何让系统找到动态库?
LD_LIBRARY_PATH
方法二:将库安装到系统中直接使用
/lib64
。可以使用cp
命令进行复制,例如:sudo cp libexample.so /lib64/ # 将动态库文件复制到系统目录
sudo cp libexample.h /usr/include/ #将静态库头文件复制到系统目录
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++只能选择静态库进行链接。
选择使用静态库还是动态库取决于具体的需求。如果关注程序的独立性和稳定性,且不介意较大的程序大小,可以选择静态库;如果更关心资源的有效利用,并且可以保证目标系统上有适当的动态库版本,那么使用动态库是更好的选择。