一.对软硬链接的补充
1、无法对目录进行硬链接
为什么呢?
首先,我们在访问文件时,每一个文件都会有自己的dentry结构,这些结构会在内存中维护一棵路径树,来快速进行路径查找。但是如果某个节点直接使用硬链接到了根节点/某个节点,此时就会在树中出现环路。这就会导致再路径树种查找路径出现死循环的情况。
所以再Linux操作系统中,不支持给目录创建硬链接,而只能给普通文件硬链接。
但其实我们每个目录底下的两个隐藏文件.和..其实也是硬链接,但它们属于系统开了绿灯,因为它们是特殊字符,所以在查找时可以特殊处理,不易导致路径环问题。
2、可以对目录进行软链接
首先,软链接出来的文件才是实际意义上的链接文件,因为它的开头是l
并且,软链接之后的文件有独立的inode,它的内容是目标路径字符串。所以它并不会直接的影响路径树的结构,它只是通过类似解引用的方式实现访问目标路径的目的。
3、要想删除一个软/硬链接,除了直接删除链接的文件,也可以使用unlink命令,取消链接属性
二.什么是库?
我们学习c/c++至今,每一次代码的编写都使用到了c/c++的标准库。库其实就是将一些常用的方法总结了起来,并进行实现,之后当我们再需要使用该方法时,就直接使用库中实现好的即可。
所以,库其实就是常用方法的二进制集合。
本质上,库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库分为两种:
- 静态库Linux(.a) windows(.lib)
- 动态库Linux(.so) windows(.dll)
三.静态库的制作与使用
现在,我们实现了加法方法和减法方法,将其制作成为库。
0x1.阶段1
在没有将其形成库之前,如果想要用户使用我们的方法,我们就得将我们实现的所有.h文件和.c文件直接给用户。但是,直接将.c给用户,这不就把我们实现的方法给暴露了么,我只给你用,但你不能看我具体的实现。
所以,我们就先将.c文件通过遍历但不链接形成同名的.o文件,然后将头文件和.o目标文件给用户使用即可。
而在用户视角,我们拿到了.h和,.o文件,然后只需要编写自己的代码即可。最后将所有的.o文件链接起来即可。
通过gcc命令对所有的.o目标文件进行链接,形成可执行程序。
0x2.阶段2
虽然这样用户可以使用我们的方法,但是如果我们实现的方法有几百上千个头文件和源文件呢?我们需要将这么多个源文件都生成.o文件,再传给用户。虽然这样对我们没什么,但是对用户就有点麻烦了。
所以,我们可以先将所有的.o目标文件打包成为一个库,再将该库和头文件发给用户使用即可。这样用户就不必接收多个目标文件了。
ar -rc dst src
使用上述命令,将所有的.o文件进行归档,形成一个归档文件。另外,我们形成的静态库要以.a结尾(.a后面还可以跟其他后缀),以lib开头,并且我们静态库真正的名字是去掉前后lib和后缀.a之后剩余的部分。
有了静态库,在用户方,就只需要拿到静态库和头文件即可。用户使用的时候,可以直接将该静态库与自己的.o目标文件进行链接。
.a文件本质上是一种归档文件,不需要使用者进行解包,而直接使用gcc/g++直接链接即可。
gcc -o code usercode.o -l mymethod
我们使用-l选项,告诉编译器我要链接哪一个库。
但是直接这样,编译器是找不到库的,因为编译器默认只会在系统指定的目录下寻找动静态库。所有想要使用我们自己的库,还要使用-L选项,指定静态库的位置。
gcc -o code usercode.o -L . -l mymethod
0x3.阶段3
虽然形成静态库,避免了多个目标文件的问题,但是头文件依旧很多。所以一般的做法都是新建目录,在该目录下创建include和mylib子目录,分别将头文件和库放入这两个目录下,然后将这个目录的内容压缩发送给用户。
然后用户就在使用gcc来进行链接操作,但是这次链接时,gcc命令还要多加一个选项 -I(大写i),用来指定寻找头文件的路径
gcc -o code usercode.o -I ./lib/include -L ./lib/mylib/ -l mymethod
-I选项就是告诉编译器,除了在系统指定的目录下搜索头文件,也要从我指定的目录下,搜索头文件。
至此,静态库的制作和使用就结束了。
四.动态库的制作与使用
动态库的制作直接使用gcc遍历器来生成。我们写一个makefile来自动化构建我们的动态库以及头文件与动态库的打包。
libmymethod.so:add.o sub.o
gcc -o $@ $^ -shared
%.o:%.c
gcc -fPIC -c $<
.PHONY:output
output:
mkdir -p dylib/include
mkdir -p dylib/mylib
cp -f *.h dylib/include
cp -f *.so dylib/mylib
tar czf dylib.tgz dylib
.PHONY:clean
clean:
rm *.o *.so
生成动态库时用gcc命令时要加上-shared选项,表示生成共享库格式
另外,在生成目标文件时,也要加上选项 -fPIC:产生位置无关码
我们将生成好的压缩包传给用户,让用户使用动态库。生成动态库时要以lib开头,.so结尾,剩余部分才是真正的动态库名。
我们在链接动态库时并不会出现问题,可以生成对应的可执行程序,但是在执行的时候,就会报错,说找不到我们自己的动态库?
我们可以使用ldd命令,查看可执行程序所使用的动态库!
我们看到,我们自己的动态库确实没有被找到,这是为什么呢?我们不是在编译的时候指定了在哪里找我们的动态库了么?
原因是:我们在链接的时候指定动态库的位置,只是让编译器找到了动态库,可是系统并不知道!!!操作系统并不等于编译器。而操作系统查找动态库只会在系统指定的目录下查找,所以找不到我们的动态库。
这与静态库不同,链接静态库实际上就是将静态库的内容拷贝到自己的代码中,所以在使用静态链接之后,静态库便没有用了。
而我们使用动态库,默认采取动态链接,动态链接后的可执行程序内部并没有库方法的实现,它类似于函数调用,指定到库方法时会跳转到对应的动态库中,执行完毕再返回。所以动态链接对动态库的依赖性很大。所以,使用动态库后,系统必须得找到对应的动态库。
那怎么解决呢?
法1:将我们的动态库拷贝到系统中
法2:建立同名软链接
法3:将动态库所在路径添加至环境变量LD_LIBRARY_PATH中
操作系统查找动态库,除了再指定的系统目录下查找,也会再环境变量LD_LIBRARY_PATH中查找。
但是,我们的环境变量是内存级的,当我们退出终端之后,环境变量就会被还原。我们可以修改配置文件来达到永久修改环境变量的目的。
法4:在/etc/ld.so.conf.d/下新建一个conf文件,并将动态库所在路径写入
但是注意,我们写入之后依旧无法直接使用,我们得使用ldconfig命令重新加载库更新路径,之后便可以生效
五.总结
1.动静态库中,其实根本不需要包含main函数,所有的库不论动静,本质上都是源文件对应的.o目标文件。
2.gcc/g++链接所有非c/c++标准库,都需要指明-L -l。
3.所有我们所说的将库安装到系统中,实际上就是将库拷贝到对应的系统目录下。
4.gcc/g++,在动态和静态库都存在的情况下,默认使用动态库
如果非得静态链接,必须加-static选项
如果只存在静态库,那就只能使用静态链接
5.在Linux操作系统中,默认情况下安装的大部分库,都是动态库