Linux | 编译器gcc/g++的使用【动静态库的认识】-2

简介: Linux | 编译器gcc/g++的使用【动静态库的认识】

四、谈谈链接的过程【动静态库的理解】

在上面,我们说到了对于一个程序而言分为四步,预编译 --> 编译 --> 汇编 --> 链接。对于前面三步而言我给出了对应的记忆方法。但是对于最后一步的链接,却不是那么好理解,所以我们专门来谈谈这个链接🔗

1、 库的初步认识

首先要说一些必不可少的前言小知识

  • 问:我们为什么能够在Linux在进行C、C++代码的编写和编译呢?

对于上面这个问题相信你也是非常得困惑,在上面的演示过程中,我也往file.c里写了一些C语言的代码,其实你把后缀改为.cc就可以写一些C++的代码了image.png

  • 这究竟是为何呢?答:因为Linux大部分都是用C写的,少量使用汇编,而C++则是兼容C,所以在Linux系统默认已经携带了语言级别的头文件和语言对应的
  • 可以先来看看系统中的头文件所在目录,然后就可以看到我们熟悉的stdio.hstdlib.h等等

image.png

  • 然后在这些头文件中包含了对应的库函数实现,我们可以进去看看
  • 然后在里面就可以看到我们在C语言中使用的printf()scanf()这些库函数,这下你应该知道为什么我们在代码的时候引入stdio.h这个头文件就可以使用里面的库函数了

image.pngimage.png所以可以得出一个结论:只要有头文件就必有源文件

  • 其实我们在安装VS2019、VS2022的时候。最重要的一个工作是什么呢?其实在这么几十分钟的安装时间里,这个自动化程序更多的是在帮我们下载并安装语言的头文件和库文件【编译需要的两个东西】

📚拓展【ldd】指令

接下去我们来介绍一个指令,因为在后面的讲解中会使用到

格式:

ldd filename —— 检测可执行程序形成的时候都依赖了哪些库

l -> list  【列出】

  • d -> dynamic  【动态的】
  • d -> dependencies【依赖关系】
  • ——> 列出动态依赖关系
  • 然后我们就可以去看看每次gcc最终链接后形成的a.out这个可执行程序都依赖了哪些库。然后可以看到对于我框起来的这个就是标准的C语言库

image.png解释:

  • => 左边的是程序需要连接的共享库
  • => 右边的是由 Linux 的共享库系统找到的对应的共享库在文件系统中的具体位置

知道了这些其实就可以回答上一个小节我所提出的问题了:mag: 【最后的答案是】:==系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是⭐链接⭐的作用==


这样就又衍生出了一个问题:

  • Linux中的这些指令从何而来,为什么使用这些指令可以完成对应的操作?

接下去我列举几个常见的指令

image.pngimage.pngimage.png

  • 通过列出上面三个指令所依赖的动态库,相信你应该知道为什么这些指令完成它们对应的操作,也可以回答出我提的这个问题

【最后的答案是】:==程序、工具、指令是一回事【都是用C语言写的,都是经过编译形成的,都和C语言标准库有关】==

2、 动态库与静态库

看完了上面这些,对Linux中的库有了一个基本概念后,我们便可以来谈谈【动态库】与【静态库】,可能现今我的水平有限,讲不了很深入,后续弥补~

  • 因为对于动静态库而言Windows也是有的,所以我会做一个对比
  1. 动态库
  • Linux下:以lib作为前缀,.so作为后缀【libXXXXXX.so】image.png
  • Windows下:以.dll作为后缀image.png
  1. 静态库
  • Linux下:以lib作为前缀,.a作为后缀【libXXXXXX.so】image.png上面的这些是在/lib64下找到的
  • Windows下:以.lib作为后缀image.png

但是光这么看动静态库就非常晦涩难懂了,不用急,在下一模块,我将用更加形象的方式来介绍它们

3、动静态库的感性理解【小蚂蚁网吧🐜】

本故事均为虚构,如有雷同纯虚巧合U•ェ•*U

