逐梦代码深林:Linux编译之舞,链接之诗——自举、动静态库的浪漫旅程

news2024/11/13 11:48:36

在这里插入图片描述

文章目录

  • 问题引入,为什么要进行编译->汇编?
  • 一、详细解释编译器自举
    • 1. 从最初的二进制编程到汇编
    • 2. 第一代汇编编译器的诞生
    • 3. 编译器自举的出现:从汇编到更高级的编译器
    • 4. 自举的延续:从汇编到高级编程语言
    • 5. 为什么要进行编译器自举?
  • 二、快速了解动静态库——以及操作
    • 动态库与静态库的概念:
    • Linux环境下动态库的操作与查看方法:
    • Linux环境下依赖静态库的方法:
  • 三、❤️❤️生动小故事解释动静态库与动静态链接——理论❤️❤️
    • 1. 概念详解
    • 2.🥰🥰 故事引入🥰🥰
  • 优缺点


问题引入,为什么要进行编译->汇编?

编译过程从高级语言到汇编语言,再到二进制代码,这个逐步转换过程是为了实现代码的高效执行,并降低开发成本:

  1. 编译到汇编再到二进制

    • 高级语言:如C、Java、Python等,为了让程序员方便编写和理解,设计了接近人类自然语言的语法结构。但是,高级语言的代码对于计算机硬件来说是无法直接执行的。
    • 汇编语言:是介于高级语言和机器语言(即二进制)之间的低级语言,与具体的计算机架构紧密相关,直接对应CPU的指令集。编译器先将高级语言翻译成汇编语言,有助于生成有效率、紧凑的二进制代码,减少硬件操作的复杂性。
    • 二进制代码(机器码):是最终的执行代码,由0和1组成,能够直接在硬件上运行。将汇编进一步转换为二进制,得到直接操作内存和处理器的指令,最终提升执行效率。
  2. 减少语言开发成本

    • 从高级语言编译到二进制的分步流程可以简化编译器开发过程。编译器可以分阶段生成特定平台适配的代码,使用现有的汇编器和链接器生成二进制文件,从而减少重写不同平台的工作量。
    • 这种分步式编译还利于优化不同层级代码,适应多种硬件架构,节省了开发和测试成本。
  3. 编译器自举(Compiler Bootstrapping)

    • 定义:编译器自举是指编译器使用自身的编译器来编译其源代码,从而实现自我重构和优化。比如一个C编译器用C语言写成,借助现有的低版本或跨平台编译器来生成二进制。
    • 优点:自举方法在不依赖其他语言编译器的情况下,提供了代码优化、可移植性等好处。例如,GCC(GNU编译器)就是自举的一个经典案例,这种设计降低了对外部工具的依赖,有助于稳定和高效地更新和发布新版本。

因此,从高级语言到汇编再到二进制的逐步编译过程,可以帮助实现跨平台兼容,同时为编译器开发和维护节约成本,而自举技术则推动了编译器的持续演进和优化。


一、详细解释编译器自举

编译器自举(Compiler Bootstrapping)是一种编译器的自我构建方法,让编译器能够用自身的编译器来编译。这个过程之所以重要,是因为它大大提高了编译器开发的灵活性和效率,摆脱了早期二进制编程的繁琐局限。以下是编译器自举从最早的二进制设计到现代编程语言的发展过程,以及自举在编译器设计中的作用:

在这里插入图片描述

1. 从最初的二进制编程到汇编

最早期的计算机程序是用二进制编码直接在硬件上运行的,二进制代码完全由0和1构成。程序员要直接使用二进制编码指令,这不仅容易出错,而且非常不便。为了简化编程,发明了汇编语言——一种更接近人类可理解的低级语言,其中的指令符号化,可以转化成硬件指令。汇编语言不仅直观,还提供了一种“符号化”的方式,使程序开发不再局限于繁琐的二进制输入。

2. 第一代汇编编译器的诞生

