前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Linux】软硬链接与动静态库

【Linux】软硬链接与动静态库

作者头像
诺诺的包包
发布2023-10-15 13:55:37
1690
发布2023-10-15 13:55:37
举报
文章被收录于专栏:个人笔记总结个人笔记总结

环境:centos7.6,腾讯云服务器 Linux文章都放在了专栏:【 Linux 】欢迎支持订阅 🌹 相关文章:Linux】动静态库以及动静态链接 Linux】基础IO_文件系统

软硬链接

软链接

我们知道,每一个文件都会有自己的inode编号,我们可以通过如下指令来查看:

代码语言:javascript
复制
ls -l -i

 我们发现,每一个不同文件的inode编号都不相同,所以inode可以说是用来标识文件的标识符。接下来,我们通过下面指令来给mysoft文件,创建软链接:

代码语言:javascript
复制
 ln -s mysoft mysoft-s

 我们发现,创建出来的软链接文件的inode编号与源文件并不相同,并且,软链接文件的大小远远小于源文件的大小。实际上,软链接又称为符号链接,软链接文件是一个独立的文件有自己的inode属性以及内容软链接文件的内容就是只包含了源文件的路径名称。因此大小要比源文件要小很多。这就类似于Windows下创建的快捷方式文件。

当然,假如我们将源文件删除或者改变源文件路径后,那么软链接文件也就运行不了,因为软链接文件内仅仅只是源文件所在的路径,当源文件不在时,软链接文件也就“失效了”。如下所示:

 硬链接

接下来我们来看硬链接,我们可以通过下面命令给mylink文件创建硬链接:

代码语言:javascript
复制
ln mylink mylink-h

我们发现,创建出来的硬链接文件不管是大小,还是inode编号都与源文件相同,并且那个数字也从1变成了2。可以看出来,硬链接代表的意义就是给源文件起一个“别名”,也就是说,虽然它们名字不同,但代表的都是同一个文件,因为它们的inode编号相同!。而这个数字2,就是用来统计一个文件究竟有几个“别名”。因此这里变成了2。

当然,假如此时我们将源文件的路径给更改,并不会发生什么影响,但是假如我们将源文件给删除。我们会发现,那个数字就会由2变成1,但依然可以运行。

接下来我们看一个现象:为什么目录文件硬链接数为2?

 答案是很简单,因为每一个目录文件,即使是个空目录,里面也一定有如下内容:当前路径文件.和上级路径文件..。

 当然,假如我们在B目录文件下再建立一个新的路径文件,那么B的硬链接数就会由2变成3,因为新的路径文件下都会有一个..文件,..标识上级路径,也就是B,因此B的硬链接数会由2变3。

这里有一点需要注意:我们可以给普通文件创建硬链接,但不能给目录文件创建硬链接,因为假如能给目录文件建立硬链接,就容易发生环路路径问题。

 软硬链接的区别

  1. 软链接又称为符号链接,是一个独立的文件,有单独的inode编号,该文件的内容为目标文件的路径。
  2. 硬链接是将不同的文件名关联到同一个inode节点,名字不同,但都是指同一个文件。
  3. 软链接可以给目录创建,但硬链接不可以给目录创建
  4. 删除原目标文件后,软链接文件会收到影响,会“失效”,但硬链接文件不受影响,依然可以正常运行,仅仅只是硬链接数-1。
  5. 硬链接的文件属性类型与原文件保持一致,而软链接文件的属性类型为l,l表示链接文件
  6. 软链接的大小很小,硬链接的大小与原目标文件一致,因为硬链接文件本身就是原目标文件的“别名”。

动静态库

什么是库文件?

我们在编写C/C++代码时,实际上一直都在用库(c/c++标准库),在编写代码时,有很多库函数诸如printf等,我们为什么能直接拿来用呢?是因为我们包含了各自对应的头文件,而头文件的内容包含了该函数的声明,具体的实现方法则在库文件中,在链接阶段,我们经过编译后的.o文件会与库文件进行合并,最终形成可执行程序。

因此,可以这么来说:库文件中提供函数的实现方法,而头文件中提供函数说明。两者配套使用。实际上,库其实就是大量方法文件形成的.o文件的集合

为什么要存在库?

库的存在就是为了提高开发效率,举个例子,假如没有c/c++标准库,我们在写代码时就要手动将printf、cout等高频函数的实现方法进行编写,这样就大大减少了我们的开发效率。