故事背景:你是一个初三的学生,在学校被老师管,在家里又被父母管,于是你就想着在上了高中之后要住校,然后就可以无拘无束地玩了,你呢,很喜欢玩游戏,于是就找到你心仪哪所高中的学长询问附近有没有对应的网吧,提前做好打听; 一年后你也考上了自己心仪的高中,终于可以挣脱父母的管教了

  • 在高中的学校待了两个多礼拜之后呢,来到了周末,老师也布置了很多的作业,所以你本来想要去。你呢前一天晚上躺在床上就想自己原本要出去网吧玩的计划可能很难实现,因为作业比较多,于是呢就给自己列了一个清单,明天要完成哪些学习任务
  • 然后到了第二天早上八点,你起来了,要先做一小时数学作业,然后写会作文,叫做《我的父亲👨》,因为文笔不是很好,所以可能需要写两个小时,那这个时候来到了中午,要吃饭了,你呢可以点外卖可以不用去食堂,可以边吃外卖🍚边看看教学视频📺,然后在吃完饭后就练习练习配化学方程式
  • 然后这个时候时间来到了下午两点,室友都在午休,然后你呢就想着也写了一上午+中午的作业了,应该要休息一下,做到“==劳逸结合==”,于是呢就顺序着自己的计划找到了学校的北门,恰好保安也没有在岗,你就溜了出去,此时呢就看到你学长和你说的 #小蚂蚁网吧🐜#,于是就进去找到网管,网管给你分配了一个座位,3块钱一个小时,你买了两个小时,然后就开始自己疯狂的游戏💻image.png
  • 然后两个小时到了之后,你只能忍痛割爱离开你的游戏o(╥﹏╥)o

:dart:我们可以将这个网吧当做的是系统中的库,然后把你所列的这个清单当做是一段程序。那对于做数学作业、写文章这些就相当于是你在写一些自己的算法或者是简单的循环、条件判断语句,不需要调用库中的内容。但是当程序执行到了【上网】阶段后,就无法再自己执行下去了,只能去调用库函数 :dart:为什么呢?就相当于是 我们在写代码的时候可能有一段逻辑写不下了,因为这段逻辑很复杂。但是要是实现这个逻辑刚好有这个库函数,所以你就去会调用这个库函数。这个库函数就相当于是网吧:dart:但是你怎么知道这个库函数在哪里的呢?——>你学长告诉你的呗。也就相当于你知道了库函数中这个函数的地址在哪里,你只需要顺着北门然后根据这个地址找过去就可以了

image.png

:dart:那这么来说你的学长就相当于是一个链接器,网吧就是一个动态库,你知道了动态库中所需要的这个函数的地址,然后你就顺藤摸瓜🍈地找到了这个地方,你的学长就做了一个链接的功能,还 :dart:记得我在一开始放出的那张图吗,你就可以把自己想做是目标文件。那个可执行程序也就是最后你可以打游戏的那台机子。你在打游戏的这个过程其实就相当于是在调用库函数的一个过程了。最后你回到学校那也就是调用函数结束,然后要继续往下执行你的程序

image.png但是事情完了嘛,当然没有,才刚刚开始。。。

  • 你们学校呢也有很多同学喜欢玩游戏,也通过他们各自的学长知道了这个【小蚂蚁网吧】,于是周末也来玩,但是呢这个网吧似乎不太感谢,突然警察👮‍造访,说他们涉嫌非法经营🈲,然后这个网吧就被封了,你和你的同校同学都玩不了,知道默默回学校image.png

:dart:上面有说到,这个是网吧就是动态库,我们在上面有说到过这个很多指令都在使用这个C语言的标准动态库 :dart:这个警局你可以认为是恶意入侵的黑客,然后将你系统的库搞坏了,那网吧中的点电脑就可以算作是指令,若是库没了,那你的指令就无法执行了,所以我们不要去随便动系统中的已经写好的库,否则你的Linux就无法正常使用了

👉因此我们其实可以初步看出来这个动态库其实是不安全的,因为是共享的,所以大家都可以使用,但是只要它被破坏了,那大家都没得玩了