尽管有了汇编语言,要让计算机理解并执行汇编代码,还需要一个编译器(也称汇编器)来将汇编代码转换成二进制。最早的汇编器实际上是由直接用二进制编程完成的,这样的汇编器成为第一代汇编编译器。第一代汇编器的诞生意味着以后可以用汇编语言来编写代码,并通过汇编器转换成机器可执行的二进制,这大大简化了代码开发过程。

3. 编译器自举的出现:从汇编到更高级的编译器

由于第一代汇编器是用二进制编程完成的,维护和升级十分困难。于是,为了摆脱对二进制编程的依赖,人们开始用汇编语言写更复杂、更高效的第二代汇编器。这个过程就是编译器的自举:通过编写并运行一个能理解自己语言的编译器,实现自我升级。具体步骤如下:

  • **用第一代汇编编译器(基于二进制)**编写并编译一个更强大的汇编编译器。
  • 使用新生成的汇编编译器,再次编译汇编语言的代码,完成自举。

通过这种方式,编译器的开发不再依赖二进制编码,而可以完全使用汇编语言。汇编器自举完成后,开发者只需编写汇编代码即可,不再依赖直接的二进制编程,极大地降低了开发难度。

4. 自举的延续:从汇编到高级编程语言

随着计算机发展,汇编语言逐渐不能满足更高层次的编程需求,高级语言如C、C++被设计出来。与汇编编译器类似,最早的C编译器是用汇编语言编写的,这样也允许我们用高级语言开发程序。此时,C编译器的自举过程如下:

  • 用汇编编写最初的C编译器,将其用于生成C语言的第一代二进制代码。
  • 之后,编写一个能用C编写和编译自身的C编译器,并通过现有的C编译器编译出一个更高效的C编译器版本,从而完成自举。

最终,通过多次自举和优化,C编译器可以完全用C语言编写并编译,从而形成了稳定的C/C++开发环境,彻底摆脱了对汇编的依赖。

5. 为什么要进行编译器自举?

编译器自举的主要原因是:

  • 降低开发复杂度:自举过程避免了低级编程的麻烦,使开发者可以专注于更高层次的语言优化和功能。
  • 提高移植性和扩展性:自举让编译器只需用自身语言实现,而不依赖于底层硬件,可以移植到不同平台。
  • 增强稳定性:自举过程多次验证编译器的稳定性和正确性,确保编译器能更高效地自我迭代和更新。

通过自举,从最早的汇编到高级语言(如C/C++),逐步形成了现代编程环境。


二、快速了解动静态库——以及操作

动态库与静态库的概念:

  • 静态库(Static Library):静态库在编译时被直接嵌入到目标程序的可执行文件中,因此当程序运行时不需要额外的库文件。使用静态库会增加可执行文件的体积,因为库的代码被复制到每个使用它的程序中。

  • 动态库(Dynamic Library):动态库(在Linux下也称为共享库)在程序运行时才加载到内存中,多个程序可以共享同一个动态库。这种库不会增加可执行文件的体积,减少了存储和内存的占用。

Linux和Windows下的动态、静态库文件前后缀

  • Linux

    • 动态库文件前后缀为 libXXX.so(即“shared object”),例如 libm.so
    • 静态库文件后缀为 libXXX.a,例如 libm.a
  • Windows

    • 动态库文件后缀为 .dll(即“dynamic-link library”),例如 math.dll
    • 静态库文件后缀为 .lib,例如 math.lib

Linux环境下动态库的操作与查看方法:

  1. ldd命令:用于显示可执行文件或动态库所依赖的动态库。可以使用如下命令查看某个程序的动态库依赖关系:

    ldd <可执行文件或动态库>
    

    在这里插入图片描述

