目录
前言:
一、软硬链接
1.软链接
2.硬链接
3.硬链接数
4.软硬链接的区别
5.使用unlink删除链接的文件
6.目录文件链接数( . 和 .. )
二、静态库的制作和使用
1.制作静态库
2.使用静态库
2.1方法一
2.2方法二
2.3方法三
三、动态库的制作和使用
1.制作动态库
2.使用动态库
2.1方法一
2.2方法二
2.3方法三
2.4方法四
四、命令总结
1.关于软硬链接:
2.关于动静态库和连接命令:
3.关于gcc编译器选项
总结:
前言:
嘿嘿,各位如果第一次看到这里会很懵,因为这里并没有从头到尾讲解。了解软硬链接至少要知道文件系统这方面知识,我把他们总结成了文件操作篇,可以去看我前面的文章(相当于文件篇一二三),再来看这篇文章就畅通无阻了(有基础的除外)。
一、软硬链接
1.软链接
软链接本质是一个独立的文件,我们使用以下命令创建软链接:
ln -s 源文件 创建软链接目标文件
大家可以理解为Windows中的创建快捷方式,当时我们删除这个文件的其他相关文件,即使快捷方式存在也无法打开。我们可以理解为创建了一个新文件,这个文件记录的是路径。
软链接一般就是相当于创建快捷方式,我们在Linux中是为了方便减少路径的输出和记忆使用的。
2.硬链接
使用创建软链接的命令不加上-s选项就是创建硬链接。我们来观察区别:
可以发现,我们向硬链接文件追加内容,源文件也有内容也同时修改,毕竟指向的是同一inode。当我们把硬链接文件删除,并不会影响源文件。
3.硬链接数
我们来讲解我们使用ll命令展示的目录文件中从来没有提及的一个参数:硬链接数
硬链接本质不是一个独立的文件。
这个链接数实际上是inode中的一个属性(count),被称为引用计数,记录当前有多少指针指向该inode。
4.软硬链接的区别
硬链接不是独立的文件,没有独立indoe,硬链接本质是一组文件名和已经存在文件的映射关系。
软链接有独立的inode,内容上保存的是目标文件的路径!
我们也可以用硬链接对文件重命名和备份。
硬链接和复制不是同一回事!复制在磁盘上是开辟空间的,而硬链接只是改变inode的一个属性,并没有新开辟空间。
5.使用unlink删除链接的文件
我们可以使用unlink删除链接的文件,当然也可以删除普通的文件。
6.目录文件链接数( . 和 .. )
我们创建一个目录empty,之后观察链接数,之后进入empty,并在empty中创建目录dir,返回上级目录lesson20,在lesson20创建一个普通文件,观察empty的硬链接数的变化。
(注:dir默认创建后链接数是2)
我们先分析dir目录为什么硬链接数是2:
因为.代表当前文件。
而为什么我们在empty中创建一个新目录dir之后,empyt的硬链接数变为了3?是因为dir中的 .. 代表上级文件,其inode与其相同。
但是对于根目录,其中..和.对应的都是他自己:
Linux中,不允许对目录进行硬链接:
而对软链接,可以对目录操作,因为软链接里面存储的是路径。
二、静态库的制作和使用
回顾一下,我们之前说过,静态库相当于在宿舍装了台电脑;而动态库相当于是在去网吧玩电脑。各位直接看操作。
1.制作静态库
我们先手动制作一个静态库,不要想的很复杂,也就是写了一个.c文件和.h文件,但是没有写main方法。
我们将之前的文件缓冲区代码的my_stdio.c和my_stdio.h两个文件都拷贝进去,注意这里只是为了有几个头文件和.c文件,方便展示,文件中的内容与本博客中知识几乎无关,但是大家有兴趣可以看博主之前写的文件缓冲区的博客。
我们要将这些方法给别人用,制作静态库,.h头文件必须暴露,没必要藏。但是.c源文件需要隐藏,所以我们可以将.c编译为.o文件,之后把所有.o文件连接起来形成静态库。
之后就是将这些.o文件打包形成静态库了,我们要使用一个新命令:
ar -rc xxx.a
当然刚才由于作者失误并没有打包.o文件,我们已经生成了库文件,此时需要往里面添加.o文件:
接下来我们来演示把头文件和库文件交给另一个人,看他如何使用。
2.使用静态库
2.1方法一
我们可以把库安装到系统中,把.h头文件也装到系统中,Linux系统中搜索头文件在/usr/include目录中,所以我们先把所有的头文件移动到该目录中(注意要提权)。
一般把库文件装在/lib64目录下:
所以Linux中的安装本质都是拷贝。
我们创建一个other目录并进入,写一个main.c源文件,使用刚才的库和头文件。
之后我们编译这个.c文件:
目前属于编译时报错,因为/lib64中有很多库,我们不知道到底要找哪一个。
但是我们平时写的不包含自己写的库时,gcc/g++不需要指定库文件,是因为他们本身就是编译C/C++的,所以这些编译器一定本身就认识这些库的,不需要指定。但是我们的库相当于第三方库,我们要添加选项。
2.2方法二
当然还有其他方式,我们先把库和头文件都删除。
当时我们还没有删除可执行文件,我们进行完删除操作之后再执行a.out可执行文件:
发现还能执行。 因为程序在编译链接的时候把库的代码链接到可执⾏⽂件中,程序运⾏的时候将不再需要静态库。
我们可以使用ldd(查看可执行文件或共享库(动态库)所依赖的动态库)命令。
这些静态库不一定要安装到系统中,可以就直接把库和头文件给你,你自己操作一番就可以使用。
我们在写程序的时候,自己写的头文件会使用双引号包起来,也就是在当前路径下找。
gcc在查库的时候,不会默认在当前路径下查找,是会去系统指定的路径下查找,所以我们要指定路径。
-L:告诉编译器,编译的时候,除了系统路径,也要在我指明的路径下查找。
2.3方法三
库发布的时候,把所有的头文件都放在include目录中;把库放在lib目录中。
为了方便创建,我们写一个makefile文件:
这里面的内容我们不做过多讲解,可以去看博主之前写的makefile文件的博客。
我们当然也可以把发布的文件压缩,方便去使用:
我们把发布的压缩包拷贝到要用的地方,并解压,此时直接编译肯定报错:
接下来要使用-I来指定除了系统路径找,还要到指定的路径下找头文件,当然还要指定目录,指定库名。
当然这里选项可以不带空格。
三、动态库的制作和使用
1.制作动态库
当我们要链接形成动态库的时候,需要使用gcc编译器,并且不能直接编译,需要带上-shared选项告诉编译器形成的是动态库。
gcc -shared
当我们从.c文件形成.o时,因为要形成动态库,所以我们在形成.o文件时,也要加上一个选项 -fPIC 形成与位置无关码(嘿嘿,静态库加载其实和函数和变量的地址是有关的,在连接的时候会重定位,但这里是动态库,大家可以去看看《程序员的自我修养》这本书)。
fPIC:flag position ignore code
之后和静态库一样,将所有.o文件打包成动态库,但是记得加上-shared选项。
2.使用动态库
动态库又是如何让别人使用呢?还是三个方法。
2.1方法一
将头文件和动态库安装到(拷贝)系统路径下:
这里依旧需要指明用到的是哪个库:
这里也从侧面说明了动态库无论是编译期间还是链接期间都需要用到。
我们再次执行ldd命令看当前可执行程序依赖哪个库:
我们把头文件删掉,把动态库也删掉。此时没有头文件是不会有影响的,因为头文件最多使用到编译阶段(步骤为:预编译、编译、汇编、链接),此时已经结果链接器生成了可执行文件,则头文件是否存在已经不重要,但是动态库没有则无法执行文件。
2.2方法二
我们将拷贝的动态库和头文件都从系统目录中删除,依旧还是只有头文件和动态库,这次就和静态库的步骤相似,需要指定路径和库名了:
2.3方法三
我们还是要把库发布,所以先更改makefile文件:
依旧是指定路径名找头文件,指定库路径名和指定库名:
此时你可能会发现,这和刚才的静态库好像没有区别,步骤都是一样的。
但是如果此时我们执行程序:
它并没有运行。我们再次ldd看看它依赖哪些动态库:
但是我们已经告诉过编译器库在哪里了,为什么没有执行?
动态库是要加载的!在程序运行的时候,和gcc没有关系了!
运行时是OS要加载你的程序,所以这个报错是OS说的。静态库OS不需要找。
OS是在/lib64中找对应的库的,我们又是该如何给OS指定路径,查找自己的库?
- 拷贝到系统默认路径下,比如/lib64
- 我们可以建立一个软链接
- Linux中,OS查找动态库,是从环境变量中查找的。
环境变量?对,没错,就是这个:LD_LIBRARY_PATH
(这里因为作者配置过vim编译器,大家可以去下载一个vim编译器,当然还有其他方法链接到动态库)
我们使用export临时把动态库的路径导入LD_LIBRARY_PATH这个环境变量中。
但是我们知道,这只是内存级别的环境变量,我们可以将其写入到配置文件中(.bashrc)永久生效。
2.4方法四
如果我们想让系统找到库的路径,系统有全局的配置路径/etc/ld.so.conf.d/
这里面可以写各种配置文件。配置文件中, 我们可以在里面只写一个路径即可。
我们在里面创建一个112.conf配置文件,并写入动态库的绝对路径。
当我们把该路径设置到配置文件中,操作系统要重启,我们可以加载一下使用ldconfig命令:
这里其实一般建议使用方法一和方法三。
四、命令总结
1.关于软硬链接:
ln:创建软硬链接。
- -s:创建软链接(不带该选项则是硬链接)。
unlink:删除链接文件,当然也可以删除非连接任意文件。
2.关于动静态库和连接命令:
ar:形成静态库。
- -r:静态库中有该.o文件就替换它。
- -c:静态库中没有该.o文件就创建它。
使用形式:
ar -rc xxx.a xxx.o xxx.o
ldd:查看当前可执行文件依赖哪些动态库。
3.关于gcc编译器选项
-l:(这是小写的l)编译时指定库的名称选项。此时要把库的前缀lib和后缀.a/.so去掉。
-L:告诉编译器,编译的时候,除了系统路径,也要在我指明的路径下查找。查找的库文件。
-I:(这是大写的i),告诉编译器,编译的时候,除了系统路径,也要在我指明的路径下查找头文件。
-shared:将所有的.o打包形成.so动态库文件。
-fPIC:(flag position ignore code)将所有的.c形成与位置无关的.o文件。
总结:
这张内容其实很有用,但是我们并没有深入讲解,因为这里涉及很多关于编译原理和程序编译链接的知识,很多人都对这部分知识不以为然,但是真的很重要!有本书叫做《程序员的自我修养》,确实这部分知识其实从底层学习会让我们在日常生活中更好的知道一个程序崩溃时问题到底出现在哪,能更好的解决问题,这确实是一个程序员的自我修养!
至于ELF格式文件等之类的内容,作者会先暂停一段时间,因为作者还没有彻底搞懂那部分知识,写博客害怕误人子弟,不过等作者大彻大悟之时,一定会把这部分知识补上!敬请期待吧!