> 技术文档 > 程序员必备技能:Linux 链接与库文件入门指南

程序员必备技能:Linux 链接与库文件入门指南


一、软硬链接

ln -s //建立软链接,无 s 选项,建立硬链接

1. 建立软硬链接有什么区别呢?

程序员必备技能:Linux 链接与库文件入门指南

可以看到,软链接是一个独立的文件,因为它有独立的 inode ,硬链接不是一个独立的文件

文件 = 属性 + 内容。软链接的属性会保留在 inode里那内容呢保存的是软链接指向的文件的路径字符串

那么,硬链接是什么呢本质上,是在指定目录下,建立新的文件名和目标 inode的映射关系,并没有在系统层面创建新的文件。

2. 软硬链接有什么作用呢?

软链接就像 windows上启动应用的快捷方式,将可执行程序的路径进行重定向,无论是双击应用还是点击指定目录下的可执行程序都可以启动。

软链接的应用场景之一就是快捷方式,可以让用户无感知的进行软件升级

硬链接应用场景对文件进行备份

那么,硬链接在磁盘上会有多份文件,加载时会多占据内存空间吗?答案是不会

因为硬链接只是建立了文件名和 inode 的映射关系

3. 硬链接数

那么,当我们删除某个文件时,该文件是否真的被删除了呢是没有删除的。因为硬链接是对文件的备份,采用引用计数的方式管理文件,只有当引用计数为 0 时,文件才会被删除

程序员必备技能:Linux 链接与库文件入门指南

可以看到,myfile.c 的这个数字变为了2,我们把这个数字叫做硬链接数,它的本质是有几个文件名指向我们特定的 inode

程序员必备技能:Linux 链接与库文件入门指南

删除硬链接后,硬链接数变为了1

不知道大家有没有发现问题呢。为什么 ... 的硬链接数不是1呢?在解决这个问题之前,我们先来看下面的问题。

如果我们创建一个目录,它的硬链接数是几呢

程序员必备技能:Linux 链接与库文件入门指南

它的硬链接数为什么是2呢?我们只创建了一个 dir 目录,也没有对它进行硬链接啊

我们进入到目录里,看都有什么。

程序员必备技能:Linux 链接与库文件入门指南

.叫做当前目录, ..叫做上一级目录。那么,什么是当前目录,什么是上一级目录呢

程序员必备技能:Linux 链接与库文件入门指南
程序员必备技能:Linux 链接与库文件入门指南

可以看到,当前目录和 dir 目录的 inode 是一样的,所以当前目录就是 dir 目录的硬链接所以 dir的目录硬链接数是2。既然如此,上一级目录也就清楚了,它就是当前目录的上一级目录的硬链接

所以,上面刚才的那个问题又是为什么呢?自然是因为它也有当前目录和上一级目录了

这里还有一个重要的结论,我们可以对目录/普通文件设置软链接,但是硬链接,用户层面,不允许对目录设置硬链接

那不对啊,刚才的 . 和 .. 不是都对目录设置硬链接了吗那是OS设置的,内部会进行特殊处理,但是,对于我们而言,是不允许的。这是为什么呢?

如果允许我们对目录设置硬链接,那么在进行遍历目录时,就会出现路径环路问题(死循环)

二、库

1. 初识库

库分为动态库和静态库。对于库的初步认识,我们已经在构建完整工具链:GCC/G++ + Makefile + Git 自动化开发流程这篇文章中进行了初步介绍。这里就不再赘述了。

其实,库在 linux 中就是文件,不要忘记了,linux 下一切皆文件。这也就意味着,将来我们可以通过路径解析,找到其下的挂载分区,拿着目标文件的 inode 就可以在指定分区下查找该文件了

ar -rc //是将多个目标文件(.o)打包成一个静态库文件

程序员必备技能:Linux 链接与库文件入门指南

2. 理解库

所谓的库文件,其实就是我们以前写代码头文件,源文件分离由所有的源文件(具体函数实现的文件)编译,汇编形成目标文件,然后将所有的目标文件打包成一个文件的形式,这个文件其实就是库文件,而头文件就是对库文件里面的函数使用方法的说明

库的名字是去掉文件的前缀和后缀剩下的才是库的名称

静态库的本质是对源代码形成的 obj进行打包

3. 链接库

现在,我们已经对库有了基本的概念。接下来,我们就进行链接,形成可执行程序

