ELF文件详解
ELF 文件不仅仅是一个格式,它是 Linux 世界中程序的"灵魂容器",承载着程序从编译到执行的整个生命周期。
今天咱们来聊一个看起来高深,实际上理解起来其实挺简单的话题—— ELF 文件。
不知道你有没有想过:我们敲下./program命令的那一刻,计算机是怎么把这个文件变成一个活蹦乱跳的进程的?这背后的"黑魔法"到底是什么?
没错,答案就是今天的主角:ELF(Executable and Linkable Format)可执行与可链接格式。你可以把它理解为 Linux 世界里程序的"灵魂容器"!
一、什么是 ELF 文件?给个痛快话!
简单来说,ELF 是 Linux 下的可执行文件格式,就像 Windows 下的 .exe 一样。但别被这个简单的解释骗了,ELF 可比 .exe 复杂得多,也强大得多!
ELF 文件可以是:
可执行文件(比如你的./program)
目标文件(编译后但还没链接的 .o 文件)
共享库文件(就是 .so 文件,类似 Windows 下的 .dll)
核心转储文件(程序崩溃时的那个core dump)
本质上,ELF 就是一个容器,里面装着代码、数据以及程序运行所需的各种信息,按照特定的格式组织起来。
二、初见 ELF:第一印象很重要
想知道一个文件是不是 ELF 格式的?超简单:
$file/bin/ls
/bin/ls:ELF64-bitLSBexecutable,x86-64,version1(SYSV),dynamicallylinked(usessharedlibs),forGNU/Linux2.6.32,BuildID[sha1]=c8ada1f7095f6b2bb7ddc848e088c2d615c3743e,stripped
看到没?只要文件输出信息的开头是"ELF",那它就是 ELF 格式的!
再来点儿硬核的,我们直接看一下 ELF 文件的前几个字节:
$hexdump-C-n16/bin/ls
000000007f454c46020101000000000000000000|.ELF............|
00000010
这里最开始的7f 45 4c 46就是 ELF 文件的"魔数"(Magic Number)。其中 45 4c 46 是 ASCII 码中的 "ELF" 三个字母,前面的 7f 是一个特殊字符。这四个字节就是 ELF 文件的"身份证",操作系统首先会检查这四个字节,确认它是不是一个 ELF 文件。
三、ELF 文件的内部结构:化繁为简
很多教程一上来就给你画个复杂的结构图,看得人头晕眼花。咱们先别急,我用一个简单的类比来帮你理解:
把 ELF 文件想象成一本"程序说明书",这本书有三部分组成:
文件头(ELF Header):相当于书的封面和目录,告诉你这本书有什么内容,怎么看
程序头表(Program Header Table):相当于给"阅读器"(操作系统)看的指南,告诉它怎么把这本书变成一个活的程序
节区头表(Section Header Table):相当于给"编辑器"(链接器、调试器)看的指南,告诉它这本书的内部结构
然后,书的主体内容就是各种节区(Sections)或段(Segments),里面装着代码、数据等实际内容。
直观一点,用图来表示就是:
+------------------+
| ELF Header | <-- 文件开始处的标识信息和总体布局
+------------------+
| 程序头表 | <-- 告诉操作系统如何加载
| Program Header 1 |
| Program Header 2 |
| ... |
+------------------+
| Section 1 | <-- 实际内容,如代码、数据等
| Section 2 |
| ... |
+------------------+
| 节区头表 | <-- 描述每个Section的信息
| Section Header 1 |
| Section Header 2 |
| ... |
+------------------+
哎,你可能会问:什么是节区(Section)?什么又是段(Segment)?它们有什么区别?
简单来说:
节区(Section):是 ELF 文件存储的基本单位,针对链接器
段(Segment):是运行时内存的基本单位,针对加载器
一个段通常包含多个功能相似的节区。比如,包含代码的所有节区会被归入到一个叫做"TEXT"的段中。
四、深入解剖 ELF文件:逐层剥开
1. ELF头(ELF Header)
ELF 头是整个文件的"门面",包含了文件的基本信息和指向其他部分的指针。用readelf -h命令可以查看:
这里面最重要的信息是:
Entry point address:程序执行的入口点地址
Start of program headers:程序头表的位置
Start of section headers:节区头表的位置
2. 程序头表(Program Header Table)
程序头表告诉操作系统如何创建进程映像,用readelf -l命令查看:
最重要的是那些类型为LOAD的段,它们会被加载到内存中。
注意看Flags:
R表示可读(Read)
W表示可写(Write)
E表示可执行(Execute)
这就是为什么有的内存区域可执行,有的只能读不能写,这些权限在 ELF 文件里就定义好了!
3. 节区头表(Section Header Table)
节区头表描述了文件中各个节区的信息,用readelf -S查看:
常见的重要节区包括:
.text:存放程序的机器代码
.data:已初始化的全局变量和静态变量
.bss:未初始化的全局变量和静态变量(不占用文件空间)
.rodata:只读数据(如字符串常量)
.symtab:符号表,存储程序中定义和引用的函数、变量
.strtab:字符串表,通常存储符号名
.dynamic:动态链接信息
五、ELF 文件的生命周期:从编译到执行
为了彻底搞懂 ELF 文件,我们需要了解它的整个生命周期:
源代码(.c) --编译--> 目标文件(.o) --链接--> 可执行文件 --加载--> 进程
1. 编译阶段:生成目标文件(.o)
当你写完 C 代码,运行gcc -c hello.c时,会得到一个hello.o的目标文件。这个文件已经是 ELF 格式的了,但它还不能直接执行,因为里面有很多"坑"等着被填上。
这些"坑"在 ELF 文件中表现为"重定位表",用readelf -r可以看到:
$readelf-rhello.o
Relocationsection'.rela.text'at offset 0x2d0 contains 2 entries:
OffsetInfoTypeSym.ValueSym.Name+Addend
000000000013000a00000004R_X86_64_PLT320000000000000000printf-4
000000000023000b00000004R_X86_64_PLT320000000000000000exit-4
这表示代码中调用了printf和exit函数,但编译器不知道它们在哪儿,所以留了个"坑"等着链接器来填。
2. 符号表:程序的"通讯录"
说到这些函数(printf 、exit),咱们不得不提 ELF 文件中的"符号表"。简单来说,符号表就像是程序的"通讯录",记录了程序中所有函数和变量的名字和位置。
来看看符号表长啥样:
$readelf-shello.o
Symboltable'.symtab'contains 12 entries:
Num:ValueSizeTypeBindVisNdxName
0:00000000000000000NOTYPELOCALDEFAULTUND
1:00000000000000000FILELOCALDEFAULTABShello.c
2:00000000000000000SECTIONLOCALDEFAULT1
...
9:000000000000000041FUNCGLOBALDEFAULT1main
10:00000000000000000NOTYPEGLOBALDEFAULTUNDprintf
11:00000000000000000NOTYPEGLOBALDEFAULTUNDexit
瞧,这里面有main函数(我们自己定义的),还有printf和exit(外部函数)。注意它们的Ndx(索引)列:main是1,表示在第1个节区;而printf和exit是UND,表示"未定义",这就是前面说的"坑"。
这个目标文件的符号表就像一张"半成品通讯录",只记录了自己有什么函数,以及自己需要哪些外部函数,但还不知道那些外部函数在哪里。所以它还不能独立工作,需要链接器来帮忙找到这些外部函数。
3. 动态链接:程序的"即插即用"
说到外部函数,就不得不提 ELF 的一个超强功能:动态链接。
还记得 Windows 上安装软件时经常冒出的"DLL缺失"错误吗?Linux 上也有类似概念,不过实现得更优雅,这就是动态链接库(.so文件)。
动态链接的好处简直不要太多:
节省内存:多个程序共享同一个库
节省磁盘:不用把所有代码都打包进可执行文件
方便升级:库更新后,程序自动用上新版本,不用重新编译
那么问题来了:程序怎么知道自己需要哪些库?又是如何找到这些库的呢?
ELF 文件中有一个特殊的.dynamic节区,专门记录这些信息:
$readelf-d/bin/ls|grepNEEDED
0x0000000000000001(NEEDED)Shared library:[libselinux.so.1]
0x0000000000000001(NEEDED)Shared library:[libc.so.6]
这告诉我们,ls命令依赖于这两个共享库。如果你想更直观地看到所有依赖及它们的实际位置,可以用ldd命令:
$ ldd /bin/ls
linux-vdso.so.1 (0x00007ffc961cd000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f27f989e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f27f96b3000)
...
看到没?ldd不仅告诉你需要哪些库,还告诉你它们的实际位置和加载地址。
那程序又是怎么找到这些库的呢?它会按照以下顺序查找:
环境变量LD_LIBRARY_PATH指定的目录
可执行文件的RPATH属性指定的目录
/etc/ld.so.cache缓存中记录的位置
默认目录如/lib、/usr/lib等
动态链接器(ld.so)会在程序启动时自动处理这些依赖关系,把所有需要的库都加载进来,就像乐高积木一样把程序拼装完整,非常巧妙!
4. 链接阶段:生成可执行文件
链接器会把多个目标文件和库文件链接在一起,解决那些"坑"(重定位),最终生成可执行文件。
那么链接器具体是怎么解决这些"坑"的呢?简单来说就是做个"牵线搭桥"的活:
收集所有目标文件中的符号表,建立一个全局符号表
找到所有标记为"未定义"(UND)的符号
在全局符号表或者库文件中寻找这些符号的定义
把找到的地址填回原来的"坑"中
比如当链接器找到printf函数在 libc.so 中的实际地址后,就会修改原来调用 printf 的指令,让它指向正确的地址。
链接完成后,再看同一个程序的符号表,会发现那些 UND 的符号要么有了实际地址(静态链接),要么指向了动态链接的跳转表(动态链接)。
在动态链接的情况下,还会在 ELF 文件中记录运行时需要哪些共享库,前面已经说过了。
5. 加载阶段:从文件到进程
当你执行./program时,操作系统(确切地说是加载器 ld.so )会做这些事:
检查 ELF 头的合法性
根据程序头表,将需要的段加载到内存
如果是动态链接的,还会找到并加载所需的共享库
跳转到 Entry Point 开始执行
这个过程可以用strace命令观察:
$ strace ./hello
execve("./hello", ["./hello"], 0x7ffcef8db490/* 52 vars */)= 0
brk(NULL) = 0x55c84f34c000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
...
execve就是创建新进程的系统调用,后面一系列操作就是在加载和准备程序运行环境。
六、ELF实用工具箱:玩转ELF文件
好了,了解了 ELF 的原理后,来看看有哪些工具可以帮我们操作 ELF 文件:
(1) file:判断文件类型
$ file /bin/ls
(2) readelf:查看ELF文件的所有信息
$ readelf -a /bin/ls # 显示全部信息
(3) objdump:反汇编 ELF 文件
$ objdump -d /bin/ls # 反汇编代码段
(4) nm:列出符号表
$ nm /bin/ls # 显示符号(函数、变量)
(5) ldd:查看动态依赖
$ ldd /bin/ls # 显示依赖的共享库
(6) strings:提取文件中的字符串
$ strings /bin/ls | grep"GNU"# 查找包含"GNU"的字符串
(7) strip:移除ELF文件中的符号表和调试信息
$ strip -s program # 减小文件体积
(8) patchelf:修改 ELF 文件的属性
$ patchelf --set-interpreter /lib64/ld-custom.so program # 修改解释器
七、实际应用:ELF文件的那些神奇玩法
ELF文件的知识不仅仅是理论,来看看一些实际的例子:
1. 程序加固与混淆
想象你开发了一个软件不想被轻易破解:
# 删除符号表,让逆向分析更困难
$ strip --strip-all myprogram
# 对比前后大小
$ ls -lh myprogram*
-rwxr-xr-x 1 user user 236K myprogram
-rwxr-xr-x 1 user user 176K myprogram.stripped
看,文件体积一下减少了几十k,因为符号信息都被删掉了!
2. 程序补丁与热修复
假设你想修改程序使用的解释器路径:
# 查看当前解释器
$ readelf -l myprogram | grep interpreter
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
# 修改为自定义解释器
$ patchelf --set-interpreter /opt/mylibs/ld-linux.so myprogram
# 确认修改成功
$ readelf -l myprogram | grep interpreter
[Requesting program interpreter: /opt/mylibs/ld-linux.so]
这样程序就会使用你自定义的动态链接器,而不需要重新编译!
更酷的是,Linux 还提供了一种不用重启程序就能热修复的黑科技——LD_PRELOAD环境变量!它可以让你悄悄地"替换"程序中的函数实现。
来看一个简单实用的例子 —— 监控程序的内存分配:
创建一个简单的内存跟踪库: memtrace.c
#define _GNU_SOURCE
#include<stdio.h>
#include<dlfcn.h>
// 原始malloc函数指针
staticvoid* (*real_malloc)(size_t) = NULL;
// 拦截 malloc 函数
void* malloc(size_t size){
// 延迟初始化原始函数
if (real_malloc == NULL) {
real_malloc = dlsym(RTLD_NEXT, "malloc");
}
// 调用原始malloc
void* ptr = real_malloc(size);
// 打印跟踪信息
fprintf(stderr, "malloc(%zu) = %p\n", size, ptr);
return ptr;
}
编译成共享库:
$ gcc -shared -fPIC memtrace.c -o libmemtrace.so -ldl
接着使用我们的库监控任何程序的内存分配:
LD_PRELOAD=./libmemtrace.so ./my_program
输出:
malloc(100) = 0x55e930e2f6b0
malloc(200) = 0x55e930e2f720
malloc(300) = 0x55e930e2f7f0
看到了吗?我们只用了十几行代码,就实现了一个能够监控任何程序内存分配的工具!这个例子的工作原理很简单:
定义一个与系统函数同名的malloc
用dlsym(RTLD_NEXT, "malloc")找到真正的 malloc 函数
在调用真正的 malloc 前后添加我们的代码(这里是打印日志)
通过LD_PRELOAD让系统优先加载我们的库
这种技术经常用于:
调试内存问题
给程序添加日志
修改程序行为而不用改源码
临时修复运行中的服务
当然,这项技术也常被黑客利用来劫持程序函数,所以理解它不仅能提升编程能力,也对安全防护很重要!
八、总结:ELF 文件的精髓
好了,咱们来总结一下 ELF 文件的核心要点:
(1) ELF是容器:装载了代码、数据和各种元数据
(2) 分层结构:ELF 头、程序头表、节区、节区头表
(3) 两种视角:
执行视角:段(Segments)- 加载器关心
链接视角:节(Sections)- 链接器关心
(4) 生命周期:从源代码到目标文件,再到可执行文件,最后变成进程
当你理解了 ELF 文件的本质,Linux 下的很多问题就迎刃而解了:为什么有些程序不能在不同版本的 Linux 上运行?为什么动态库版本不匹配会导致程序崩溃?为什么有些恶意软件难以检测?——这些问题的答案都藏在 ELF 文件的结构中!
记住,ELF 文件不仅仅是一个格式,它是 Linux 世界中程序的"灵魂容器",承载着程序从编译到执行的整个生命周期。
相关文章:
ELF文件详解
ELF 文件不仅仅是一个格式,它是 Linux 世界中程序的"灵魂容器",承载着程序从编译到执行的整个生命周期。 今天咱们来聊一个看起来高深,实际上理解起来其实挺简单的话题—— ELF 文件。 不知道你有没有想过:我们敲下./…...
【学习笔记】Shell编程---流程控制语句
最近学了好多个流程控制语句,都有点混乱了,赶紧先把各种用法记录下来! if 语句 语法格式: if 条件测试命令串 then 条件为真时执行的命令 else 条件为假时执行的命令 fi 以关键字if开头,后跟条件测试表达式&…...
TensorFlow 常见使用场景及开源项目实例
TensorFlow 常见使用场景及开源项目实例 摘要 本文详细介绍了 TensorFlow 在多个领域的典型应用及其对应的开源项目案例。涵盖了图像处理、自然语言处理、语音音频处理、推荐系统与时间序列预测、移动端与边缘计算以及生成式模型与创意应用等多方面内容,列举了大量…...
王炸组合!STL-VMD二次分解 + Informer-LSTM 并行预测模型
往期精彩内容: 单步预测-风速预测模型代码全家桶-CSDN博客 半天入门!锂电池剩余寿命预测(Python)-CSDN博客 超强预测模型:二次分解-组合预测-CSDN博客 VMD CEEMDAN 二次分解,BiLSTM-Attention预测模型…...
OpenCV进阶操作:风格迁移以及DNN模块解析
文章目录 前言一、风格迁移1、风格迁移是什么?2、步骤1)训练2)迁移 二、DNN模块1、什么是DNN模块2、DNN模块特点3、流程图4、图像预处理功能 三、案例实现1、数据预处理2、加载模型 总结 前言 风格迁移(Style Transfer࿰…...
使用bitNet架构
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、配置二、报错总结 前言 大型语言模型(LLM)面临的挑战:高能耗、高内存需求、部署门槛高。 微软提出 BitNet 架构&#x…...
OpenCV中的光流估计方法详解
文章目录 一、引言二、核心算法原理1. 光流法基本概念2. 算法实现步骤 三、代码实现详解1. 初始化设置2. 特征点检测3. 光流计算与轨迹绘制 四、实际应用效果五、优化方向六、结语 一、引言 在计算机视觉领域,运动目标跟踪是一个重要的研究方向,广泛应用…...
Java集合框架详解与使用场景示例
Java集合框架是Java标准库中一组用于存储和操作数据的接口和类。它提供了多种数据结构,每种数据结构都有其特定的用途和性能特点。在本文中,我们将详细介绍Java集合框架的主要组成部分:List、Set和Queue,并通过代码示例展示它们的…...
多模态融合【十九】——MRFS: Mutually Reinforcing Image Fusion and Segmentation
目录 一.摘要 二.Introduction 三. 背景与动机 四.方法 4.1. 概述 4.2. IGM-Att模块 4.3. PC-Att模块 4.4. 任务头 五.实验 5.1. 数据集与实现细节 5.2. 语义分割 5.3. 图像融合 5.4. 消融研究 5.5. IGM-Att和PC-Att的应用增益 5.6. 复杂度讨论 5.7. 目标检测的…...
音频转文字-在线工具包及使用记录
资料来源:https://zhuanlan.zhihu.com/p/269603431(多种方案) 视频教程:https://www.youtube.com/watch?vL1H5ov4WTBg https://github.com/openai/whisper // 创建虚拟环境 python -m venv myvnev// 激活虚拟环境 source myvne…...
集合-进阶
Collection collection的遍历方式 迭代器遍历 不依赖索引 import java.util.ArrayList; import java.util.Collection; import java.util.Iterator;public class mycollection {public static void main(String[] args) {//1.创建集合并添加元素Collection<String> co…...
【阿里云】阿里云 Ubuntu 服务器无法更新 systemd(Operation not permitted)的解决方法
零、前言 目前正在使用的Ubuntu服务器中,仅阿里云(不止一台)出现了这个问题,因此我判定是阿里云服务器独有的问题。如果你的服务器提供商不是阿里云,那么这篇文章可能对你没有帮助。 如果已经因为升级错误导致依赖冲突…...
wpf DataGrid 行选择 命令绑定
在WPF中实现DataGrid行选择与命令绑定的MVVM模式,可通过以下方式结合代码示例实现: 1. 基础绑定与命令触发(SelectionChanged事件绑定) 通过Interaction.Triggers捕获SelectionChanged事件,并绑定到ViewModel中的命令: <DataGrid ItemsSource="{Binding I…...
【认知思维】验证性偏差:认知陷阱的识别与克服
什么是验证性偏差 验证性偏差(Confirmation Bias)是人类认知中最普遍、最根深蒂固的心理现象之一,指的是人们倾向于寻找、解释、偏爱和回忆那些能够确认自己已有信念或假设的信息,同时忽视或贬低与之相矛盾的证据。这种认知偏差影…...
大容量存储的高性能 T-BOX 方案对智能网联汽车的支撑
在智能网联汽车快速发展的当下,车载 T-BOX(Telematics Box)作为车辆与云端互联的核心枢纽,其性能和可靠性直接决定了用户体验的上限。米客方德(MK)推出的基于 STM32H7RX 主控芯片与 MKDV4GIL-AST࿰…...
Linux 内核网络协议栈:从 Socket 类型到协议注册的深度解析
Linux 内核的网络协议栈是一个复杂而高效的体系,涉及多层次的协议处理与数据流转。本文通过分析核心数据结构(如 inetsw 数组、sock_type 枚举)和关键函数(如 inet_add_protocol),深入探讨其工作原理与设计哲学。 一、Socket 类型与 sock_type 枚举 1.1 Socket 类型的定…...
vim,gcc/g++,makefile,cmake
一、vim:你的小帮手——文本编辑器 它是干嘛的? 想象你的代码就像是写在一本“程序的笔记本”里,vim就是一个超级厉害的“数字笔记本”或“文字编辑器”。 它有什么用? 编写代码:编辑、修改你的源代码代码高亮&…...
解决 CentOS 7 镜像源无法访问的问题
在国内使用 CentOS 系统时,经常会遇到镜像源无法访问或者下载速度慢的问题。尤其是默认的 CentOS 镜像源通常是国外的,如果你的网络环境无法直接访问国外服务器,就会出现无法下载包的情况。本文将介绍如何修改 CentOS 7 的镜像源为国内镜像源…...
“傅里叶变换算法”来检测货物外形损坏
“傅里叶变换算法”来检测货物外形损坏 要使用傅里叶变换算法来检测货物外形损坏,首先需要理解基本概念。傅里叶变换是一种数学变换,用于将信号从时域(或空间域)转换到频域。在图像处理中,二维傅里叶变换可以用来分析…...
python打卡day24
可迭代对象、OS模块 知识点回顾: 元组可迭代对象os模块 作业:对自己电脑的不同文件夹利用今天学到的知识操作下,理解下os路径 1.元组 在day3的打卡内容中就介绍了元组,跟列表比起来就是用了圆括号,有序可以重复&#x…...
MapReduce 入门实战:WordCount 程序
一、引言 在大数据处理领域,MapReduce 是一种开创性的编程模型和处理框架,它使得我们能够高效地在大规模分布式系统上处理海量数据。而 WordCount 程序作为 MapReduce 的经典入门案例,堪称大数据领域的 “Hello World”,帮助无数…...
深度剖析:Vue2 项目兼容第三方库模块格式的终极解决方案
当我们为 Vue2 项目引入某些现代 JavaScript 库时,常常会遇到这样的报错: error in ./node_modules/some-lib/lib/index.mjs Cant import the named export xxx from non EcmaScript module这类问题的本质是模块格式的世纪之争 —— ES Moduleÿ…...
5.11作业
拓扑图: 需求分析: 要求五台路由器的环回地址均可以相互访问 配置: r1 int g 0/0/0 i…...
MyBatis 批量新增与删除功能完整教程
一、功能概述 通过 MyBatis 动态 SQL 实现以下功能: 批量新增:一次性插入多条员工记录,支持自增主键回填。批量删除:根据 ID 数组一次性删除多条记录。二、代码逐行解析 1. Mapper 接口定义 // 批量新增:传入员工对象集合 void insertAll(List<Emp> empList);// …...
Spark,RDD中的行动算子
RDD中的行动算子 collect算子 格式:def collect(): Array[T] 参数说明:该算子没有参数。 并以数组的形式返回 统计个数 reduce算子 格式:def reduce(func: (T, T) > T): T 返回值:返回一个单一的值,其类型与…...
Linux:进程控制2
一:进程程序替换 1. 一旦程序替换成功,就去执行新代码了,原始代码的后半部分已经不存在了 2. exec*系列的函数,没有成功返回值,只有失败返回值-1 在程序替换的过程中,并没有创建新的进程,只是…...
Java jar包程序 启动停止脚本 shell bash
启动 启动时 可指定前缀(名称) start.sh #!/bin/bash # 使用时直接运行# 寻找当前目录下后缀为 .jar 的文件 #options($(find . -maxdepth 1 -type f -name "*.jar")) # 寻找当前目录下后缀为 .jar 的文件,并按时间倒序排序 opt…...
【Linux】进程通信 管道
🌻个人主页:路飞雪吖~ 🌠专栏:Linux 目录 一、👑进程间通信分类 二、👑管道 🌟什么是管道? 🌟匿名管道 🎉原理: 🔥站在文件描述…...
基于智能家居项目 解析DHT11温湿度传感器
一、模块简介 DHT11 是一款数字式温湿度传感器,内部集成了温度传感元件、湿度传感元件以及一个 8 位单片机芯片,用于采集数据和通信。。 测量范围:湿度 20%~90% RH,温度 0~50℃ 精度:湿度 5% …...
3.1 泰勒公式出发点
第一步:引入背景与动机 首先,泰勒公式(Taylor Series)是数学分析中的一个重要工具,它允许我们将复杂的函数近似为多项式形式。这不仅简化了计算,还帮助我们更好地理解函数的行为。那么为什么我们需要这样一…...
裸机开发的核心技术:轮询、中断与DMA
一、裸机开发的核心技术:轮询、中断与DMA 1. 轮询(Polling) 定义:程序主动、周期性地检查硬件状态或数据。应用场景:适用于简单、实时性要求不高的任务。示例: C while (1) { if (GPIO_ReadPin(SENSOR_P…...
从零开始:使用 Vue-ECharts 实现数据可视化图表功能
目录 前言为什么选择 Vue-ECharts案例:Vue-Echart开发一个分组柱状图 安装依赖 引入 全局引入 按需引入编写组件总结 前言 你好,小二!很高兴你愿意分享关于 Vue-ECharts 的使用经验。 📊 Vue-ECharts:让你在 Vue 项…...
Antd中Form详解:
1.获取Form表单值的方式: ① 使用Form.useForm()钩子(推荐方式) const [form] Form.useForm();const getFormValues () > {const values form.getFieldsValue();};<Form form{form}>...<Form.Item label{null}><Button onClick{ge…...
(2)python开发经验
文章目录 1 pyside6加载ui文件2 使用pyinstaller打包 更多精彩内容👉内容导航 👈👉Qt开发 👈👉python开发 👈 1 pyside6加载ui文件 方法1: 直接加载ui文件 from PySide6.QtWidgets import QAp…...
Landsat 5介绍
USGS Landsat 5 Level 2, Collection 2, Tier 1 数据集可用性:1984-03-16T16:18:01Z–2012-05-05T17:54:06Z 数据集提供程序 USGS Earth Engine 代码段 ee.ImageCollection("LANDSAT/LT05/C02/T1_L2") open_in_new 重新访问间隔:16 天 说…...
PowerShell 实现 conda 懒加载
问题 执行命令conda init powershell会在 profile.ps1中添加conda初始化的命令。 即使用户不需要用到conda,也会初始化conda环境,拖慢PowerShell的启动速度。 解决方案 本文展示了如何实现conda的懒加载,默认不加载conda环境,只…...
解锁ozon运营新路径:自养号测评技术如何实现降本增效
OZON测评自养号技术在跨境电商运营中具有显著的技术优势,主要体现在环境安全、账号控制、成本效率及风险规避等方面。以下是具体分析: 一:安全可控的测评环境搭建通过模拟俄罗斯本地物理环境和家庭住宅IP,自养号测评可规避平台风…...
算法第十七天|654. 最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树
654. 最大二叉树 题目 思路与解法 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:def constructMaximumB…...
Spring Boot 的自动配置为 Spring MVC 做了哪些事情?
Spring Boot 的自动配置为 Spring MVC 做了大量的工作,极大的简化了我们开发时的配置负担,我们可以快速启动并运行一个基于 Spring MVC 的 Web 应用。以下是 Spring Boot 自动配置为 Spring MVC 所做的主要事情: DispatcherServlet 的自动注册…...
【python】—conda新建python3.11的环境报错
1.报错 conda create -n py3.11 python3.11 --channel https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ Collecting package metadata: done Solving environment: failed PackagesNotFoundError: The following packages are not available from current channel…...
桑德拉精神与开源链动2+1模式AI智能名片S2B2C商城小程序的协同价值研究
摘要:本文深入剖析桑德拉无私奉献精神在商业领域的映射价值,结合开源链动21模式、AI智能名片及S2B2C商城小程序的技术特性,系统探讨其在用户赋能、资源协同与价值共创中的协同效应。研究表明,该技术组合通过去中心化激励、智能需求…...
JavaEE--初识网络
目录 一、IP地址 二、端口号 三、认识协议 四、五元组 五、协议分层 1. OSI七层模型 2. TCP/IP五层(或四层)模型 3. 网络设备所在分层 4. 封装和分用 一、IP地址 IP地址(Internet Protocol Address)是用于标识设备在网络…...
2.7/Q2,Charls最新文章解读
文章题目:Climate risks, multi-tier medical insurance systems, and health inequality: evidence from Chinas middle-aged and elderly populations DOI:10.1186/s12913-025-12648-2 中文标题:气候风险、多层次医疗保险制度和健康不平等—…...
Mac显卡的工作原理及特殊之处
目录 🧠 一、显卡的基本工作原理(适用于所有平台) 🍏 二、Mac 显卡的工作机制 1. Mac 使用的显卡类型 Intel 架构时代(Intel CPU Intel/AMD 显卡) Apple Silicon 时代(M1/M2/M3 芯片&…...
MUSE Pi Pro 编译kernel内核及创建自动化脚本进行环境配置
视频讲解: MUSE Pi Pro 编译kernel内核及创建自动化脚本进行环境配置 今天分享的主题为创建自动化脚本编译MUSE Pi Pro的kernel内核,脚本已经上传到中 GitHub - LitchiCheng/MUSE-Pi-Pro-Learning: MUSE-Pi-Pro-Learning ,有需要可以自行clon…...
flink的TaskManager 内存模型
Flink TaskManager 的内存模型是一个多层管理体系,从 JVM 进程到具体任务的内存分配均有明确的逻辑划分和配置策略。以下是其核心构成及运行机制: 一、内存模型总览 TaskManager 内存整体分为 JVM 特有内存 和 Flink 管理内存 两大层级&…...
【NLP 72、Prompt、Agent、MCP、function calling】
命运把我们带到哪里,就是哪里 —— 25.5.13 一、Prompt 1.User Prompt 用户提示词 当我们与大模型进行对话时,我们向大模型发送的消息,称作User Prompt,也就是用户提示词,一般就是我们提出的问题或者想说的话 但是我们…...
无人机俯视风光摄影Lr调色预设,手机滤镜PS+Lightroom预设下载!
调色详情 无人机俯视风光摄影 Lr 调色是利用 Adobe Lightroom 软件,对无人机从俯视角度拍摄的风光照片进行后期处理的调色方式。通过调整色彩、对比度、光影等多种参数,能够充分挖掘并强化画面独特视角下的壮美与细节之美,让原本平凡的航拍风…...
【HTML5】【AJAX的几种封装方法详解】
【HTML5】【AJAX的几种封装方法详解】 AJAX (Asynchronous JavaScript and XML) 封装是为了简化重复的异步请求代码,提高开发效率和代码复用性。下面我将介绍几种常见的 AJAX 封装方式。 方法1. 基于原生 XMLHttpRequest 的封装 XMLHttpRequest。其主要特点如下…...
STM32 __rt_entry
STM32中__rt_entry函数的深度解析 在STM32的启动流程中,__rt_entry是一个由ARM C库提供的核心函数,负责在__main完成基础初始化后,搭建完整的C语言运行环境。以下是其核心功能及工作机制的详细分析: 一、__rt_entry的核心作用 …...