软硬链接
软硬链接的本质区别:有没有独立的inode
软链接
软链接有独立的inode -->软链接是一个独立的文件
特性:可以理解成为:软链接的文件内容,是指向的文件对应的路径
应用:相当于windows下的快捷方式
硬链接
创建硬链接,不是真正的创建新文件,创建硬链接究竟做了什么呢?
就是在指定的目录下,建立了文件名和指定inode的映射关系,仅此而已。
通过观察可以看到,我们在创建文件时,默认权限后面的那个数字是1,这个数字就是硬连接数,当我们把一个文件跟它硬连接的时候,这个数字就变成了2,删除掉其中一个文件的时候,又变成了1
我们可以把这个数字看成inode里面的一个计数器,我们叫他引用计数,表示和这个inode相关的文件名数目,当我们创建文件的时候,就必然有一个文件名与这个inode相关,所以硬连接数就是1,每次添加硬链接,计数器就+1,而当我们删除文件的时候,并不是直接把这个文件的inode删除,而是将这个文件的inode引用计数- -,当引用计数为0的时候,这个文件才被真正删除。
而当我们创建一个目录的时候,我们会发现这个目录的默认硬链接数是2,原因是除了这个目录名关联这个目录文件的inode之外,这个目录中的 . ,代表的就是当前目录,它也和目录的inode相关联,硬连接数就是2,除了 . ,还有 . . , 所以当我们当我们在这个目录下面再创建子目录的时候,硬连接数就变成了3
动静态库
写一个库
生成静态库
首先库中是没有main函数的。
我们平时编译一个源文件,我们都是直接一起编译这个源文件所需的其他.c文件,因为毕竟都是我们自己写的程序,但是如果我们也想让别人也运行这个程序,那么我们就需要把头文件和源文件也发过去。
但如果我们只把 .h 和 .o 文件给别人的话,别人也是可以直接用的。
但是可能有很多的 .o 文件这样用gcc编译的的时候要写一大堆,就很麻烦,所以我们需要将它们打包成一个静态库,lib开头 ,.a 后缀结尾
通常我们发布一个库的话,会有一个文件夹,头文件包含在文件夹中的 inlcude 目录中,库文件包含在文件夹中的 lib目录中,类似这样的形式
生成静态库:
ar -rc libmy.a mymath.o myprint.o
ar是gnu归档工具,rc表示(replace and create)
也可以用make工具,makefile文件内容:
1 libhello.a:myprint.o mymath.o
2 ar -rc libhello.a myprint.o mymath.o
3
4 myprint.o:myprint.c
5 gcc -c myprint.c -o myprint.o
6 mymath.o:mymath.c
7 gcc -c mymath.c -o mymath.o
8
9 .PHONY:hello
10 hello:
11 mkdir -p hello/include
12 mkdir -p hello/lib
13 cp *.h hello/include
14 cp *.a hello/lib
15
16 .PHONY:clean
17 clean:
18 rm -f *.o libhello.a
使用静态库
- 将头文件和库文件拷贝到系统路径下
其中头文件gcc的默认搜索路径是:/usr/include
库文件的默认搜索路径是:/lib64 or /usr/lib64
拷贝头文件:
sudo cp hello/include/*.h /usr/include/
拷贝库文件:
sudo cp hello/lib/*.a /lib64/
拷贝完成之后,此时还是不能编译通过,因为gcc,g++就是编译C/C++的工具,所以他们默认都是去寻找C/C++的库,所以我们还需要指定它们找我们自己的静态库
这样写:
gcc main.c -lhello
其中hello是我们的库名,去掉lib前缀和 .a 后缀。
- 直接硬使用库
gcc main.c -I hello/include/ -L hello/lib/ -lhello
-I ,I就是include,-L,L就是lib,在后面指定用哪个库即可
生成动态库
动态库命名格式是以 lib 开头,.so 结尾。
同静态库一样,动态库也是将 .o 文件打包在一起,但命令是:
gcc -shared myprint_d.o mymath_d.o -o libhello.so
同静态库的 ar 命令不同,同时gcc 后面要加 -shared 选项,就是共享的意思,动态库就是共享的。
还需要注意的是,这个被打包的 .o 文件的生成方式:
gcc -c -fPIC myprint.c -o myprint_d.o
打包完成,我们发布的方式同静态库发布。
使用动态库
编译方式依旧是:
gcc main.c -I output/include/ -L output/lib/ -lhello
但注意此时我们的output是这样的:
我们发现这样依旧编译成功了,同时我们运行a.out ,会发现它找不到这个动态库。
此时我们发现了存在这样两个问题:
- 在用gcc编译的时候,虽然你指明了库名为hello,但是我们可是有两个库啊,名字都为hello,不会混淆吗?
说明gcc默认优先对动态库进行动态链接
如果动静态库同时存在,默认用的就是动态库。
如果我们就只有静态库,没办法,gcc就只能针对该库进行静态链接。
如果动静态库同时存在,但我非要使用静态库呢? 那就要用到 -static 了,-static 的意义就是:摒弃默认优先使用动态库的原则,而是直接使用静态库。 - 我编译形成a.out 可执行程序的时候,我不是已经告诉你动态库的路径了吗,为什么在运行的时候找不到动态库呢?
其实从我这个描述,我们可以大概推出,可能是因为动态库编译和运行是分离的,你告诉了gcc 动态库在哪里,但是你没有告诉我加载器 动态库在哪里啊,动态库和静态库的重要区别就是静态库在编译的时候就直接加载到程序中了,加载器直接运行程序就可以了,根本不需要去找库在哪里
我们来看看动态库静态库,在程序加载的时候是怎么运行的:
对于一个可执行程序来说,如果用的是静态库,那么这个可执行程序代码中就已经包含静态库了,在运行的时候,就没有什么静态库了,代码从磁盘加载到内存中,在进程的地址空间中对应的就是代码段,通过页表建立与内存地址的映射关系,执行程序时在代码段一条条执行就行了。
而动态库是一个独立的库文件,动态库可以和可执行程序,分批加载。
如果一个可执行程序使用的是动态库,那么在程序加载进内存的时候,开始就只是可执行程序加载进内存,动态链接可执行程序中是没有动态库代码的,只是地址。建立地址空间,代码段在页表的映射关系,当沿着代码段执行程序的时候,此时遇到了一个动态库的地址,就把动态库对应代码也加载到内存中,与进程地址空间的共享区建立映射关系,也就是栈区和堆区中间的那段,指令就跳到这里来继续执行,执行完毕,跳到代码段向后执行。
所以我们可以看到,虽然编译的时候给gcc指明了库的路径,但是在执行的时候依旧需要找到库代码,所以也需要知道库的路径。
三种方法:
- 更改 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./output/lib/
将当前库的路径添加到环境变量中即可。
但这个方法你退出服务器,在登陆就失效了。
2. ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新
直接用sudo在该路径下新建一个文件,把库的绝对路径放进这个文件中即可
3. 使用软链接
ln -s 库的绝对路径 /lib64/libhello.so