W...Y的主页 😊
代码仓库分享💕
目录
文件的软硬链接
动静态库
回归动静态库
创建动静态库
生成静态库
生成动态库
库搜索路径
文件的软硬链接
上篇文章中我们讲述了文件系统从硬件到软件,理解了如何创建一个文件的具体流程,今天我们先来看一下文件的软硬链接。
我们看如何创建一个硬链接:
指令: ln 目标文件 硬链接文件名
硬链接创建的不是一个文件,其inode和目标文件的inode的是相同的,而且我们可以看出在没有创建硬链接时我们的test.txt权限和拥有者中间有一个数字从1变成了2这是什么呢?这个数字表示硬链接数。当有硬链接指向文件时,这个数字就会+1,当我们删除时这个数字就会-1,就好比C++中的引用一样。
当我们给test.txt中写入内容时,无论是test.txt还是link.hard都会指向相同的内存空间,显示相同的内容。当我们将test.txt删除后查看link.hard内容时我们照样可以查看。说明这个内存空间还没有被释放,知道硬链接数为0时才会将其释放。只有引用计数减到0才会将它的block bitmap 、inode bitmap对应的比特位清空。
当我们创建一个目录时,其默认的硬链接数就是2,那是因为进入目录后隐藏文件中有一个.的inode与当前目录i弄得相同,这就是为什么硬链接数为2的原因。
如果我们在dir目录中继续创建一个目录,那么dir的硬链接数就会成为3,因为再创建的目录中的上级目录..的inode也是这样。所以一个目录下有多少个子目录:硬链接数 -2得到的。
如何给一个文件创建一个软链接:
指令:ln -s + 目标文件 软连接文件名
我们可以看到软链接本质是一个文件,其有一个独立的inode。而软链接文件中存放的内容就是目标文件的路径。这就好比Windows中的快捷方式一样。
所以软链接的应用场景一般是创建快捷方式,如果一个可执行程序在目录中嵌套太深,我们每次想指向其程序要么就要进入到可执行程序的当前目录下,要么就要将可执行程序与当前用户所在的路径下的相对目录进行输入非常繁琐,所以我们可以使用软链接的方式进行简化。
Linux不允许给目录建立硬链接,因为寻找文件是通过路径寻找的,如果我们给根目录创建硬链接就会导致环路问题出现死循环递归。除非系统自己给目录建立硬链接!!
动静态库
回归动静态库
动静态库是编程中两种常用的库文件类型,它们在程序的编译和链接过程中起到不同的作用:
静态库(Static Library):
- 静态库文件通常具有
.lib
或.a
的文件扩展名(取决于操作系统)。- 它们在编译时被链接到最终的可执行文件中。这意味着静态库中包含的代码和资源在编译阶段被复制到最终的程序中。
- 优点包括减少运行时依赖、可能提高程序的加载速度,以及在某些情况下提高程序的执行效率。
- 缺点是生成的可执行文件体积较大,因为包含了库的代码。
动态库(Dynamic Library):
- 动态库文件通常具有
.dll
(Windows)、.so
(Linux)、.dylib
(macOS)等文件扩展名。- 它们在程序运行时被加载。这意味着程序在运行时才链接到库文件,而不是在编译时。
- 优点包括减少磁盘空间的使用,因为多个程序可以共享同一个库文件;动态库的更新也更加方便,不需要重新编译使用该库的所有程序。
- 缺点是程序启动时需要加载库,可能会稍微增加启动时间;程序运行时依赖于库文件的存在,如果库文件被移动或删除,程序可能无法运行。
我们一般可以使用ldd + 可执行程序进行查看文件的动静态库使用情况。
上图我们可以看出使用了libc.so.6,去掉前缀lib去掉后缀.so.6就只有C了,证明使用了C库。
使用file + 可执行程序就可以看出文件使用的是动态链接还是静态链接。 gcc与g++编译器默认链接的都是动态库,若我们想使用静态库进行链接就得使用static,指令为:g++ 目标文件 -static即可进行静态链接。
创建动静态库
一个可执行程序是由若干个头文件以及.c文件 + main函数文件进行编译合成的,实质是将许多.c文件先编译成.o文件,然后.o文件与.h文件进行组合成为可执行程序。但是当我们想隐藏源文件内容时,我们只需要将编译好的.o文件和.h打包再配合main.c文件即可。
那为什么要有库这个概念呢?1.提高开发效率。2.为了隐藏源代码
所以我们要学习怎样对许多.o文件变成库文件:
测试程序
/add.h/
#ifndef __ADD_H__
#define __ADD_H__
int add(int a, int b);
#endif // __ADD_H__
/add.c/
#include "add.h"
int add(int a, int b)
{
return a + b;
}
/sub.h/
#ifndef __SUB_H__
#define __SUB_H__
int sub(int a, int b);
#endif // __SUB_H__
/sub.c/
#include "sub.h"
int sub(int a, int b)
{
return a - b;
}
///main.c
#include <stdio.h>
#include "add.h"
#include "sub.h"
int main( void )
{
int a = 10;
int b = 20;
printf("add(10, 20)=%d\n", a, b, add(a, b));
a = 100;
b = 20;
printf("sub(%d,%d)=%d\n", a, b, sub(a, b));
}
生成静态库
[root@localhost linux]# ls
add.c add.h main.c sub.c sub.h
[root@localhost linux]# gcc -c add.c -o add.o
[root@localhost linux]# gcc -c sub.c -o sub.o
生成静态库
[root@localhost linux]# ar -rc libmymath.a add.o sub.o
ar是gnu归档工具,rc表示(replace and create)
查看静态库中的目录列表
[root@localhost linux]# ar -tv libmymath.a
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 add.o
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 sub.o
t:列出静态库中的文件
v:verbose 详细信息
[root@localhost linux]# gcc main.c -L. -lmymath
-L 指定库路径
-l 指定库名
测试目标文件生成后,静态库删掉,程序照样可以运行。
生成动态库
生成动态库和静态库的方法有所不同。
shared: 表示生成共享库格式
fPIC:产生位置无关码(position independent code)
库名规则:libxxx.so
首先我们想要生成.o文件时使用gcc/g++时不能直接进行gcc -c 而是要加上-c -FPIC这两个选项(一会讲述-FPIC有什么用)。然后进行打包,而打包也不使用ar -rc这个指令,直接使用gcc -shared即可。
示例:
[root@localhost linux]# gcc -fPIC -c sub.c add.c
[root@localhost linux]# gcc -shared -o libmymath.so *.o
[root@localhost linux]# ls
add.c add.h add.o libmymath.so main.c sub.c sub.h sub.o
当我们生成了动态库后我们就可以进行编译了,编译的方法和静态库相同。使用gcc -L+相对路径 -l动态库名称(去掉前面lib,去掉后面.so即可)。因为我们做的动静态库都是第三方库,操作系统只会在自己默认的库中寻找,一般是lib64目录下进行的。
我们可以这么理解 L :你要链接的库在哪一个路径下。l:你要链接的库是谁。
但是编写库的人未来要给别人(用库的人),交付的是:头文件+库文件,不给源代码我们想要一键式发布直接交给别人我们应该怎么办:
lib=libmymath.a
$(lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.c
gcc -c $^
.PHONY:clean
clean:
rm -rf *.o *.a lib
.PHONY:output
output:
mkdir -p lib/include
mkdir -p lib/mymathlib
cp *.h lib/include
cp *.a lib/mymathlib
我们tree一下就可以发现将头文件全部放在include目录中,将库文件放在lib目录下。
我们继续使用tar czf + 压缩包名 + 目标内容指令将mylib目录生成压缩包,然后将压缩包上传到一些网站上供别人使用 。但是这样的缺点就是头文件和库文件都不在同一个路径下,所以我们得使用gcc指令的选项就不仅仅使用l和L还要加一个I,I这个选项是告诉编译器头文件在哪个路径下的。
我们必须使用-I -L -l进行gcc编译出可执行程序。
动态库的使用也是有很多的小细节,使用不当也会产生找不到动态库或者找不到头文件的问题。 就像我们直接编译好了可执行程序,在运行时系统还是找不到我们的库在哪里。 这是为什么呢?
静态库和main函数在形成可执行程序后我们就可以直接运行程序,因为静态库的编译原理就是将我们的静态库内容拷贝到代码中,之后我们再也不需要库的内容。但是动态库不同,当我们使用动态库编译后形成可执行程序后运行程序时必须要找到对应的库的位置,因为动态库没有将内容拷贝到代码中,所以我们在运行时也需要动态库。当使用编译器的时候我们已经告诉动态库的位置,但是在运行时我们使用的是操作系统和编译器没有任何关系,所以OS也不知道动态库的位置,运行就会报错。
库搜索路径
从左到右搜索-L指定的目录。
由环境变量指定的目录 (LIBRARY_PATH)
由系统指定的目录
/usr/lib
/usr/local/lib
方法一:将头文件和库文件安装到系统中
如果我们不想这样编译一个代码,也可以将头文件拷贝到系统默认路径下,所谓把库安装到系统中本质就是把对应的文件拷贝到系统指定的路径下!(系统路径一般在/user/include路径下)但是路径的权限都是root,我们就要使用sudo来进行拷贝。库文件也是一样的,需要我们将其拷贝到/lib64路径下。这时我们就不用使用-L和-I了,只需要-l进行声明使用哪个库即可!
方法二:建立一个软链接
我们可以使用ln -s + 目标库绝对路径 /lib64/libmyc.so创建一个软连接即可。
方法三:修改环境变量
在环境变量中有一个LD_LIBRARY_PATH变量
我们的环境变量中可能有也可能没有这个环境变量,我们使用echo $LD_LIBRARY_PATH进行配置即可。
在系统运行时,动态库查找的辅助路径!!!
在我们的家目录下有配置文件.bash_profile和.bashrc,这两个隐藏文件里面就可以配置环境变量,我们也可以使用vim将我们需要的路径写入里面。
方法四:更改配置文件
Linux下含有一个文件夹其中包含有关动态库加载的配置文件,里面只含有一个路径。我们可以在其目录下创建一个相同格式的空白文件,再将我们库的路径填入其中保存即可。
系统中有一个ld.so.conf的配置文件在/etc/路径下:
这时一个目录,我们在目录中可以在该目录下自定义用户级的配置文件,我们可以创建一个名字随便起后缀.conf的文件,在里面添加库文件路径即可。因为这些都是root权限的文件,所以我们使用root账户或者使用sudo来执行指令。
添加完毕后使用ldconfig进行配置文件生效后就可以使用动态库了!!!
以上就是本次的内容,感谢大家观看!