程序员必备技能:Linux 链接与库文件入门指南

这是怎么回事?bash 说不认识 my_fopen… 函数,库文件,头文件我们都有,但是链接失败了这是因为,我们没有指定链接的库

但是,我们以前链接时也没有指定库啊,为什么也成功了。这是因为 gcc, g++ 编译器默认是认识C,C++的库的,所以不需要指定

程序员必备技能:Linux 链接与库文件入门指南

这一次它说,没有找到该文件,这又是为什么呢,库文件不就在当前目录下吗?这是因为,编译器会自动在特定目录下(一般是 /usr/lib 或者 /usr/lib64)查找库文件,而我们的库是没有在指定的目录下的,所以找不到

程序员必备技能:Linux 链接与库文件入门指南

-L代表链接的库的路径,-l代表链接的库名称

所谓库的安装,就是把库文件拷贝到系统默认路径下

程序员必备技能:Linux 链接与库文件入门指南

可以看到,链接静态库形成的可执行文件比链接动态库形成的可执行文件体积要大不少,这是因为,链接静态库是将静态库的内容进行了拷贝,所以体积会更大。这也意味着,链接静态库是不需要再依赖库的,而链接动态库依然需要依赖库

还记得它们两个的区别吗?

程序员必备技能:Linux 链接与库文件入门指南

\"\" 代表在当前路径下进行查找,若找不到就去库里面查找,否则就报错。 代表在库里面查找,找不到就报错

那么,如果我就想使用这种方式呢?也是有办法的。

程序员必备技能:Linux 链接与库文件入门指南

可以看到,这样直接进行编译是不行的。应该这样做。

程序员必备技能:Linux 链接与库文件入门指南

-I指定一个搜索路径,表明头文件搜索路径我们都知道代表的是在库里面进行查找头文件查找头文件也是在特定的路径下查找的,所以,我们也可以把头文件放在特定的路径下

程序员必备技能:Linux 链接与库文件入门指南

总结库 = 头文件 + 库文件

库安装本质是把头文件(/usr/include)和库文件(/usr/lib64)拷贝到系统指定的,默认的,编译器能找到的路径下

上面基于静态库的例子,让大家了解到了库。现在,就要谈谈动态库了。

形成动态库要使用 gcc 编译器

//fPIC:产生位置无关码gcc -fPIC -c *.c //把库形成 .ogcc -o ... ... -shared //把 .o打包形成动态库

程序员必备技能:Linux 链接与库文件入门指南

那么,直接运行 a.out看看结果吧。

程序员必备技能:Linux 链接与库文件入门指南

没找到这个文件,这是为什么呢?我们不是已经指明了动态库的路径了吗

我们指明动态库的路径只是告诉了 gcc 编译器,链接的时候在哪里找这个动态库,当可执行程序运行变成进程的时候,在物理内存上也要找到动态库才可以,因为,我们并没有将库拷贝到可执行程序里,因此还是需要依赖动态库

可是,以前C程序变成进程的时候并没有指明依赖的动态库啊这是因为,进程在运行的时候,加载器或者是OS会在系统默认的路径下查找到对应的库

所以,我们也可以把自己制作的库放在系统默认的路径下,也可以通过使用软链接的方式达到目的

程序员必备技能:Linux 链接与库文件入门指南

第三个方法是配置 LD_LIBRARY_PATH 环境变量

程序员必备技能:Linux 链接与库文件入门指南

方法四将动态库,查找路径全局有效,更改系统配置文件(/etc/ld.so.conf.d/)

程序员必备技能:Linux 链接与库文件入门指南

创建新的文件必须以 conf 结尾,然后需要重新加载库搜索路径(ldconfig),才能生效。新的文件内容就是动态库的路径

4. 库的注意事项

接下来,我们需要注意一些事项:

1. 动静态库同时存在,gcc/g++默认优先使用动态库,默认进行动态链接

程序员必备技能:Linux 链接与库文件入门指南

2. 一个可执行程序,可能会依赖多个库,如果我们只提供静态库,即便我们是动态链接,gcc也只能对静态库进行静态链接

程序员必备技能:Linux 链接与库文件入门指南

3. -static,要求我们必须采用静态链接的方案,静态库必须存在

程序员必备技能:Linux 链接与库文件入门指南

今天的文章分享到此结束,觉得不错的给个一键三连吧!