C缺陷与陷阱 — 8 编译与链接

目录

1 程序的编译过程

2 动态链接的优缺点

2.1 动态链接的优点

2.2 动态链接的缺点

2.3 只使用动态链接

3 函数库链接的5个特殊秘密

4 警惕Interpositioning

5 产生链接器报告文件


1 程序的编译过程

程序的编译过程是将源代码转换成计算机可以执行的机器代码的过程。这个过程通常包括以下几个主要步骤:

(1)预处理(Preprocessing预处理是编译过程的起始步骤,主要针对源代码文件中的预处理指令进行相应处理。

  • 宏定义展开(#define):通过#define指令定义宏,预处理时会按照定义规则对代码里相应的宏进行文本替换,比如定义#define PI 3.14,代码中出现PI的地方就会被替换成3.14。
  • 条件编译指令(#ifdef、#ifndef、#endif 等):条件编译指令可依据是否定义了特定宏等条件,来决定代码段是否参与后续编译。
  • 包含头文件(#include:预处理器会把指定头文件的全部内容插入到源代码中相应的位置。例如,#include <stdio.h>会将标准输入输出头文件里的内容(像printf、scanf等函数的声明等)添加进来。

(2)编译(Compilation):将预处理后的源代码转换为中间代码,并进行优化以及最终生成目标代码的过程,涉及多个子环节。

  • 词法分析:编译器会把预处理后的源代码当作字符流,将其拆解成一个个单词(tokens),这些单词涵盖了编程语言里的关键字(如 C 语言中的int、if、for等)、标识符(变量名、函数名等)、常量(整数常量、浮点数常量、字符常量等)、运算符(+、-、*、/等)以及界符(;、{、}等)。例如对于代码int num = 10;,词法分析器能准确分解出 “int”(关键字)、“num”(标识符)、“=”(运算符)、“10”(常量)和 “;”(界符),为后续分析提供基础元素。
  • 语法分析:基于词法分析得到的单词,编译器依据编程语言既定的语法规则构建语法树,以树形结构清晰呈现程序的语法构成以及语句、表达式之间的层次关系。比如针对if语句if (condition) { statement; },语法树会将if作为根节点,其下细分出条件表达式condition节点以及语句块statement节点。一旦代码存在语法错误,像括号缺失或者关键字拼写有误等情况,语法分析阶段就能检测出来。
  • 语义分析:主要检查程序语义的正确性,包含检查变量是否正确定义与使用、类型是否匹配等关键方面。例如在表达式int a = 3.14;中,语义分析环节会察觉到将双精度浮点数赋值给整型变量属于类型不匹配的错误;同时也会处理变量的作用域问题,确保变量仅在其定义的有效范围内被正确访问,例如函数内部定义的局部变量不能在函数外部随意调用。
  • 中间代码生成:部分编译器会生成中间代码,它处于源语言与目标机器语言之间,具备平台无关性优势,常见形式有三地址码等。例如对于表达式a = b + c,可能生成类似t1 = b + c; a = t1的三地址码形式,这种中间表示形式利于后续进一步的优化以及适配不同目标机器的代码生成工作。
  • 代码优化:编译器会对生成的中间代码实施优化操作,旨在提升程序的执行效率,优化手段多种多样。比如常量折叠,像int a = 2 + 3;可直接优化为int a = 5;,减少不必要的运算;死代码消除,即去除那些永远不会被执行的代码,避免占用资源;循环展开,通过将循环体展开一定次数来降低循环控制方面的开销等,通过这些优化策略让程序在执行时间和存储空间利用上更为高效。
  • 目标代码生成:这是编译阶段的收尾环节,会把经过优化的中间代码(若有)或者直接把处理后的代码转换为目标机器的语言,这种目标代码可能是汇编语言形式,也有可能直接就是机器代码(二进制形式),具体取决于编译器的设计以及编译的配置。

(3)汇编(Assembly):汇编器(Assembler)将汇编语言代码转换成机器代码。汇编语言是一种低级语言,它更接近于机器代码,但仍然包含一些助记符,使得程序员更容易理解和编写。

(4)链接(Linking):链接过程旨在将编译生成的多个目标文件(通常以.o(在 Linux 等系统下)或.obj(在 Windows 系统下)为扩展名)与各类库文件以及其他相关目标文件进行合并整合,最终生成一个单一的可执行文件(像.exe(Windows 系统)或.out(Linux 等系统)文件)。

  • 符号解析:目标文件中包含诸多符号,例如函数名、全局变量名等,链接器的关键任务之一就是确定这些符号的具体定义位置。比如一个源文件中调用了另一个源文件里定义的函数int add(int a, int b),在链接时,链接器必须精准找到该函数实现代码所在的目标文件,若无法找到某个符号的定义,就会出现如 “undefined reference to 'add'” 这类链接错误提示。
  • 重定位:目标文件中的代码与数据部分往往包含一些相对地址或者虚拟地址,在链接阶段,需要依照最终可执行文件的整体布局对这些地址进行相应调整。例如,目标文件中某个函数调用指令原本采用相对地址来指向被调用函数,当链接器把多个目标文件组合到一起时,就需要重新计算这些相对地址,以保障函数调用能够准确无误地跳转到被调用函数的实际内存位置,确保程序执行的正确性。
  • 库链接:程序在开发过程中通常会借助一些库来实现额外的功能,像标准 C 库、数学库等,链接器要把这些库文件与目标文件妥善链接起来。库分为静态库和动态库两种类型,静态库是多个目标文件的集合,链接时,链接器会把程序实际使用的静态库中目标文件的代码和数据全部复制到最终可执行文件里。动态库链接时不会把库文件全部代码复制到可执行文件中,而是在程序运行需调用其函数时,由操作系统加载动态库并完成相应函数调用操作。

(5)加载(Loading):加载器(Loader)负责将可执行文件加载到内存中,进而启动程序的执行流程。

2 动态链接的优缺点

2.1 动态链接的优点

动态链接是一种更为现代的方法,能够更加有效地利用磁盘空间,动态链接只需要处理对动态库的引用关系,不需要像静态链接那样将库的全部代码合并到可执行文件中,因此在编译和链接阶段时间也会缩短。尽管单个可执行文件约启动速度稍受影响,但动态链接可以从两个方面提高性能:

(1)动态链接可执行文件比功能相同的静态链接可执行文件的体积小,相应的会节省磁盘空间和虚拟内存。静态链接在生成可执行文件时,会把程序所用到的函数库(比如标准 C 库、数学库等)中相关目标文件的代码和数据全部复制到可执行文件里面,即便它可能只调用了库中的少数几个函数,而动态链接则不同,动态链接的可执行文件中只是包含了对相应动态库中函数和变量等的引用信息,真正的函数库代码并不嵌入到可执行文件里。

(2)当多个可执行文件都动态链接到同一个特定函数库时,在运行期间,操作系统只会把这个函数库加载到内存中一次,形成一个单独的拷贝。例如,在 Linux 系统下,多个应用程序都动态链接了libc.so(C 标准库对应的动态库),那么在内存中只会存在一份libc.so的实例。操作系统内核通过内存映射机制来实现共享,它会将这份已加载到内存中的函数库映射到每个需要使用它的进程的虚拟地址空间中。各个进程虽然感觉像是自己独占了这个函数库,但实际上它们共享的是同一份物理内存中的代码和数据,这样就避免了重复加载相同的函数库内容到内存里。如果可执行文件是静态链接的,每个文件都将拥有一份函数库的拷贝,显然极为浪费。

动态链接使得函数库的版本升级更为容易。新的函数库可以随时发布,只要安装到系统中,旧的程序就能够自动获得新版本函数库的优点而无需重新链接。

2.2 动态链接的缺点

动态链接是一种“just-in-time(JIT)”链接,这意味着程序在运行时必须能够找到它们所需要的函数库。链接器通过把库文件名或路径名植入可执行文件中来做到这一点。这意味着,函数库的路径不能随意移动。如果把程序链接到/user/ib/libthread.so库,那么就不能把该函数库移动到其他的目录,除非在链接器中进行特别说明。否则,当程序调用该函数库的函数时,就会在运行时导致失败,给出这样一条错误信息:

ld.so.1:main:fatal:libthread.so:can't open file:errno 2

当在一台机器上编译完程序后,把它拿到另一台不同的机器上运行时,也可能出现这种

情况。执行程序的机器必须具有所有该程序需要链接的函数库,而且这些函数库必须位于在链接器中所说明的目录。对于标准系统函数库而言,这并不成问题。

2.3 只使用动态链接

动态链接现在是运行System V release4UNIX的计算机所采用的缺省设置。从作用上

看,静态链接现已过时,只能静静躺在一边睡大觉。

使用静态链接的最大危险在于将来版本的操作系统可能与可执行文件所绑定的系统函数库不兼容。如果应用程序静态链接于版本N的操作系统中,当把程序运行于版本N+1的操作系统上时,它可能会立即崩溃,也可能出现一个不明显的错误。我们无法保证早期版本的系统函数库能够在后期版本的系统上正确地运行。事实上,反过来考虑倒还比较保险一点。但是,如果应用程序动态链接到版本N的系统函数库,当它运行于版本N+1的操作系统上时,它就会正确选取N+1版本的系统函数库。

相反,静态链接的应用程序不得不针对每个新版本的操作系统进行重新生成以保证能够运行。而且,有些函数库(扣libaio.so,libdl.so,libsys..so,libsolv.so以及librpcsvc.so等)只能以动态链接的形式使用。如果在应用程序中使用了这些函数库中的任何一个,你的程序就必须使用动态链接。最好的策略就是所有的应用程序都使用动态链接,这就可以避免可能产生的问题。

3 函数库链接的5个特殊秘密

当使用函数库时,需要掌握5个基本的、不明显的约定。绝大多数C语言书籍或手册对此并没有作出清楚的解释。

(1)动态库文件的扩展名是“.so”,而静态库文件的扩展名是“.a

按照约定,所有动态库的文件名的形式是libname.so。这样,线程函数库便被称作libthread.so。静态库的文件名形式是libname.a,共享archive的文件名形式是libname.sa。共享archive只是一种过渡形式,帮助人们从静态库转变到动态库,现在已过时。

(2)通过-lthread选项,告诉编译链接到libthread.so

传给C编译器的命令行参数里并没有提到函数库的完整路径名,甚至没有提到在函数库目录中该文件的完整名字!函数库名字的呈现形式是把 “lib” 部分和文件的扩展名去掉,然后在前面添加一个 “l”。例如,对于名为 libthread.so 的函数库,在命令行中就是通过 -lthread 选项来告知编译器去链接它。

gcc -o thread_demo thread_demo.c -lthread

(3)编译器期望在确定的目录找到库

这里,你可能会疑惑,编译器是怎么知道该往什么目录寻找函数库呢?就像存在一种特殊的规则用于查找头文件一样,编译器也自有办法来寻找函数库。它查看一些特殊的位置,如在/usr/ib中查找函数库,例如线程库位于usr/Iib/libthread.so。

编译器选项-Lpathname告诉链接器一些其他的目录,如果命令中加入了-l选项,链接器就往这些目录查找函数库。系统中存在几个环境变量,LD LIBRARY_PATH和

LD_RUN_PATH,也是用于提供这类信息。出于安全性、性能和创建/运行独立性方面的考虑,使用环境变量的做法现在己经不提倡。一般还是在链接时使用-Lpathname和-Rpathname选项。

(4)观察头文件,确认所使用的函数库

你有可能遇见的另一个关键问题是“我怎么知道必须链接到哪些函数库?如果观察程序中的源代码,就会发现自己调用了一些自己不曾实现的函数。例如,如果程序跟三角有关,可能会调用像sin()和cos()这样的函数,它们可以在math函数库中找到。

一个很好的建议就是可以观察程序所使片的#include指令。在程序中所包舍的每个头文件都可能代表一个必须链接的库,但需要注意头文件的名字通常并不与它所对应的函数库名相似。函数库链接所存在的另一个不一致性就是函数库所包含的某个函数的原型可能与其他头文件中所声明的函数的原型一样。

(5)与提取动态库中的符号相比,静态库中的符号提取的方法限制更严

最后,在动态链接和静态链接的链接语义上还存在一个额外的大区别,它经常会迷惑不够仔细的用户。

假设我们有一个简单的 main.c 文件,里面调用了一些来自标准 C 库的函数(比如 printf 函数,其所在的库在动态链接时会被自动处理),使用 gcc 编译器进行动态链接编译生成可执行文件的命令示例如下:

gcc main.c -o dynamic_executable

这里,-o 选项指定了输出的可执行文件名是 dynamic_executable。在这个过程中,像 printf 这类库符号所在的动态库(例如 libc.so 等)会在运行时被加载到程序的虚拟地址空间,使得程序可以找到并执行相应的函数,并且多个链接在一起的文件都可以访问到这些动态库中的符号。

同样针对上述 main.c 文件进行静态链接,并且假设有一个自定义的静态库 mylib.a,同时假设 main.c 中调用了 mylib.a 里定义的函数以及标准 C 库中的函数(比如 printf),按照正确顺序进行静态链接编译生成可执行文件的命令示例如下:

gcc main.c mylib.a -o static_executable

这里必须保证先写 main.c ,再写 mylib.a ,因为静态链接时符号是从左到右按顺序解析的,如果写成:

gcc mylib.a main.c -o static_executable

可能就会出现问题,因为在处理 mylib.a 这个静态库时,链接器一开始还没遇到 main.c 里对 mylib.a 中符号的未定义引用(也就是还不知道需要从 mylib.a 里找哪些符号),那么就可能不会正确地从 mylib.a 中提取对应的符号,导致后续链接 main.c 时出现符号未定义等错误。

如果在自己的代码之前引入静态库,又会带来另一个问题。因为此时尚未出现未定义的符号,所以它不会从函数库中提取任何符号。接着,当目标文件被链接器处理时,它所有的对函数库的引用都将是未实现的!

例如,像 “cc -lm main.c” 这样进行静态链接(math 库常以静态链接的 archive 形式存在用于提高运行时性能),若程序使用了如 sin 等数学函数,会得到 “Undefined first referenced symbol in file sin main.o ld:fatal:Symbol referencing errors.No output written to a.out” 这样的错误信息。

为从 math 库中提取所需符号,需要写成 “cc main.c -lm” 这种形式,让文件先包含未解析的引用。个人都习惯了通用的命令形式<命令><选项><文件>,所以让链接器采用<命令><文件><选项>这样的约定是很容易引起混淆。

4 警惕Interpositioning

Interpositioning 是一种通过编写与函数同名的函数来取代库函数行为的技术。可以在特定程序中拦截库函数的调用,以便检查参数、返回值或跟踪程序的执行流程,有助于快速定位问题。在某些情况下,可以通过优化同名的用户函数来提高特定操作的执行效率。

使用Interpositioning需要格外小心。很容易发生自己代码中某个符号的定义取代函数库中的相同符号的意外。这意味着不仅自己对该库函数的调用会被自己版本的函数调用所取代,而且所有调用该库函数的系统调用也会被用户函数取代。例如,假设程序中使用了一个库函数 printf 进行输出,而程序员不小心编写了一个同名的 printf 函数。在这种情况下,程序中所有原本应该调用库函数 printf 的地方都会调用用户自定义的 printf 函数,这可能会导致输出结果与预期不符,甚至可能引发程序错误。

当编译器注意到库函数被另外一个定义覆盖时,它通常不会给出错误信息。这是因为 C 语言遵循“程序员所做的都是对的”的设计哲学,编译器认为这是程序员的意图。这种特性使得在使用 Interpositioning 时,错误很难被及时发现。程序员可能在不经意间使用了这种技术,却没有意识到自己已经覆盖了库函数,从而导致程序出现难以察觉的错误。

由于 Interpositioning 具有较高的风险,只有在确实需要进行调试或提高效率时才考虑使用。对于新手来说,应尽量避免使用这种技术,以免伤害自己。

5 产生链接器报告文件

可以在ld程序中使用“-m”选项,让链接器产生一个报告,里面包括了被Interpose

的符号的说明。通常,带“-m”选项的ld会产生一个内存映射或列表,显示在可执行文件中的什么地方放入了哪些符号。它同时显示了同一个符号的多个实例,通过查看报告的内容,用户可以判断是否发生了Interpositioning。假如你有一个待链接的目标文件叫 main.o,想生成包含符号相关信息的报告,命令可能如下:

ld -m main.o -o output_executable

ld程序中的“-D”选项是随SunOS5.3引入的,日的是提供更好的链接-编辑调试。这个选项允许用户显示链接-编辑过程和所包含的输入文件。如果需要监视从archive中提取对象的过程,这个选项尤其有用,同时可用于显示运行时绑定信息。同样针对 main.o 这个目标文件,想要查看链接编辑过程和涉及的输入文件,命令示例如下:

ld -D main.o -o another_executable

ld是一个复杂的程序,还有很多其他选项和约定未在此处说明。对于绝大多数应用来说,这些说明已经足够了。如需知道更多有关它的知识,下面提供了四条途径,按其复杂程度分列如下:

  • 使用1dd命令,列出可执行文件的动态依赖集。这条命令会告诉你动态链接的程序所需要的函数库。
  • ld程序的-Dhelp选项能提供一些信息,有助于查找链接过程中出现的问题。
  • 查看ld程序的在线文档。

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT c++ 测控系统 一套报警规则(上)

本文适用于pc based的测控系统的上位机&#xff0c;定义了一套报警规则。 由5个部分组成&#xff1a;自定义4布尔类、在全局文件定义工位错误结构体和结构体变量&#xff0c;其它地方给此变量的当前值成员赋值&#xff0c;报警线程类、数据库保存类、弹框类。 1.自定义4布尔类…

作业Day4: 链表函数封装 ; 思维导图

目录 作业&#xff1a;实现链表剩下的操作&#xff1a; 任意位置删除 按位置修改 按值查找返回地址 反转 销毁 运行结果 思维导图 作业&#xff1a;实现链表剩下的操作&#xff1a; 1>任意位置删除 2>按位置修改 3>按值查找返回地址 4>反转 5>销毁 任意…

WebSocket入门与结合redis

WebSocket是什么 WebSocket 是一种用于在客户端和服务器之间建立双向通信的协议&#xff0c;它能实现实时、持久的连接。与传统的 HTTP 请求响应模式不同&#xff0c;WebSocket 在建立连接后允许客户端和服务器之间相互发送消息&#xff0c;直到连接关闭。由于 WebSocket 具有…

WSL Ubuntu

文章目录 1. 概述1.1 什么是适用于 Linux 的 Windows 子系统1.2 什么是 WSL 21.3 WSL 2 中的新增功能1.4 比较 WSL 2 和 WSL 1 2. 参考资料3. 修改存储位置4. 网络访问 1. 概述 1.1 什么是适用于 Linux 的 Windows 子系统 适用于 Linux 的 Windows 子系统可让开发人员按原样运…

unity接入coze智能体

官网链接 coze智能体创建、设置 点击创建–选着智能体&#xff0c;随便起一个名字&#xff0c;就可以了 添加令牌 把随便起一个名字&#xff0c;设置时间&#xff0c;把所有选项都勾选上&#xff0c;一定要勾选所有团队空间&#xff0c;否则无法点击确定。 点击确定后&a…

基于51单片机的交通灯设计—夜间、紧急、复位、可调时间、四个数码管显示

基于51单片机的交通灯设计 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;PCB&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1、采用四方向数码管设计&#xff0c;更加符合真实的交通信号灯设计&#xff1b; 2、左侧按键从上到下依次为…

省略内容在句子中间

一、使用二分查找法 每次查找时&#xff0c;将查找范围分成两半&#xff0c;并判断目标值位于哪一半&#xff0c;从而逐步缩小查找范围。 循环查找 计算中间位置 mid Math.floor((low high) / 2)。比较目标值 target 和中间位置的元素 arr[mid]&#xff1a; 如果 target ar…

Python:动态粒子爱心

预览 代码结构概述 这段代码使用了 pygame 库来创建一个动态的图形窗口&#xff0c;绘制一个心形图案&#xff0c;并在其中显示闪烁的文本。代码主要分为以下几个部分&#xff1a; 初始化和设置心形曲线的计算粒子类的定义生成粒子文本设置主循环 1. 初始化和设置 import p…

springboot449教学资源共享平台(论文+源码)_kaic

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统教学资源共享平台信息管理难度大&#xff0c;容错率低&am…

DataOps驱动数据集成创新:Apache DolphinScheduler SeaTunnel on Amazon Web Services

引言 在数字化转型的浪潮中&#xff0c;数据已成为企业最宝贵的资产之一。DataOps作为一种文化、流程和实践的集合&#xff0c;旨在提高数据管道的质量和效率&#xff0c;从而加速数据从源头到消费的过程。白鲸开源科技&#xff0c;作为DataOps领域的领先开源原生公司&#xf…

【大模型】GraphRAG技术原理

核心概念 GraphRAG 的核心在于用大模型构建知识图谱知识图谱聚类社区化RAG RAG就是输入&#xff08;问题知识&#xff09;到大模型 1-大模型自动从海量数据中构建知识图谱&#xff08;提取合并实体关系&#xff09; 2-聚类算法从知识图谱中聚类社区并生成社区摘要 3-输入问题…

揭秘区块链隐私黑科技:零知识证明如何改变未来

文章目录 1. 引言&#xff1a;什么是零知识证明&#xff1f;2. 零知识证明的核心概念与三大属性2.1 完备性&#xff08;Completeness&#xff09;2.2 可靠性&#xff08;Soundness&#xff09;2.3 零知识性&#xff08;Zero-Knowledge&#xff09; 3. 零知识证明的工作原理4. 零…

王佩丰24节Excel学习笔记——第十二讲:match + index

【以 Excel2010 系列学习&#xff0c;用 Office LTSC 专业增强版 2021 实践】 【本章小技巧】 vlookup与match&#xff0c;index 相结合使用match,index 结合&#xff0c;快速取得引用的值扩展功能&#xff0c;使用match/index函数&#xff0c;结合照相机工具获取照片 一、回顾…

探秘C语言:从诞生到广泛应用的编程世界

引言 在编程的广袤天地里&#xff0c;C 语言宛如一颗璀璨的恒星&#xff0c;持久而耀眼地散发着光芒。自诞生以来&#xff0c;它就以独特的魅力和强大的功能&#xff0c;深深扎根于软件开发的各个层面。无论是构建复杂的操作系统&#xff0c;还是操控微小的嵌入式设备&#xff…

【Python】pandas库---数据分析

大学毕业那年&#xff0c;你成了社会底层群众里&#xff0c;受教育程度最高的一批人。 前言 这是我自己学习Python的第四篇博客总结。后期我会继续把Python学习笔记开源至博客上。 上一期笔记有关Python的NumPy数据分析&#xff0c;没看过的同学可以去看看&#xff1a;【Pyt…

常见异构程序设计语言

目录 一、OpenMP 二、MPI 三、CUDA/HIP 四、OpenACC 五、Athread 六、OpenCL 七、oneAPI 20世纪80年代&#xff0c;异构计算技术就已经诞生了。异构就是CPU、DSP、GPU、ASIC、协处理器、FPGA等各种计算单元、使用不同的类型指令集、不同的体系架构的计算单元&#xff0c…

番外篇 Git 的原理与使用

PS&#xff1a;本篇是个长篇&#xff0c;但是阅读完&#xff0c;可以基本了解 Git 在实际开发中的绝大部分常用操作。 前言&#xff1a;什么是Git 我们在日常工作 / 学习时&#xff0c;对于某些文档 / 代码&#xff0c;可能会存在多个版本需要维护&#xff0c;但是随着版本的…

音频开发中常见的知识体系

在 Linux 系统中&#xff0c;/dev/snd 目录包含与声音设备相关的文件。每个文件代表系统中的一部分音频硬件或音频控制接口。以下是你列出的文件及其含义&#xff1a; 一.基本术语 样本长度(sample)&#xff1a;样本是记录音频数据最基本的单位&#xff0c;计算机对每个通道采…

我的工作会被AI替代吗?

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

‌HBase是什么,‌HBase介绍

‌官方网站&#xff1a;Apache HBase – Apache HBase Home HBase是一个分布式的、面向列的NoSQL数据库&#xff0c;主要用于存储和处理海量数据。‌它起源于Google的​​​​​​​BigTable论文&#xff0c;是Apache Hadoop项目的子项目。HBase设计用于高可靠性、高性能和可伸…