Linux内存管理--系列文章肆
一、引子
上篇文章介绍了目标文件,也就是讲到编译过程中的汇编这个阶段。本篇要讲目标文件怎么变成一个可执行文件的,介绍编译过程中的链接。
链接主要分为两种,静态链接和动态链接。它们本质上的区别,是在程序的编译和运行过程中使用库的方式不同。
特性 | 静态链接 | 动态链接 |
---|---|---|
文件大小 | 较大 | 较小 |
运行独立性 | 高(无需外部库) | 低(需要外部库) |
启动速度 | 较快 | 稍慢 |
内存使用 | 不共享库,可能使用更多内存 | 库共享,节省内存 |
更新和维护 | 更新库需要重新编译和分发整个程序 | 更新库只需替换库文件 |
兼容性 | 无库版本问题,较高 | 可能存在库版本冲突问题 |
二、静态链接要做什么
在编译时将所有必要的库和模块直接嵌入到生成的可执行文件中,这是静态链接的定义。这意味着可执行文件包含了所有运行所需的代码,因此无需依赖外部库文件。
下面将用两个Demo来示范,1.c和2.c
这是1.c
extern int a;
extern void func1(void);int main()
{int b = 0;func1();return 0;
}
这是2.c
int a = 0;void func1(void)
{int c = 0;c++;
}
objdump -h 1.o1.o: 文件格式 elf64-x86-64节:
Idx Name Size VMA LMA File off Algn0 .text 0000001f 0000000000000000 0000000000000000 00000040 2**0CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data 00000000 0000000000000000 0000000000000000 0000005f 2**0CONTENTS, ALLOC, LOAD, DATA2 .bss 00000000 0000000000000000 0000000000000000 0000005f 2**0ALLOC3 .comment 0000002c 0000000000000000 0000000000000000 0000005f 2**0CONTENTS, READONLY4 .note.GNU-stack 00000000 0000000000000000 0000000000000000 0000008b 2**0CONTENTS, READONLY5 .note.gnu.property 00000020 0000000000000000 0000000000000000 00000090 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA6 .eh_frame 00000038 0000000000000000 0000000000000000 000000b0 2**3CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATAobjdump -h 2.o2.o: 文件格式 elf64-x86-64节:
Idx Name Size VMA LMA File off Algn0 .text 00000016 0000000000000000 0000000000000000 00000040 2**0CONTENTS, ALLOC, LOAD, READONLY, CODE1 .data 00000000 0000000000000000 0000000000000000 00000056 2**0CONTENTS, ALLOC, LOAD, DATA2 .bss 00000004 0000000000000000 0000000000000000 00000058 2**2ALLOC3 .comment 0000002c 0000000000000000 0000000000000000 00000058 2**0CONTENTS, READONLY4 .note.GNU-stack 00000000 0000000000000000 0000000000000000 00000084 2**0CONTENTS, READONLY5 .note.gnu.property 00000020 0000000000000000 0000000000000000 00000088 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA6 .eh_frame 00000038 0000000000000000 0000000000000000 000000a8 2**3CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
ld 1.o 2.o -e main -o a.out
objdump -h a.out a.out: 文件格式 elf64-x86-64节:
Idx Name Size VMA LMA File off Algn0 .note.gnu.property 00000020 00000000004001c8 00000000004001c8 000001c8 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA1 .text 00000035 0000000000401000 0000000000401000 00001000 2**0CONTENTS, ALLOC, LOAD, READONLY, CODE2 .eh_frame 00000058 0000000000402000 0000000000402000 00002000 2**3CONTENTS, ALLOC, LOAD, READONLY, DATA3 .bss 00000008 0000000000404000 0000000000404000 00003000 2**2ALLOC4 .comment 0000002b 0000000000000000 0000000000000000 00002058 2**0CONTENTS, READONLY
2.1 地址和空间的分配
我们将上面两个文件变成目标文件后,每个目标文件都是有自己的代码段,数据段,bss段等。但可执行文件是一个文件,那也就是说链接时要找到各文件中的各符号地址和各段地址,并将它们合并起来,并且分配运行时候的空间。
链接器会为可执行文件或共享库分配内存空间,还会为每个符号分配一个唯一的内存地址。
2.1.1 各段依次堆叠(Sequential Stacking)
最简单的一个方案,是将输入的目标文件按照次序依次叠加起来,生成输出的可执行文件或共享库。
步骤:
扫描目标文件: 链接器首先会扫描所有输入的目标文件,提取每个目标文件的信息,包括代码段、数据段、BSS 段以及符号表等。
地址分配: 链接器会为可执行文件或共享库分配内存空间,并为每个符号分配一个唯一的内存地址。
生成输出文件: 链接器会将所有输入的目标文件的内容依次复制到输出文件中,并根据符号表修正符号地址。
特点:
实现简单: 按序叠加的实现代码比较简单,易于理解和开发。
效率低下: 按序叠加会导致输出文件体积较大,并且可能会产生大量的内存碎片,降低程序的运行效率。对于一些硬件来讲,段的装载和空间对齐单位是页,也就是4KB大小。即便只有1B大小也会分配出4KB的内存。
不适用于大型项目: 对于大型项目,按序叠加会导致链接过程非常耗时,并且可能无法有效利用内存空间。
2.1.2 相似段合并(Merge Similar Segments)
各段依次堆叠会产生各种问题。所以在链接目标文件的过程中,可能会执行相似段合并的优化步骤,以减少可执行文件的大小并提高执行效率。相似段合并的步骤通常在优化和合并阶段进行,其主要目标是识别并合并具有相似代码或数据的段,以消除重复并节省空间。
步骤:
识别相似段:链接器分析目标文件中的代码段和数据段,寻找具有相似内容的段。相似内容可能是相同的代码片段、常量数据或其他类型的数据。
合并相似段:对于找到的相似段,链接器将其合并成一个单一的段。合并过程可能涉及将重复的代码片段或数据项替换为单个引用,并确保所有引用指向合并后的段。
更新引用:
在合并后的段被创建后,链接器需要更新目标文件中的所有引用,以便指向合并后的段。这可能涉及修改目标文件中的地址引用或符号表条目。
删除冗余段:完成相似段的合并后,链接器可能会删除原始目标文件中的冗余段,以释放空间并简化可执行文件的结构。
2.1.3 示例分析
a.out的.text段的size就是目标文件的.text段段的size总和。a.out的VMA和LMA是有数值的,但目标文件并没有的。VMA是虚拟地址,是在链接后根据各段的大小被分配的。LMA是加载地址,在确定每个段的大小、对齐要求以及其他系统布局策略并并保证各段在内存中不会相互冲突的情况下,链接器为每个段分配一个加载地址。
有些资料会讲Linux中的.text段默认会分配到0x08048094,.data段默认会分配到0x08049108,说是ELF文件默认从该地址分配。但目前新一点版本的静态链接的可执行文件的默认加载地址也不是固定的,它的加载地址同样是由操作系统的动态链接器 ld.so(或者其替代品)在可执行文件被加载时动态确定的。如果强制指定可执行文件的加载地址,可以使用一些链接选项来指定加载地址,例如-Wl,-Ttext-segment=地址 。该技术是地址空间布局随机化(ASLR,Address Space Layout Randomization),主要目的是在每次加载可执行文件时,随机选择加载地址,从而增加攻击者利用程序漏洞的难度。
File off是各个段的起始偏移量。这个偏移量表示了每个段在文件中的位置,即相对于文件的起始位置的偏移量。在链接时,File off的作用主要是用来确定每个段在最终生成的可执行文件中的位置,并且在重定位阶段被使用。在生成最终的可执行文件时,File off 信息通常会被包含在可执行文件的头部或段表中,以便操作系统在加载可执行文件时正确地映射每个段到内存中的位置。
2.2 符号解析和重定位
符号解析和重定位对于程序的正确运行至关重要。如果没有符号解析,程序将无法找到符号定义的变量、函数等对象。如果没有重定位,程序将无法在不同的内存地址上运行。
一般出现在该步骤的错误:
符号未定义: 程序中使用了未定义的符号。
符号重复定义: 程序中有多个符号定义使用了相同的名称。
符号地址冲突: 两个或多个符号被分配了相同的地址。
2.2.1 符号解析(Symbol Resolution)
通过符号解析,链接器能够确保所有的符号引用都能正确指向其在其他目标文件或库中的定义,从而使得最终生成的可执行文件能够正确地执行。
链接器首先解析所有目标文件中的符号(函数和变量)。每个目标文件包含的符号可以分为以下两类:
定义的符号:在目标文件中定义的符号(例如函数实现或变量定义)。
引用的符号:目标文件中使用但未定义的符号(例如外部函数或变量)。
链接器需要确保所有引用的符号都能在某个目标文件或库中找到定义。
主要过程:
定义和引用:在程序中,符号可以是函数、变量或其他命名实体。在目标文件中,这些符号可以被定义(有实际的实现)或引用(被使用但未定义)。
扫描目标文件:链接器首先会扫描所有的目标文件和库文件,从中提取出所有的符号。这个过程包括解析目标文件的符号表、重定位表等信息。
符号解析:对于每个引用的符号,链接器需要在其他目标文件或库中找到对应的定义。这可能涉及搜索多个目标文件和库文件,直到找到符号的定义。如果找不到符号的定义,链接器将会报错并提示未定义的符号。
符号表管理:链接器维护一个符号表,记录了每个符号的定义和引用情况。对于每个引用的符号,链接器会在符号表中查找其定义,以确定其在可执行文件中的实际位置。
生成符号表:在符号解析过程完成后,链接器会生成一个最终的符号表,其中包含了所有解析后的符号信息。这个符号表将被用于后续的符号重定位等操作。
2.2.2 符号重定位(Symbol Relocation)
目的是将程序中使用的符号引用(例如函数地址、变量地址等)修正为实际的内存地址。符号重定位是确保程序在不同环境下能够正确运行的关键步骤之一。
在目标文件中,符号的地址通常是相对的或未确定的。链接器需要将这些符号地址调整到它们在最终可执行文件中的实际内存地址。这一步包括以下过程:
地址计算:在符号解析完成后,链接器需要计算每个符号在最终可执行文件中的内存地址。这个地址通常是相对于程序的起始地址的偏移量。
地址调整:链接器将目标文件中的所有引用符号的地址调整为其在最终可执行文件中的实际内存地址。这包括修改目标文件中的指令、数据、符号表等,以便在程序执行时正确地指向符号的位置。
重定位表:重定位表记录了需要进行重定位的符号及其偏移量。链接器根据重定位表中的信息,将引用的符号地址进行调整,以确保程序能够正确执行。
代码和数据重定位:对于代码段和数据段中的引用符号,链接器会修改指令或数据中的地址,使其指向正确的内存位置。这可能涉及修改指令中的跳转地址、函数调用地址,或者修改数据中的符号地址引用。
符号表更新:在完成符号重定位后,链接器可能会更新符号表中的符号信息,以反映符号在最终可执行文件中的实际位置。
2.2.3 示例分析
objdump -d a.out
a.out: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000401000 <main>:401000: f3 0f 1e fa endbr64 401004: 55 push %rbp401005: 48 89 e5 mov %rsp,%rbp401008: 48 83 ec 10 sub $0x10,%rsp40100c: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)401013: e8 07 00 00 00 callq 40101f <func1>401018: b8 00 00 00 00 mov $0x0,%eax40101d: c9 leaveq 40101e: c3 retq 000000000040101f <func1>:40101f: f3 0f 1e fa endbr64 401023: 55 push %rbp401024: 48 89 e5 mov %rsp,%rbp401027: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)40102e: 83 45 fc 01 addl $0x1,-0x4(%rbp)401032: 90 nop401033: 5d pop %rbp401034: c3 retq
例子1.o中.text段的大小是1F,2.o中的.text段的大小是16。并且我们将程序的入口函数定位了main函数。所以a.out中.text段的起始位置也就是main函数的位置。那func1函数的位置就是main + 1F了。查看a.out的汇编代码,发现callq 40101f ,这就是调用了func1函数。这样可执行文件就可以正常运行了。
三、静态链接过程注意点
3.1 强符号和弱符号不一致
在链接器的世界中,符号可以被分为两种类型:强符号(Strong Symbol)和 弱符号(Weak Symbol)。这些术语通常用于描述符号的可见性和优先级。
强符号(Strong Symbol):强符号是具有全局可见性和固定地址的符号。当多个目标文件或库文件中存在相同名称的强符号时,链接器会选择其中的一个作为最终的定义。通常情况下,全局变量和函数都是强符号。强符号的定义具有最高的优先级,将覆盖所有弱符号和普通符号。
弱符号(Weak Symbol):弱符号是具有全局可见性但没有固定地址的符号。当多个目标文件或库文件中存在相同名称的弱符号时,链接器会选择其中的一个作为最终的定义。弱符号的定义具有较低的优先级,当没有找到强符号时,链接器才会考虑使用弱符号。通常情况下,全局变量的声明和函数的声明会被视为弱符号,因为它们没有固定的地址。
在程序中强弱符号定义不一致,主要分三种:
两个及以上强符号类型不一致:多个强符号定义,会在链接时报重定义,因为链接器无法判断该符号的类型。
类型不一致的一个强符号和多个弱符号:在程序分配内存时,会按强符号的类型分配内存,同时报告警:size of symbole 符号 change from 。在连接的时候,要是弱符号的类型大于强符号,将报alignment 1 of symbol 符号 in 目标文件 is smaller than 8 in 目标文件。
两个及以上弱符号类型不一致:这个时候,编译器不会给出要符号内存,只能到链接过程中看有没有强符号,在分配内存。这种情况下不会报告警和错误,但运行时会有未定义符号或者直接段错误。
综上,我们应该可以清楚了,为什么目标文件的bss段在磁盘中的大小是0。因为编译器无法判断其他编译单元中是否有比本单元该符号所占的内存更大的符号,所以给不出内存空间。
3.2 静态库链接
将静态库(Static Library)嵌入到可执行文件(Executable File)中的过程。静态库是一种预编译的代码库,包含函数、变量和其他代码对象,但并非直接可执行。在静态库链接过程中,链接器会将静态库中的所有代码和数据复制到可执行文件中,使其成为可执行文件的一部分。
优点:
可执行文件独立性: 静态链接的可执行文件无需依赖外部库,即使在没有库的情况下也能运行。
提高安全性: 静态链接的可执行文件包含所有代码,因此更不容易受到外部库漏洞的影响。
减小可执行文件大小: 在某些情况下,静态链接可使可执行文件更小,因为无需包含额外的库文件。
缺点:
增加可执行文件大小: 在大多数情况下,静态链接会使可执行文件更大,因为包含了所有静态库代码。
浪费资源: 如果多个可执行文件使用相同的静态库,则每个可执行文件都包含该库的副本,这会导致资源浪费。
更新困难: 当静态库更新时,需要重新编译所有依赖它的可执行文件。
本篇大致讲述了静态链接的情况,下一篇文章将会对动态链接的情况进行阐述。
相关文章:

Linux内存管理--系列文章肆
一、引子 上篇文章介绍了目标文件,也就是讲到编译过程中的汇编这个阶段。本篇要讲目标文件怎么变成一个可执行文件的,介绍编译过程中的链接。 链接主要分为两种,静态链接和动态链接。它们本质上的区别,是在程序的编译和运行过程中…...

kali下载zsteg和stegpy
1.kali下载zsteg 从 GitHub 上克隆zsteg到kali git clone https://github.com/zed-0xff/zsteg 切换目录 cd zsteg 用于安装名为 zsteg 的 Ruby Gem 包 gem install zsteg 2.kali下载stegpy 下载网站内的stegpy-master压缩包GitCode - 开发者的代码家园 并拉到kali中 切换到s…...

前端面试题日常练-day34 【面试题】
题目 希望这些选择题能够帮助您进行前端面试的准备,答案在文末。 1. jQuery中,以下哪个选项用于筛选出第一个匹配的元素? a) first() b) get(0) c) eq(0) d) find(":first") 2. 在jQuery中,以下哪个选项用于在元素上…...

网站笔记:huggingface model memory calculator
Model Memory Utility - a Hugging Face Space by hf-accelerate 这个工具可以计算在 Hugging Face Hub上托管的大型模型训练和执行推理时所需的vRAM内存量。模型所需的最低推荐vRAM内存量表示为“最大层”的大小,模型的训练大约是其大小的4倍(针对Adam…...

SpringBoot2.0.x旧版集成Swagger UI报错Unable to infer base url...解决办法
一、问题描述 1.1项目背景 SpringBoot2.0.9的旧版项目维护开发,集成Swagger-ui2.9.2无法访问的问题。不用想啊,这种老项目是各种过滤器拦截器的配置,访问不到,肯定是它们在作妖。懂得都懂啊,这里交给大家一个排错的办…...

软件项目详细设计说明书实际项目参考(word原件下载及全套软件资料包)
系统详细设计说明书案例(直接套用) 1.系统总体设计 2.性能设计 3.系统功能模块详细设计 4.数据库设计 5.接口设计 6.系统出错处理设计 7.系统处理规定 软件开发全文档下载(下面链接或者本文末个人名片直接获取):软件开发全套资料-…...

电脑文件qt5core.dll如何修复?如何快速的解决qt5core.dll丢失问题
软件应用程序依赖于各种复杂的文件系统以保证其顺畅运行。这些文件中,动态链接库(Dynamic Link Library,简称DLL)是Windows操作系统中实现多种功能的关键组件之一。然而,DLL文件出现问题是Windows用户可能面临的常见挑…...

USART串口通信(stm32)
一、串口通信 通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统 通信协议:制定通信的规则,通信双方按照协议规则进行数据收发 STM32F103C8T6 USART资源: USART1、 USART2、 USART3 自带波特率发生器&…...

快速分析变量间关系(Boruta+SHAP+RCS)的 APP(streamlit)
快速分析变量间关系(BorutaSHAPRCS)的 APP(streamlit) 以下情况下,你需要这个快速分析的APP: 正式分析之前的预分析,有助于确定分析的方向和重点变量;收集变量过程中,监测收集的变量…...

解决docker中container运行闪退终止的问题
在运行bindmount-test时,点击完运行按钮后闪退结束运行。 第一步查看log日志: 2024-05-18 23:46:18 Error: Cannot find module /app/nodemon 2024-05-18 23:46:18 at Function.Module._resolveFilename (internal/modules/cjs/loader.js:668:15) …...

Redis 性能管理
一、Redis 性能管理 #查看Redis内存使用 172.168.1.11:6379> info memory 1. 内存碎片率 操作系统分配的内存值 used_memory_rss 除以 Redis 使用的内存总量值 used_memory 计算得出。内存值 used_memory_rss 表示该进程所占物理内存的大小,即为操作系统分配给…...

节水“云”科普丨北京昌平VR节水云展馆精彩上线
2024年5月15日上午,由北京昌平区水务局主办的“推进城市节水,建设美丽昌平——2024年全国城市节约用水宣传周暨‘坚持节水优先 树立节水标杆’昌平节水在行动主题实践活动”隆重举办,活动期间,昌平区水务局应用VR虚拟现实技术创新…...

linux的系统调用open, read函数(文件编程)使用demo
1.引言 为了学习linux系统下的app开发,记载了学习文件编程的笔记 2.open函数 功能 打开一个文件 头文件 #include<sys/stat.h> #include<fcntl.h> 函数形式 int open(const char* pathname, int flags, mode_t mode); 返回值 如果调用成功,…...

C语言基础——循环(2)+关机程序
欢迎点赞支持 个人主页:励志不掉头发的内向程序员; 专栏主页:C语言基础; 文章目录 目录 前言 一、for循环的补充 二、循环的嵌套 1、嵌套的介绍 1.1 练习: 题目解析: 优化: 三、goto语句 1、go…...

cnVcXsrv 21.1.13.1—VcXsrv 21.1.13中文版本简单说明~~
对于VcXsrv的使用目的和用途相信大家都很了解。前不久VcXsrv做了更新,并且将项目托管到github上了。链接如下: VcXsrv: Windows X-server based on the xorg git sourceshttps://github.com/marchaesen/vcxsrv也可以简单查看如下链接: VcXs…...

心链2---前端开发(整合路由,搜索页面,用户信息页开发)
心链——伙伴匹配系统 接口调试 说书人📖:上回书说到用了两种方法查询标签1.SQL查询,2.内存查询;两种查询效率是部分上下,打的是难解难分,是时大地皴裂,天色聚变,老祖斟酌再三最后决…...

wordpress主题模板兔Modown 9.1开心版附送erphpdown v17.1插件
Modown 9.1开心版是一款模板兔开发的wordpress主题可,持续更新多年,优秀的资源下载类主题该模板基于Erphpdown,可以销售软件、视频教程、文章等等,通过主题和插件结合可以实现付费下载、付费阅读等功能,配合模板兔的一…...

openai api的初次尝试
不懂已经不去百度了,现在直接问chatgpt就解决绝大多数问题了。 OpenAI API目前还没有官方支持的npm库,但是您可以使用现有的第三方npm库进行OpenAI API的访问和使用。这里提供一个npm库 openai-node 的安装和使用方法: 在命令行或终端中使用…...

Distributed Transactions Mit 6.824
Topic1:distributed transactions concurrency control atomic commit 传统计划:事务 程序员标记代码序列的开始/结束作为事务。 事务示例 x 和 y 是银行余额——数据库表中的记录。x 和 y 位于不同的服务器上(可能在不同的银行&#x…...

Redis可视化工具:Another Redis Desktop Manager下载安装使用
1.Github下载 github下载地址: Releases qishibo/AnotherRedisDesktopManager GitHub 2. 安装 直接双击exe文件进行安装 3. 连接Redis服务 先启动Redis服务,具体启动过程可参考: Windows安装并启动Redis服务端(zip包)…...

Parquet文件格式详解(含行、列式存储区别)
Parquet文件格式详解 Parquet 是一种列式存储格式,旨在高效地存储和处理大规模数据集。它被设计用于在大数据生态系统中进行数据存储和分析,如 Apache Hadoop 和 Apache Spark。 行式存储 vs 列式存储 在了解 Parquet 文件格式之前,先来对…...

一文了解https为什么是安全的
目录 前言一、https和http二、http为什么不安全?2.1 http的工作原理2.2 http的明文传输 三、https3.1 加密3.2 身份验证 四、总结 前言 目前绝大多数网站都已经切换到了https,切换的原因很简单,因为它更安全,https未来会完全取代…...

[‘column‘]和[:,‘column‘]的区别
之前,关于numpy和pandas的操作一直不熟悉,对于获取数据中的行,列一直混淆。 df[column] df[column]是 Pandas DataFrame 切片的常用语法,用于选择名为 column 的单个列。它返回一个 Pandas Series 对象。 df.loc[:,column] df[:,…...

icloud如何高效利用
iCloud是Apple提供的一项云存储和云计算服务,能够帮助用户在不同的Apple设备之间同步和共享数据。要高效利用iCloud,可以参考以下几个方面: 自动备份:确保所有重要的Apple设备都开启了iCloud备份功能,这样可以自动将设…...

k8s二进制安装与部署
目录 一、实验目的 二、实验环境 三、实验步骤 3.1 操作系统初始化配置 3.2 部署 docker引擎 3.3 部署 etcd 集群 3.3.1 在 master01 节点上操作 3.3.2 在 node01 节点上操作 3.3.3 在 node02 节点上操作 3.4 部署 Master 组件 3.4.1 在 mast…...

驱动编译报error: negative width in bit-field ‘<anonymous>’错误
错误如下图所示: 代码如下: 问题点:module_param的其他用户的权限参数上。 在Linux中,文件权限由读(r)、写(w)、执行(x)权限组成,分别对应数值4、2、1。 第一位0是占位符,在这里没有意义,因为…...

Go语言的命名规范是怎样的?
文章目录 Go语言的命名规范详解一、标识符命名规范示例代码 二、包名命名规范示例代码 三、变量命名规范示例代码 四、常量命名规范示例代码 五、函数命名规范示例代码 总结 Go语言的命名规范详解 在Go语言中,代码的命名规范对于项目的可读性、可维护性和可扩展性至…...

Vue3骨架屏(Skeleton)
效果如下图:在线预览 APIs 参数说明类型默认值必传animated是否展示动画效果booleantruefalsebutton是否使用按钮占位图boolean | SkeletonButtonPropsfalsefalseavatar是否显示头像占位图boolean | SkeletonAvatarPropsfalsefalseinput是否使用输入框占位图boolea…...

【文末附gpt升级方案】亚马逊与Hugging Face合作:定制芯片低成本运行AI模型的创新探索
亚马逊与Hugging Face合作:定制芯片低成本运行AI模型的创新探索 摘要 本文探讨了亚马逊云部门与人工智能初创公司Hugging Face的合作,旨在通过定制计算芯片Inferentia2在亚马逊网络服务(AWS)上更低成本地运行数千个AI模型。文章首…...

二叉树的链式实现
目录 一、二叉树的基础操作 二、二叉树代码图解 2.1 遍历 2.2 求大小 2.3 创建与销毁 2.4 与队列结合解决问题 三、二叉树C语言源码汇总 二叉树的代码实现运用了函数递归的思想,了解函数递归的知识请见博主的另一篇博客: http://t.csdnimg.cn/Po…...