而且假如在日常开发中,假设别人想要使用我们实现的一些接口,但是我们又不想让别人看到我们是如何实现的,此时我们就可以将接口的实现打包成一个库,然后直接将库文件和对应头文件发送给对方即可。

库又分为动态库和静态库,两者的优缺点在之前的文章已经详细讲解<<点击跳转>>,这里主要讲如何库的使用和原理。

如何制作和使用第三方库

第一方库:语言提供的库(如c/c++标准库)

第二方库:操作系统提供

第三方库:other提供,比如我们接下来自己制作的动静态库

静态库的打包

静态库的打包主要分为两个步骤:

  1. 将存放方法的源文件进行编译,编译后(含预处理--编译--汇编)生成.o为后缀的可重定位二进制目标文件。(gcc -c)
  2. 将所有的.o文件使用ar -rc指令,进行打包形成静态库。
  3. 将静态库与头文件压缩后发送给他人即可供他人使用

这里我简单举个例子:

假如我自己写了一个Add和Sub接口的实现,然后其他人想直接用我们的接口,此时我们想在不将方法的具体实现暴露出来,仅仅是将接口的功能给他人使用,此时我们就可以打包成库,发送给别人即可。如下图:

第一步:使用g++ -c的指令,将方法实现的源文件进行编译后生成.o结尾的可重定位二进制文件:

  第二步:使用ar -rc 指令将所有的.o文件进行打包

注意:ar是gnu归档工具,通常用指令ar -rc来进行静态库打包。而ar -tv指令则可以查看静态库的内容。如下:

 接下来,我们再将所有的头文件都放在同一个文件下,如下所示:

第三步:使用tar指令将库文件与头文件进行压缩,然后发送给其他人 使用即可

静态库的使用

 此时我们切换身份,我们作为otherPeople,我们想要使用这个静态库,该如何使用呢?

第三方库的使用规则

首先,任何第三方库的使用,必须在编译时要标注三个要素:库所在的路径、对应头文件的路径、要链接的库名(库名需要去掉前缀与后缀)。

第一种方式使用静态库:编译时手动指定

gcc/g++编译选项

含义

-L

指定库所在的路径

-I(大写i)

指定头文件所在路径

-l(小写L)

指定库名称(去掉前后缀)

如下,假如我要使用这个静态库,我先将这个压缩包解压:

 接下来我们进行g++编译,这里编译时我们手动指定所需要的库名(-l)、库路径(-L)、头文件路径(-I)。

对于静态库的使用,还有第二种方法如下:

第二种方式使用静态库:将头文件以及库文件安装在系统目录

由于gcc/g++在编译时,会默认去系统目录搜索,进行路径匹配,这也是为什么我们平常用c/c++标准库时,编译时不需要再指定库路径和头文件路径。

库所在系统路径:/lib64 头文件所在系统路径:/usr/include

 这里需要注意的是,一般我们不要轻易修改系统的环境,因此我们用完后要手动删除,否则会一直存在。

动态库的打包

上面讲了静态库的打包和使用,接下来将动态库的打包和使用,以及动态库链接的原理。

动态库的打包分为以下几个步骤:

  1. 将存放方法的源文件进行编译,编译后(含预处理--编译--汇编)生成以.o为后缀的可重定位二进制目标文件。同时在编译时生成与位置无关码。(gcc/g++ -c -fPIC)
  2. 直接gcc/g++对所有的.o文件进行编译,同时加上-shared选项,打包成动态库即可。(gcc/g++ -shared)
  3. 将动态库与头文件压缩后发送给他人即可供他人使用

以上静态库例子打包成动态库,步骤如下所示:

 紧接着我们可以将动态库与头文件进行压缩,将压缩包给other用户,供他人使用。

 这里有一点需要注意,就是我们一般会把头文件,单独放在一个目录,库文件单独放在一个目录。(上面静态库的例子忘记了,这里说一下。)

动态库的使用

接下来我们已other的身份,进行使用动态库,我们先将压缩包进行解压,然后进行编译,编译时指定头文件、库文件的路径,以及库名。

 虽然可以正常编译,但是此时我们运行可执行程序,就会出现如下报错:

这是因为我们在编译时,仅仅只是告诉了编译器,但是OS并不知晓,OS只会在系统路径,以及当前所在路径下进行查找头/库文件。因此此时会报错。(静态链接并不会,因为生成的可执行程序的运行,不会依赖库),这时常用的解决方法有如下几种:

 1、将库文件拷贝到系统路径