在命令 ldd mytest 的输出中,我们可以看到 mytest 可执行文件所依赖的动态库。每行代表一个动态库的依赖信息,下面逐一解释:

  1. linux-vdso.so.1 => (0x00007ffc5630c000)
  • linux-vdso.so.1:这是 VDSO(Virtual Dynamic Shared Object),即虚拟动态共享对象。VDSO 是 Linux 内核提供的一个特殊共享对象,允许用户空间程序在不进行系统调用的情况下访问一些特定的内核功能,从而减少上下文切换,提高性能。
  • 0x00007ffc5630c000:这是 linux-vdso.so.1 在内存中的加载地址,表示该对象在程序运行时被加载到了内存的这个位置。
  1. libc.so.6 => /lib64/libc.so.6 (0x00007ff4c597a000) 我们主要看这个
  • libc.so.6:这是 GNU C标准库,简称 libc,提供了 C 语言的标准库函数和一些系统调用的接口,例如输入输出、字符串操作、内存管理等。它是几乎所有 Linux 应用程序都依赖的重要基础库。
  • => /lib64/libc.so.6libc.so.6 实际被加载的路径,在这里是 /lib64/libc.so.6,说明 ldd 找到的 libc.so.6 位于系统的 /lib64 目录下。
  • (0x00007ff4c597a000):这是 libc.so.6 在内存中的加载地址,即在运行 mytest 程序时,libc.so.6 被加载到内存的 0x00007ff4c597a000 地址处。
  1. /lib64/ld-linux-x86-64.so.2 (0x00007ff4c5d48000)
  • /lib64/ld-linux-x86-64.so.2:这是 动态链接器(Dynamic Linker/Loader),也叫做动态加载器,负责在程序启动时加载并链接所需的共享库。它会根据程序和库的依赖关系加载所有必要的共享库,并把它们映射到程序的内存空间中。
  • (0x00007ff4c5d48000):这是动态链接器在内存中的加载地址,表示 ld-linux-x86-64.so.2 被加载到内存的 0x00007ff4c5d48000 地址位置。

  1. file命令:用于查看文件类型,能够判断某个文件是否是动态库或静态库。可以通过以下命令来查看文件类型:

    file <文件>
    

在这里插入图片描述

输出信息中将包含“shared”,说明它是一个动态库。


  1. ll命令:在Linux系统中可以使用 ll 来查看可执行文件所依赖的C标准库。可以先用 ldd 查看依赖关系。

在这里插入图片描述


Linux环境下依赖静态库的方法:

GCC 默认进行动态链接,因为它会使用共享库(如 .so 文件)来生成可执行文件,减少可执行文件的大小并共享内存中的库。要进行静态链接,你需要使用静态库(.a 文件)来构建可执行文件。

如果你没有拷贝静态库,但希望强制 GCC 进行静态链接,可以使用以下方法:

1. 使用 -static 选项进行静态链接
在编译时,你可以通过添加 -static 选项来告诉 GCC 强制进行静态链接,这样即使系统中存在动态库(.so 文件),GCC 会忽略它们,尝试使用静态库(.a 文件)进行链接。

命令如下:

gcc XXX.c -o XXX -static

在这里插入图片描述

这条命令会强制 GCC 使用静态链接创建 myprogram 可执行文件,所有库都会被静态链接到可执行文件中。

2. 创建静态库
有时,如果你没有显式提供静态库,GCC 仍然会默认使用动态库链接。你可以显式指定使用静态版本的标准 C 库(libc.a),具体方法如下:

sudo yum install glibc-static libstdc++-stdtic -y

在这里插入图片描述

glibc-static对应c语言的,libstdc+±stdtic对于c++的。