故事还在继续。。。


  • 一个学期结束了,你放假回到家里,这次的期末考试你考到了班级第一,虽然你也有在玩游戏,但是你也有自己的学习规划。真正地做到了#劳逸结合#,而且你的作文《我的父亲》获得了校园征文大赛一等奖,这让你的父亲非常得开心,于是就说:“儿子,干的不错。说吧,想要什么都可以满足你”
  • 那这个时候你就觉得学校外面的网吧真的不是不太安全,如果我自己有一台电脑就好了:heart_eyes:
  • 于是这个时候你就和你爸爸说想买一台电脑——>为了可以更好地学习。那这个时候你爸就觉得也很有道理,于是就答应你了image.png
  • 在新学期开学后一个礼拜,此时你正在宿舍学习,然后你爸突然就到进到宿舍然后搬进来一台电脑,接着你仔细看了看这台电脑好熟悉的感觉,Σ(っ °Д °;)っ这不是网吧的电脑💻,于是你爸就说:“儿子,怎么样,这台电脑喜欢吗,我不知道你想买怎样的电脑,于是在学校附近逛的时候就发现了一家【大象网吧】,问了里面的老板电脑的配置,然后直接从他那里买了一台,这种你们年轻人的电脑你应该用的惯吧”
  • 于是你就说:“哦哦,谢谢爸爸,可以的。大象网吧(ˉ▽ˉ;)...。。。”
  • 于是到了下个周末之后你同学约你一起去网吧,你说:“我都有电脑了,不需要去了”。然后你的同学回来之后就说:“那个大象网吧好像被封了,说是黑网吧”。此时你就笑笑不说话:blush:心想:封了还更好,这样就不会影响大家学习了。。这样的网吧真的是太没有原则了,毫无底线呀,不过里面的电脑💻是没错的,不要可惜了呀。

:dart:可以看到,其实对于从网吧中拿来的电脑,就是库中的那个函数,也就是将库中的整个函数直接放到你的程序中来,这样你完全不需要再到库中去调用函数了,直接在你的程序中使用即可 :dart:当你的同学去到黑网吧,然后得知被封了之后无奈返回后,这个时候你有这自己的电脑,完全不需要再去考虑是否有电脑可用的安全性了,但就是这个电脑放在宿舍可能有些占位子,原本比较宽阔的空间就要被一台电脑给霸占了,那确实是没办法

讲完了上面这些,相信你对【动态库】和【静态库】一定有了一个自己的理解,但是可能还有有点模糊,在下一小节我将对他们做一个进一步的分析:mag:

4、动态链接与静态链接的区分

接下去我们对【动态库】和【静态库】做一个分析梳理,更好地理解动静态库的原理

✔【动态链接】 —— 仅仅是把库中的你所用的方法的==地址拷贝==到程序里 网吧

  • 动态库优点:因为可以做到被大家共享方法,所以真正的实现永远都是在库中,程序内部只有地址,比较节省空间
  • 动态库缺点:我们的程序还是依赖任何库,一旦动态库缺失,我们的程序便无法运行

:memo:可以联想到,对于小蚂蚁网吧🐜来说,大家都可以去玩,只需要知道网吧的地址在哪里,然后直接去即可,也就和函数一样直接进行一个调用,对自己的程序而言不需要消耗任何的空间 :memo:但是呢一旦这个网吧被封了,也就是动态库丢失了,那此时我们再去调用库中的函数就会失败,这是动态库比较致命的一点:scream:

✔【静态链接】 —— 自己代码当中用到的库中的方法==直接拷贝==到程序里 台式电脑

  • 静态库优点:我们的程序不依赖任何库,自己就可以独立运行
  • 静态库缺点:因为自身的拷贝问题比较浪费空间

:memo:对于静态库来说,确实是比较方便,不需要再去库里面调函数了,直接把这个函数整体拷贝到我们的程序中即可。也就是我们不需要依赖任何的库,也不用去大象网吧🐘 :memo:但是呢这也意味着我们的程序会变得非常庞大,举个最简单的例子就是你完全不写任何自己的函数,所有功能和实现都放在main函数里,这就会显得非常浪费空间

5、做做实验【动静态链接的使用】

  • Linux命令汇总中有说到过file指令可以用来查看一个文件的类型,那我们就可以来看看这个动态的可执行文件