此时我们假如将我们的第三方动态库,拷贝到系统路径/lib64下,即可正常运行。如下:

 需要注意的是,我们一般不会对系统环境进行更改,用完后要进行删除,否则的话,下一次重新登陆,系统路径下还是会存在我们自己的第三方库。

2、将库路径导入环境变量LD_LIBRARY_PATH中

用export指令,将库路径(绝对路径)导入环境变量LD_LIBRARY_PATH中,如下:

代码语言:javascript
复制
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/qidunyan/linux-exercise/test_23_06/test_23_0607/otherPeople/mylib/

此时程序依然可以正常运行

 该方法的好处就是用完后,我们不需要对该环境变量进行删除库的路径,因为下次重新登陆时,该环境变量会被更新。

3、对系统配置文件/etc/ld.so.conf.d进行更改

系统配置文件/etc/ld.so.conf.d中存放的都是以.conf为后缀的文件,该文件内存放的是路径。我们只需要将动态库的绝对路径,放在一个以.conf为后缀的文件中,再将该文件拷贝到系统配置文件内即可。如下:

 但是此时运行的话,依然会显示失败,因为我们没有对配置文件进行更新,我们只需要输入指令:ldconfig,进行更新配置文件即可。

 当然,我们为了不污染系统环境,使用完后也要记得手动删除,否则会一直存在。

动静态库链接的原理

为什么静态链接生成的可执行程序,不会依赖库文件呢?因为在编译阶段会将库中方法的代码加载到可执行程序中,这样就会出现一个情况,假如同一个方法比如printf,被调用了多次,这也就会导致printf实现的代码,被重复复制了多次,出现大量冗余重复的代码,这也就是为什么静态链接生成的可执行程序体积大小非常大的原因。 而动态链接则不是这样,程序在链接动态库时,会通过库的起始地址+偏移量,来找到函数方法所在的位置,而这个偏移量,就是我们生成的与位置无关码。其实就是0 1 2 3 4...这样的偏移量,也就是说,只要知道库的起始地址,就能根据0 1 2 3...这样的偏移量,来找到各自的方法。为什么叫与位置无关码呢?因为库被映射到地址空间的地址是不确定的,但是偏移量是固定的,这样不管库被映射到哪个地址,通过偏移量都可以找到函数所在的位置。(举个例子,假如我对你说,我距离你10米远,那么不管你的位置在哪里,只需要从你的位置+10米,就可以找到我,这个10米,就类似位置无关码) 而在程序运行时,动态库会被加载到物理内存,同时会通过页表映射到进程对应的地址空间中的共享区。此时动态库的地址也就有了。且同一个方法,它的库地址+偏移量相同,所以代码只需要存在一份即可,避免了代码冗余。同时假如存在多个进程同时运行且使用同一个库,那么动态库也只需要在内存中加载一份,然后映射到各自的共享区,通过库地址+偏移量就可以跳转到方法的实现。大大节省了空间的使用。

补充

云服务器默认只存在动态库,因此我们若想使用C/C++静态库,需手动安装

安装C/C++静态库
代码语言:javascript
复制
sudo yum install -y glibc-static
sudo yum install -y libstdc++-static

另外,我们需要知道以下几点:

gcc/g++默认采用动态链接,但是假如只存在静态库,则gcc/g++只会进行静态链接,同样,只存在动态库,也只能进行动态链接(即使我们加上 -static)。

而若动静态库同时存在,则gcc/g++会默认进行动态链接。也可以手动指定进行静态链接(-static)

end.

生活原本沉闷,但跑起来就会有风!🌹

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-10-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 软硬链接
    • 软链接
      •  硬链接
        •  软硬链接的区别
        • 动静态库
          • 什么是库文件?
            • 为什么要存在库?
            • 如何制作和使用第三方库
              • 静态库的打包
                • 静态库的使用
                  • 第一种方式使用静态库:编译时手动指定
                  • 第二种方式使用静态库:将头文件以及库文件安装在系统目录
                • 动态库的打包
                    •  1、将库文件拷贝到系统路径
                    • 2、将库路径导入环境变量LD_LIBRARY_PATH中
                    • 3、对系统配置文件/etc/ld.so.conf.d进行更改
                  • 动静态库链接的原理
                    • 安装C/C++静态库
                • 补充
                相关产品与服务
                云服务器
                云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档