总结

  • 使用 -static 强制进行静态链接。
  • 如果没有静态库,首先需要安装相应的静态库(例如 libc.a
  • 静态链接会将所有库包含进可执行文件,导致文件体积增大,但可以使得可执行文件独立于外部库。

三、❤️❤️生动小故事解释动静态库与动静态链接——理论❤️❤️

1. 概念详解

在实际开发中,通常会将代码分散在多个源文件中,每个源文件可能依赖其他源文件的函数或数据。但每个源文件是独立编译的,这意味着每个 .c 文件会生成一个对应的目标文件 .o。为了让这些文件之间的依赖关系得以实现,需要通过链接将生成的目标文件整合为一个可执行程序。这个过程可以分为静态链接动态链接

静态链接
静态链接是在编译阶段将所有依赖的库直接嵌入到可执行文件中。这样生成的可执行文件包含了程序运行所需的所有库代码,运行时不需要依赖外部库文件。静态链接的主要缺点包括:

  • 空间浪费:每个可执行文件都独立包含所有依赖的库代码,因此多个程序如果都依赖某个库(例如 printf 函数),那么每个可执行文件中都会有 printf.o 的副本,导致内存中存在多个相同库代码的副本。
  • 更新困难:当库中的代码更新时,所有依赖该库的可执行文件都需要重新编译和链接。

尽管有这些缺点,静态链接的优点在于,生成的可执行文件独立性高,运行速度快,因为程序启动时无需加载外部库。

动态链接
动态链接在运行时将库文件加载到程序中,从而生成完整的可执行程序。与静态链接不同,动态链接在编译时不将库代码嵌入可执行文件,而是在程序运行时通过系统的动态链接器加载所需的库文件。这种方式的优势在于:

  • 节省空间:多个程序可以共享同一个库文件,在内存中只需加载一次,节省系统资源。
  • 便于更新:库更新时,无需重新编译依赖该库的可执行文件。

动态链接已广泛应用。例如,在 Linux 下查看一个 hello 可执行程序的依赖库,可以看到 hello 依赖于 libc.so.6(C 标准库的动态版本):

$ ldd hello
linux-vdso.so.1 =>  (0x00007fffeb1ab000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff776af5000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff776ec3000)

在这里,ldd 命令用于打印程序的共享库依赖列表。


2.🥰🥰 故事引入🥰🥰

我是张三,一个即将进入高中的学生,面临着一个封闭式管理的学校。学校有严格的规定,不允许带电子产品进校园,尤其是手机和电脑。但我有一个秘密爱好——玩游戏。对于我来说,游戏不仅仅是娱乐,它能帮助我放松心情,甚至让我在学习上更有效率。

有一天,一位学长发现了我的困境,他知道我对游戏的热爱,于是悄悄告诉我一个重要的秘密:“如果你想在学校放松一下,学校东门出去直走100米,左转150米,就有一家网吧,叫‘蚂蚁网吧’。”我心里一惊,记下了这条信息,心想,周末一定能派上用场。

经过一周紧张的学习,终于迎来了周日。老师布置了不少作业,我决定先把语文和数学作业写完,吃完午饭后去网吧玩一会儿游戏放松一下,然后再完成化学和物理作业。于是,我按照计划一步步进行。

中午吃完饭后,我迫不及待地依照学长告诉的路线,找到那家网吧。进门后,我打开了一台电脑,开始了我的上网计划。恰巧,我还在网吧遇到了我的舍友和好朋友,他们也在打游戏。我们一起享受了短暂的游戏时光。

玩完游戏后,我回到宿舍,继续按计划写作业。这个过程中,我就像一个可执行程序,而学长就好像是编译器和链接器。学长告诉我网吧的位置,这就像编译器在编译过程中加载了程序所需的外部信息。当我在执行程序时,遇到需要“上网”的步骤,而我自己并没有实现这个功能时,我就像调用了一个库函数,进入网吧,完成了“上网”这个功能。

网吧是一个典型的共享库,在这里,我不仅可以自己上网,其他人也可以使用这个资源。完成上网后,我就回到宿舍,继续执行剩下的任务,这就是动态库和动态链接的过程。

然而,好景不长,网吧被人举报了。突然有一天,网吧的老板关了门,帽子叔叔把它封了。我再也不能去网吧玩游戏了。于是,我把这个问题告诉了父亲,父亲决定解决这个问题,他去找到了网吧的老板,买下了一台我常用的电脑,把它搬到了宿舍里。这样,我就不再需要依赖网吧了,随时可以在宿舍里玩游戏了。

这就像是把“动态库”转换成了“静态库”。从此,我不再需要依赖外部资源,直接在宿舍里就能执行游戏程序,所有的游戏功能都已经在我的计算机里实现。这就是静态链接的过程:库函数被嵌入到程序内部,程序不再需要外部的支持。

在这里插入图片描述


优缺点

总结优缺点:

  • 静态链接的优点是:程序运行时不依赖外部库,所有功能都已经嵌入到程序中,因此程序独立性强,不会受外部环境的影响。

    • 缺点:会浪费大量空间。每个程序都包含了所有需要的库函数,这不仅占用了磁盘空间,还浪费了内存资源。而且,一旦库更新,所有的程序都需要重新编译。
  • 动态链接的优点是:库函数是共享的,不同的程序可以使用同一个库文件,这样节省了磁盘空间、内存以及更新的难度。

    • 缺点:如果动态库丢失或损坏,所有依赖该库的程序都无法运行,程序的可移植性受到影响。

综上,我们就初步完成了对于动静态库的学习,如果对您有帮助的话,求一个一键三连,谢谢大佬!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2237544.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI 写作(六):核心技术与多元应用(6/10)

一、AI 写作的核心技术概述 AI 写作在当今数字化时代正发挥着越来越重要的作用。它不仅极大地提高了写作效率&#xff0c;还为不同领域带来了创新的可能性。 AI 写作的核心技术主要包括基于模板的文本生成和基于深度学习的文本生成。基于模板的文本生成通常依赖预先设定的模板…

米家通过HomeAssistant控制笔记本电脑开关机

米家通过HomeAssistant控制笔记本电脑开关机 配置HomeAssistant配置EMQX mqtt自动化配置电脑关机实现电脑开机实现&#xff08;网络唤醒WOL包&#xff09; 环境准备&#xff1a; HomeAssistant&#xff1a;能配置接入米家的设备&#xff0c;我这里采用fnos安装MQTT服务器&…

QT信号和槽与自定义的信号和槽

QT信号和槽与自定义的信号和槽 1.概述 这篇文章介绍下QT信号和槽的入门知识&#xff0c;通过一个案例介绍如何创建信号和槽&#xff0c;并调用他们。 2.信号和槽使用 下面通过点击按钮关闭窗口的案例介绍如何使用信号和槽。 创建按钮 在widget.cpp文件中创建按钮代码如下 …

环境背景文本到语音转换

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;编程探索专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月9日23点20分 点击开启你的论文编程之旅https://www.aspiringcode.com/content?id100000000027&uida9ecaa6323844415b87…

MySQL初学之旅(1)配置与基础操作

目录 1.前言 2.正文 2.1数据库的发展历程 2.2数据库的基础操作 2.2.1启动服务 2.2.2创建与删除数据库 2.2.3数据类型 2.2.4创建表与删除表 2.3MySQL Workbench基础使用简介 3.小结 1.前言 哈喽大家好吖&#xff0c;今天博主正式开始为大家分享数据库的学习&#xff…

【环境搭建】使用Dockerfile构建容器搭建Kylin特定版本

Kylin的有些版本官方已经下架了&#xff0c;Docker Hub上也没镜像了&#xff0c;所以需要自己搭建以下&#xff0c;为了以后更方便快捷地使用&#xff0c;就编写了一个更轻量级的Dockerfile。 准备工作 本次搭建使用的源码包来自华为云镜像站&#xff0c;里面有Kylin各个版本…

【图】图学习

0 回顾数据结构逻辑 1 图的定义和基本术语 必须有顶点&#xff0c;可以没有边。 Cn2和2*Cn2&#xff08;数学上的&#xff0c;n个顶点取2个顶点&#xff09; 概念有些多。。。。。。 2 图的定义 3 图的存储结构 无向图的邻接矩阵 有向图的邻接矩阵 网&#xff08;有权图&#…

基于RMD算法模型的信号传输统计特性的matlab模拟仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于RMD算法模型的信号传输统计特性的matlab模拟仿真。参考的文献如下&#xff1a; 即通过RMD随机中点位置模型算法&#xff0c;实现上述文献的几个仿真图。 2.…

【React】React 生命周期完全指南

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 React 生命周期完全指南一、生命周期概述二、生命周期的三个阶段2.1 挂载阶段&a…

软件工程 软考

开发大型软件系统适用螺旋模型或者RUP模型 螺旋模型强调了风险分析&#xff0c;特别适用于庞大而复杂的、高风险的管理信息系统的开发。喷泉模型是一种以用户需求为动力&#xff0c;以对象为为驱动的模型&#xff0c;主要用于描述面向对象的软件开发过程。该模型的各个阶段没有…

C++20 概念与约束(2)—— 初识概念与约束

1、概念 C20 中引入新的编译期关键字 concept 用于创建概念。个人认为将其翻译为“构思”更为贴切。直接使用时&#xff0c;它更像一个只能用于模板的布尔类型关键字。 而如果用于模板中&#xff0c;他会将模板类型先带入自身&#xff0c;当自身条件为 true 才会实例化模板&…

Everything软件实现FTP功能

Windows的文件共享和ftp实在难用&#xff0c;这里介绍一种新的局域网内共享文件的方法 下载 Everything 选择想要共享的文件&#xff0c;选择包含到数据库&#xff0c;注意&#xff1a;要在对应的分卷设置&#xff0c;共享文件夹名称不要包含中文字符&#xff0c;因为Windows底…

系统管理与规划师

综合 工业化、信息化两化融合&#xff1a;战略、资源、经济、设备和技术的融合 诺兰6时期&#xff1a;&#xff08;初普控&#xff0c;整数成&#xff09;初始、普及、控制、整合、数据管理、成熟期&#xff1b;技术转型期介于控制和整合间 IT战略规划 IT战略制定&#xff1a;使…

初始MQ(安装使用RabbitMQ,了解交换机)

目录 初识MQ一&#xff1a;同步调用二&#xff1a;异步调用三&#xff1a;技术选型 RabbitMQ一&#xff1a;安装部署二&#xff1a;快速入门三&#xff1a;数据隔离 java客户端一&#xff1a;快速入门二&#xff1a;workqueues三&#xff1a;Fanout交换机四&#xff1a;Direct交…

[C++11] 类中新特性的添加

默认的移动构造和移动赋值 在 C11 之前&#xff0c;编译器会为每个类自动生成默认的构造函数、析构函数、拷贝构造函数、拷贝赋值运算符等函数&#xff0c;以实现对象的创建、销毁和拷贝操作。但拷贝操作会复制整个对象的数据&#xff0c;效率低&#xff0c;尤其是在处理大对象…

emr上使用sparkrunner运行beam数据流水线

参考资料 https://time.geekbang.org/column/intro/167?tabcatalog Apache Beam和其他开源项目不太一样&#xff0c;它并不是一个数据处理平台&#xff0c;本身也无法对数据进行处理。Beam所提供的是一个统一的编程模型思想&#xff0c;而我们可以通过这个统一出来的接口来编…

github高分项目 WGCLOUD - 运维实时管理工具

GitHub - tianshiyeben/wgcloud: Linux运维监控工具&#xff0c;支持系统硬件信息&#xff0c;内存&#xff0c;CPU&#xff0c;温度&#xff0c;磁盘空间及IO&#xff0c;硬盘smart&#xff0c;GPU&#xff0c;防火墙&#xff0c;网络流量速率等监控&#xff0c;服务接口监测&…

MyBatisPlus 用法详解

文章目录 一、快速入门1.1 引入依赖&#xff1a;1.2 定义 Mappper&#xff1a;1.3 使用演示&#xff1a;1.4 常见注解&#xff1a;1.4.1 TableName:1.4.2 TableId&#xff1a;1.4.3 TableField&#xff1a; 1.5 常见配置&#xff1a; 二、核心功能2.1 条件构造器&#xff1a;2.…

Python小游戏23——捕鱼达人

首先&#xff0c;你需要安装Pygame库。如果你还没有安装&#xff0c;可以使用以下命令进行安装&#xff1a; 【bash】 pip install pygame 运行效果展示 接下来是示例代码&#xff1a; 【python】 import pygame import random # 初始化Pygame pygame.init() # 屏幕尺寸 SCREEN…

库打包工具 rollup

库打包工具 rollup 摘要 **概念&#xff1a;**rollup是一个模块化的打包工具 注&#xff1a;实际应用中&#xff0c;rollup更多是一个库打包工具 与Webpack的区别&#xff1a; 文件处理&#xff1a; rollup 更多专注于 JS 代码&#xff0c;并针对 ES Module 进行打包webpa…