image.png

  • 最重要的就是这个dynamically linked动态的链接,这也就意味着这个可执行文件是经过动态链接生成的(gcc编译完默认是动态链接)
  • 那我们要如何使一个文件进行静态链接呢,很简单,只需要在使用gcc编译的时候在后面加上一个-static的选项即可

image.png

  • 可以看到,当我们使用静态链接时,再使用ldd去列出动态的依赖库,就会报错说not a dynamic exexutable不是动态可执行文件,所以就进一步可以看到ldd只能列出动态依赖库,对于静态库是没有办法列出来的
  • 但是我们可以使用file指令去查看这个文件的文件类型,于是就可以看到【statically linked】静态链接就可以进一步验证这是一个使用静态链接成的可执行文件

不过你在使用静态链接的时候可能会报错,因为对于我们的云服务器,默认就只装了【动态库】,而静态库是没有的,所以要安装一下静态库

  • C静态库安装: (sudo) yum -y glibc-static
  • C++静态库安装: (sudo) yum -y install libstdc++-static

安装之后应该就可以了。然后我们再来看一个地方image.png

  • 可以看到对于动态链接和静态链接所产生的可执行文件大小不太一样,差了可是80倍,这是一个惊人的数字w(゚Д゚)w
  • 所以这就是为什么云服务器默认就是以动态链接记性编译了,因为静态链接虽然是无需调用库函数,但是呢形成的程序体积会非常庞大;而且其中有一部分库的代码,然后系统中本身又带有库,这就==造成了磁盘空间的浪费==

看完了上面这些,相信你对动静态库有了一个初步的了解,不过我没有讲得很深入,如果后续有更好的内容会继续补充

五、总结与提炼

最后,我们来总结一下本文所学习的内容

  • 在一开始,我们了解了一个程序是如何诞生的,然后初步接触到了Linux中的编译器gcc
  • 然后我们使用gcc针对程序的翻译环境进行了逐步展开分析,看到了一个.c的源文件究竟是怎样一步步变成一个.exe可执行程序的,其中要经过【预编译】【编译】【汇编】【链接】四个步骤
  • 最后我们对最后一个链接做了详细的介绍,谈到了Linux中库的概念,然后了解到了【动静态库】,我用了一个小故事让你更好地感受什么是动静态库,相信你在看了这个故事后一定也会有所收获。了解了什么是动静态库后就逐步上手使用他们进行动态链接与静态链接,进一步感受到了linux的纯粹😄

以上就是本文所要介绍的所有内容,感谢您的观看

相关文章
|
4月前
|
安全 Linux vr&ar
Linux的动态库和静态库
Linux的动态库和静态库
|
2月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
106 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
2月前
|
Linux 编译器 C语言
【Linux快速入门(一)】Linux与ROS学习之编译基础(gcc编译)
【Linux快速入门(一)】Linux与ROS学习之编译基础(gcc编译)
|
3月前
|
Linux 编译器 C语言
Linux内核对GCC版本的检测
Linux内核对GCC版本的检测
|
4月前
|
Linux API
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
|
4月前
|
Linux 网络安全 API
【Azure 应用服务】App Service For Linux 环境中,如何从App Service中获取GitHub私有库(Private Repos)的Deploy Key(RSA key)呢?
【Azure 应用服务】App Service For Linux 环境中,如何从App Service中获取GitHub私有库(Private Repos)的Deploy Key(RSA key)呢?
|
4月前
|
小程序 Linux 开发者
Linux之缓冲区与C库IO函数简单模拟
通过上述编程实例,可以对Linux系统中缓冲区和C库IO函数如何提高文件读写效率有了一个基本的了解。开发者需要根据应用程序的具体需求来选择合适的IO策略。
35 0
|
7月前
|
Linux 编译器 开发工具
Linux学习第二枪(yum,vim,g++/gcc,makefile的使用)
Linux学习第二枪(yum,vim,g++/gcc,makefile的使用)
|
IDE Linux 编译器
【Linux】gcc/g++编译器、make/Makefile自动化构建工具
目录 1.gcc/c++的概念: 2.程序编译过程详解: 2.1程序编译过程:
98 0
|
7月前
|
小程序 Linux 开发工具
【Linux】Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解
【Linux】Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解