当前位置: 首页 > article >正文

RISC-V架构:gp寄存器与链接器松弛

目录0 相关内容1 gpglobal pointer全局指针寄存器1. gp 寄存器的核心作用高效访问全局数据2. 为什么 Cortex-M 没有 gp3. gp 寄存器在 FreeRTOS 中的作用2 链接器松弛3 如何将全局小变量连接到 .sdata 段并设置 gp 到 .sdata 段的中间1.gp寄存器的初始指向地址是需要工程师人为书写代码规定 还是 编译器可以自行调节为什么必须由工程师在启动代码中手动写特殊情况动态链接器与 IFUNC2.实操一份工程.ld链接脚本如何将全局变量链接到.sdata段并且初始化gp寄存器第一步使用 __attribute__ 将变量指定到 .sdata 段第二步在链接脚本.ld中定义 .sdata 段和 __global_pointer$第三步在启动代码中初始化 gp 寄存器三步操作总结问如果启动文件无法修改能否在main()函数开头再设置gp寄存器并开启链接器松弛1. 为什么不能在 main() 中设置 gp2. 既然不能改启动代码有什么解决方案方案一修改链接脚本将入口点指向你自己的包装函数推荐3.上述实操代码为什么不需要手动开启链接器松弛.option push 和 .option pop 的作用完整的执行流程为什么这样设计4 链接器松弛所带来的优化效率优化前未使用链接器松弛优化后启用链接器松弛且变量在 gp 范围内总结对比核心收益0 相关内容【笔记】RISC-V学习基础知识补充与名词解释【权威书籍】RISC-V体系结构编程与实践【外部链接】RISC-V架构的链接器松弛优化详解-CSDN博客【外部链接】寄存器与寻址 |riscv-non-isa/riscv-asm-manual |DeepWiki1 gpglobal pointer全局指针寄存器【外部链接】RISC-V MCU gp全局指针说明-电子发烧友网1.gp寄存器的核心作用高效访问全局数据来源deepseek在 RISC-V 架构中gp全局指针寄存器是一个由应用程序二进制接口ABI约定的特殊寄存器其唯一且明确的目的不是存储单一的全局变量值而是作为一个固定的基址指针用于高效地访问位于内存中一小块区域通常称为“小数据”区域内的全局变量。它的工作原理可以分解为一个固定的锚点在程序启动时通常在启动代码_start中gp寄存器被初始化并指向内存中一个精心挑选的锚点地址 __global_pointer$。这个锚点一旦设定在程序的整个生命周期内都不会再被修改。一个优化窗口这个锚点之所以被选中是因为它位于所有“小数据”变量通常存放在.sdata段中的中间位置。链接器确保所有被标记为“小数据”的全局变量其地址与__global_pointer$之间的偏移量都在一个有符号的12位立即数范围内-2048 到 2047。这意味着任何对这些变量的访问都可以通过一条指令load/store gp, offset来完成。链接器松弛Linker Relaxation这是实现优化的关键步骤。编译器和汇编器在生成代码时会为每个全局变量访问预留出最通用的指令序列例如luiaddi来构造一个32位地址。链接器在最后阶段掌握了所有变量和gp的绝对地址后会进行“松弛”优化。它会检查每个变量访问如果该变量在gp的±2KB范围内链接器就会将原本的多条指令替换为一条基于gp的、带偏移量的单条访存指令。这就大大减少了代码尺寸并提升了执行速度。举例说明假设__global_pointer$指向0x20000800而你要访问的全局变量my_var位于0x20000100。未优化代码可能为lui a5, 0x20000lw a5, 256(a5)两条指令。优化后使用gp链接器会将其替换为一条指令lw a5, -1792(gp)因为0x20000100正好是gp减去 1792 字节的位置。2. 为什么 Cortex-M 没有gp你在 Cortex-M 中没有见过类似寄存器是完全正确的。这源于两者设计哲学和应用场景的根本不同RISC-V 的设计哲学RISC-V 遵循“简约”和“软件-硬件协同”的经典RISC理念。它提供少量的、功能单一的通用寄存器并通过精炼的 ABI 和强大的工具链特别是链接器来榨取性能。gp就是一个典型例子硬件只提供一个寄存器但通过 ABI 约定和链接器松弛实现了一个精巧的、对性能有益的优化。Cortex-M 的设计哲学Cortex-M 作为面向深度嵌入式、实时控制的 MCU其设计目标是在确定的极短时间内完成中断响应。为此它在硬件层面集成了强大的嵌套向量中断控制器NVIC并采用了“硬件压栈”等机制来加速上下文切换。在这种设计下增加一个需要由软件编译器/链接器协同才能发挥作用的gp寄存器带来的收益远不如硬件自动完成的优化显著。Cortex-M 的代码密度和性能更多依赖于 Thumb-2 指令集本身的高密度编码和硬件特性而不是这种依赖链接器松弛的优化。3.gp寄存器在 FreeRTOS 中的作用基于以上原理我们可以得出关于gp在 FreeRTOS 中角色的结论任务切换中不涉及gp在你提供的portcontextSAVE_CONTEXT_INTERNAL任务上下文存档和portcontextRESTORE_CONTEXT任务上下文恢复代码中完全没有出现对gp寄存器的保存和恢复操作。这是符合规范的因为gp在系统初始化后就是一个常量指向固定的内存区域。它属于整个程序而非单个任务。因此在任务切换时无需保存和恢复它这节省了上下文切换的开销。gp的优化作用依然存在FreeRTOS 作为一个软件层其自身的数据结构和全局变量以及应用程序中的全局变量都可以从gp优化中受益。只要你的链接脚本正确地定义了__global_pointer$并安排了.sdata段的位置链接器就会自动对符合条件的全局变量访问进行优化。潜在的冲突风险这一点非常重要。如果系统软件或某些库错误地修改了gp的值将会导致所有后续基于gp的全局变量访问出错。这正是 U-Boot 这类引导加载程序在早期启动阶段需要谨慎处理gp的原因有时甚至需要通过链接选项--no-relax来禁用此优化以避免与自身的全局数据访问方式冲突。但在一个标准的 FreeRTOS 应用中gp在 C 运行时初始化后被设定之后不应被任何代码修改。总结gp是什么RISC-V ABI 中用于优化小数据访问的基址寄存器配合链接器可实现单指令访存提升代码密度和速度。为什么 Cortex-M 没有设计理念不同。Cortex-M 通过硬件机制如 NVIC确保实时性而 RISC-V 的这种优化是软件工具链与硬件精简指令集协同的结果。在 FreeRTOS 中gp作为系统级常量不被任务切换代码保存/恢复但其优化作用贯穿整个程序生命周期包括 FreeRTOS 内核和应用代码。它静静地在后台工作为所有符合条件的数据访问提供一条指令的“快车道”。2 链接器松弛【外部链接】RISC-V架构的链接器松弛优化详解-CSDN博客【权威书籍】6.4节RISC-V体系结构编程与实践3 如何将全局小变量连接到 .sdata 段并设置 gp 到 .sdata 段的中间来源deepseek1.gp寄存器的初始指向地址是需要工程师人为书写代码规定 还是 编译器可以自行调节首先明确一点gp寄存器的初始指向地址必须由工程师在启动代码中通过指令显式写入编译器无法也不应该自动完成这个操作。gp寄存器的初始化是RISC-V ABI规定的、由启动代码通常用汇编编写执行的一个关键步骤。整个过程可以分解为两个环节链接器“规定位置”和启动代码“完成加载”。为什么必须由工程师在启动代码中手动写你可能会想既然链接器知道了__global_pointer$的地址为什么不能自动帮我们设置好gp寄存器这里面有几个关键的技术原因时机问题CPU启动时寄存器是随机值在 CPU 刚上电、执行第一条指令前所有通用寄存器包括gp的值都是不可预测的。因此必须由第一条软件指令将正确的值写入gp。这不可能由链接器在编译链接时自动完成因为它不产生可执行代码。避免“先有鸡还是先有蛋”的循环依赖你看到的启动代码中在加载gp的指令前特意加了.option norelax这是一个非常重要的细节。它的目的是临时关闭链接器松弛。如果这里不关闭链接器可能会错误地将lla gp, __global_pointer$这条指令本身也当作可以“优化”的指令。这就成了一个悖论在gp还没有被设置好之前你却要用尚未初始化的gp来设置它自己程序必定会出错。无法跨越的边界链接器与CPU编译器/链接器的工作是生成程序代码而 CPU 的工作是执行代码。gp寄存器的值是一个运行时的状态它只能在程序运行时被指令修改。链接器无法越过这个边界去直接修改 CPU 的内部状态。特殊情况动态链接器与 IFUNC在你的 FreeRTOS 裸机场景中你只需要关注上面的启动代码即可。但在更复杂的 Linux 系统中当存在动态链接和 IFUNC 时动态链接器ld.so也会介入在早期解析重定位时就查找__global_pointer$并初始化gp。这恰恰从反面印证了gp初始化的关键性——连动态链接器都必须专门处理它。2.实操一份工程.ld链接脚本如何将全局变量链接到.sdata段并且初始化gp寄存器来源deepseek第一步使用__attribute__将变量指定到.sdata段首先你需要告诉编译器哪些全局变量属于“小数据”应该被放在.sdata段中。这可以通过 GCC 的__attribute__((section(.sdata)))来实现 。示例代码// 将这个变量放到 .sdata 段 int my_small_data __attribute__((section(.sdata))) 100; // 也可以是一个数组 char my_small_array[64] __attribute__((section(.sdata))); // 如果你想让所有未特别指定的变量都默认尝试放入 .sdata // 可以在编译时加上 -msmall-data-limit8 之类的选项 // 这会让大小不超过8字节的全局变量自动进入 .sdata在实际项目中如果所有符合条件的变量都要享受gp优化通常不需要手动为每个变量添加__attribute__而是通过编译选项如-msmall-data-limitN让编译器自动将小于特定大小的全局变量放入.sdata和.sbss。第二步在链接脚本.ld中定义.sdata段和__global_pointer$这是最关键的一步。你需要在链接脚本的SECTIONS命令中显式地定义.sdata段的位置并在此处设置__global_pointer$符号。这个符号的地址必须被设置为gp寄存器的值。根据 RISC-V 的 ABI 和链接器惯例__global_pointer$通常被定义在.sdata段的起始位置加上0x800字节的地方。这是因为gp使用有符号的 12 位立即数偏移范围是 -2048 到 2047将gp放在段的中间可以覆盖整个段 。链接脚本示例.ld 文件片段SECTIONS { /* ... 其他段定义 ... */ . ALIGN(4); .sdata : { /* 关键定义 __global_pointer$ 符号 * 它的值被设置为当前地址即 .sdata 段起始地址 0x800 * 这样它就能覆盖整个段 */ __global_pointer$ . 0x800; *(.srodata .srodata.*) /* 可选的只读小数据 */ *(.sdata .sdata.*) /* 所有输入文件的 .sdata 段都放在这里 */ *(.sdata.*) *(.gnu.linkonce.s.*) /* 某些编译器的链接once数据 */ } /* .sbss 段未初始化的小数据通常紧跟着 .sdata 段 */ . ALIGN(4); .sbss : { *(.sbss .sbss.*) *(.gnu.linkonce.sb.*) *(.scommon) } /* ... .data, .bss 等其他段 ... */ }重要__global_pointer$ . 0x800;这行代码必须在所有*(.sdata.*)输入之前或之中定义这样gp才能指向段的中间位置 。如果.sdata段不存在链接器如 lld为了解析__global_pointer$符号甚至会尝试创建一个空的.sdata段来放置它 。第三步在启动代码中初始化gp寄存器最后在复位向量或启动代码通常是start.S的最开始你需要将链接脚本中定义的__global_pointer$的地址加载到gp寄存器中。这一步必须在任何使用gp进行优化的代码执行之前完成 。启动代码示例汇编.section .text.init .globl _start _start: # 初始化 gp (全局指针) 寄存器 # 注意这里必须临时禁用链接器松弛否则这条指令本身可能被错误地优化 .option push .option norelax la gp, __global_pointer$ .option pop # ... 其他初始化代码如设置 sp、清零 bss 等...为什么要用.option norelax这是 RISC-V 工具链中一个非常重要的细节。la gp, __global_pointer$这条指令本身就是为了设置gp。如果在编译这条指令时启用了链接器松弛链接器可能会自作聪明地认为gp已经设置好了并试图将这条指令“优化”成基于gp的短指令但这显然是不可能的因为此时gp还未设置会导致程序崩溃。因此在初始化gp的代码段前后必须显式地关闭松弛功能 。三步操作总结步骤操作工具/位置目的1用__attribute__((section(.sdata)))标记变量或使用-msmall-data-limit编译选项C 源码 / Makefile告诉编译器哪些变量需要被放到小数据区。2在链接脚本中定义.sdata段并在其中用__global_pointer$ . 0x800;设置锚点。.ld链接脚本告诉链接器在内存中为小数据分配空间并确定gp寄存器的目标值。3在启动汇编代码中用.option norelax临时禁用松弛然后用la gp, __global_pointer$加载值。start.S启动文件在程序运行的第一个瞬间将gp寄存器硬件初始化为正确的值。完成这三步后链接器在最后的松弛阶段就会自动将对.sdata段中变量的访问从原本的绝对地址访问可能需要多条指令优化为单条的gp相对寻址指令 。你的全局变量访问效率就得到了提升。问如果启动文件无法修改能否在main()函数开头再设置gp寄存器并开启链接器松弛不能。在main()函数的第一条语句设置gp寄存器为时已晚。这会导致你的程序在使用任何受gp优化的全局变量时立即崩溃。下面是详细的技术解释和可行的解决方案。1. 为什么不能在main()中设置gp根本原因在于链接器在链接时就已经假设gp的值存在并基于这个假设对指令进行了优化。如果在main()中才设置gp那么在main()函数执行之前所有已经被优化的代码都将使用一个尚未初始化的gp值导致程序崩溃。具体来说这个“之前”包括两个关键阶段C运行时初始化C Runtime Initialization在main()执行之前编译器会自动插入一段启动代码crt0或类似名称负责复制已初始化数据.data段从Flash到RAM。清零未初始化数据.bss段。设置堆指针等。如果这些初始化代码本身被链接器优化为使用gp访问全局变量比如用于定位复制源的地址而此时gp还是垃圾值那么复制和清零操作就会从完全错误的内存地址读取或写入导致数据损坏和不可预测的行为。全局对象的构造函数C如果你的工程包含C代码全局对象静态存储期的对象的构造函数也是在main()之前执行的。这些构造函数内部如果访问了任何受gp优化的成员变量同样会因为gp未初始化而崩溃。2. 既然不能改启动代码有什么解决方案你的情况启动代码被封装在静态库中无法修改在嵌入式开发中很常见。这里有几种可行的解决方案你可以根据工程的具体约束来选择方案一修改链接脚本将入口点指向你自己的包装函数推荐这是最正统、侵入性最小的解决方案。其核心思想是不修改厂家的启动代码而是通过链接脚本将程序的“入口点”从厂家的_start改为你自己写的一个小汇编函数在这个函数里设置gp然后再跳转到厂家的_start。这个方案有明确的先例可循。在RISC-V的生态中klibc项目就采用了完全相同的策略来解决类似问题1.编写一个汇编文件my_entry.S.section .text.init .globl _my_entry _my_entry: .option push .option norelax la gp, __global_pointer$ # 在最早的时刻设置 gp .option pop j _start # 跳转到厂家的原始启动代码2.修改链接脚本.ld文件将入口点改为_my_entryENTRY(_my_entry) /* 原来可能是 ENTRY(_start) */ SECTIONS { /* ... 其他段定义 ... */ .text.init : { *(.text.init) /* 确保 my_entry.o 的这部分代码被放在最前面 */ } /* ... */ }这样CPU 上电后第一条指令就会执行你设置的gp然后才进入厂家的启动代码完美绕过了无法修改启动代码的限制。3.上述实操代码为什么不需要手动开启链接器松弛.option push和.option pop的作用在 GNU 汇编器中.option push和.option pop是一对用于保存和恢复选项状态的伪指令.option push将当前的所有汇编选项设置包括松弛使能状态压入一个内部栈中保存起来。.option norelax临时修改当前选项关闭链接器松弛。.option pop从栈中恢复之前保存的选项状态。这就会自动恢复到执行.option push之前的状态。也就是说如果进入这段代码前松弛是默认开启的确实是默认开启的那么.option pop就会把松弛重新打开不需要显式地写一个.option relax。完整的执行流程.option push // (1) 保存当前状态松弛是开启的 .option norelax // (2) 临时关闭松弛 la gp, __global_pointer$ // (3) 安全地加载gp不会被错误优化 .option pop // (4) 恢复之前保存的状态松弛重新开启为什么这样设计这种“保存-修改-恢复”的模式是汇编编程中的标准实践有几点好处防御性编程你不需要假设进入这段代码前松弛是什么状态无论之前是开是关退出时都能正确恢复。避免遗漏如果用.option relax手动打开万一将来某天这段代码被复制到另一个默认关闭松弛的环境中就会出问题。用push/pop则没有这个顾虑。成对清晰push和pop成对出现代码意图非常明确——“这段区域内我要临时修改选项之后恢复原状”。4 链接器松弛所带来的优化效率优化前未使用链接器松弛指令数量2 条取一个全局变量假设地址是 0x20000100编译器需要先生成这个 32 位地址然后再访存。典型的指令序列是lui a5, 0x20000 # (1) 将高 20 位加载到寄存器a5 0x20000000 lw a5, 256(a5) # (2) 加上低 12 位偏移访存时钟周期数至少 2 个周期通常更多这个数字需要分两种情况讨论情况周期数估算说明理想情况指令和数据 cache 均命中2 周期lui1 周期 lw1 周期。lw如果 cache 命中可以在 1 周期内完成。实际情况cache 未命中几十到上百周期lw指令如果数据在 cache 中未命中需要从主存加载这个延迟通常在10-50 周期甚至更多取决于内存速度。此外lui和lw之间可能有结构冒险或数据依赖流水线可能 stall。因此优化前的最小代价是 2 条指令 2 个理想周期但实际往往更高。优化后启用链接器松弛且变量在 gp 范围内指令数量1 条如果该全局变量的地址在gp的 ±2KB 范围内链接器会将原来的两条指令替换为一条基于gp的单条访存指令lw a5, -1792(gp) # 一条指令直接从 gp 偏移量 取数时钟周期数1 个周期理想情况情况周期数估算说明理想情况数据 cache 命中1 周期单条lw指令如果数据在 cache 中1 周期完成。实际情况cache 未命中与优化前相同的内存延迟如果数据在 cache 中未命中仍需等待主存此时延迟与优化前一致。但优化后减少了一条指令的取指和执行所以整体仍更快。总结对比场景指令条数时钟周期理想情况cache 命中时钟周期cache 未命中未优化2 条2 周期内存延迟 至少 1 周期优化后gp 范围内1 条1 周期内存延迟核心收益指令数量减半从 2 条 → 1 条代码密度提升。执行时间减半理想情况从 2 周期 → 1 周期。减少取指带宽压力少取一条指令对流水线和 cache 更友好。降低功耗少执行一条指令意味着更少的动态功耗。需要强调的是这些数字是典型 1 发多级流水线处理器的估算值。具体的周期数会因处理器设计是否支持指令双发射、是否有 load 延迟槽等而略有差异但优化前后指令数量的减少是确定的性能收益是显著的。

相关文章:

RISC-V架构:gp寄存器与链接器松弛

目录 0 相关内容 1 gp(global pointer)全局指针寄存器 1. gp 寄存器的核心作用:高效访问全局数据 2. 为什么 Cortex-M 没有 gp? 3. gp 寄存器在 FreeRTOS 中的作用 2 链接器松弛 3 如何将全局小变量连接到 .sdata 段并设置 …...

2026年局域网考试系统选型对比:优考试助力政企信创与内网安全

在数字政府与信创产业全面推进的当下,政企、事业单位及涉密单位的考试场景,正面临国产化适配、数据安全、灵活部署三重核心要求。既要满足内网环境下的数据安全与物理隔离,又要兼顾部分场景下外网访问的灵活性,传统单一架构考试系…...

【Midjourney拟物化风格实战指南】:20年视觉设计专家亲授3大材质渲染公式与5步出图工作流

更多请点击: https://kaifayun.com 第一章:拟物化风格的本质与Midjourney语义解码 拟物化(Skeuomorphism)并非简单的视觉仿拟,而是一种通过材质、光影、物理反馈等多维语义锚点唤起用户认知惯性的交互范式。在AI图像生…...

设计师私藏的11个纹理Prompt原子模块(仅限本周开放下载:含PBR贴图映射表+光照反射系数速查卡)

更多请点击: https://intelliparadigm.com 第一章:纹理Prompt原子模块的设计哲学与底层逻辑 纹理Prompt原子模块并非简单拼接关键词的字符串生成器,而是以认知建模为根基、以可组合性为约束、以语义保真度为校验目标的结构化表达系统。其设计…...

linuxcnc开发环境搭建

linux cnc ,数控机床开源控制软件,实时系统。下载linuxcnc.iso镜像,在虚拟机里安装。安装成功运行起来。安装了amd64版本的qtcreator运行提示少libxcb:sudo apt update sudo apt install libxcb-cursor0打开窗口成功新建 一个工程…...

Win11 右键菜单缺少“新建文本文档“win11 某些软件中文乱码

Win11 右键菜单缺少“新建文本文档“Win11 右键菜单缺少"新建文本文档"是‌常见系统配置问题‌,主要通过注册表修复或记事本应用重装即可解决。‌‌核心解决方法(win11 亲测可行)‌注册表修复(最常用)‌按Wi…...

达梦数据库-统计信息收集-记录

达梦数据库-统计信息收集-记录总结 1统计信息收集 统计信息主要是描述数据库中表和索引的大小及数据分布状况等信息。比如:表的行数、块数、平均每行的大小、索引的高度、叶子节点数以及索引字段的行数等。统计信息对于CBO(基于代价的优化器&#xff0…...

原来赛事专用匹克球工厂还有这么多门道?你了解吗?

引言在匹克球运动蓬勃发展的当下,赛事专用匹克球的品质至关重要。而赛事专用匹克球工厂背后,其实隐藏着诸多门道。泉州凯瑞麟体育用品有限公司作为行业内的佼佼者,在这方面有着独特的技术与经验。核心材料与技术创新赛事专用匹克球对核心材料…...

终极指南:3分钟让Switch手柄变身PC游戏神器

终极指南:3分钟让Switch手柄变身PC游戏神器 【免费下载链接】BetterJoy Allows the Nintendo Switch Pro Controller, Joycons and SNES controller to be used with CEMU, Citra, Dolphin, Yuzu and as generic XInput 项目地址: https://gitcode.com/gh_mirrors…...

从零实现一个高性能 FTP 服务器(C++ / Linux)

目录一、搭建 TCP 服务器骨架服务器代码测试二、支持多客户端并发三、线程模型核心思路为什么使用 detach输出为什么会错乱四、函数重构重构后的结构五、FTP 协议基础控制连接数据连接六、命令解析行缓冲区命令解析为什么要转大写七、PASV 被动模式为什么需要数据连接&#xff…...

关注模块 API

关注用户 POST /api/v1/relations/followHeaders:Authorization: Bearer {token}Request: {"user_id": "target_user_id" }Response: {"code": 0,"data": {"relation_type": "following"} }接口语义设计 POST /…...

仪式感,从来与你无关

2.2万人点赞的扎心评论:仪式感,从来都与你无关 有2.2万个男生偷偷点了赞。 没有歇斯底里的控诉,没有长篇大论的抱怨,只有一句轻飘飘的陈述,和一句"兄弟,没绷住"。 但就是这两句话,像一根针,精准地扎破了无数男生藏在心里最深处的、不敢说出口的委屈。 01…...

LangChain 是什么?从零开始学会 LangChain 的工程实践指南

LangChain 是什么?从零开始学会 LangChain 的工程实践指南 1. 文章背景:为什么这个主题重要 在大模型应用开发中,很多人第一次接触 LangChain,是因为想快速做一个“基于大模型的应用”:例如知识库问答、RAG 检索增强生…...

Python EXE逆向工程完全指南:使用python-exe-unpacker快速反编译打包程序

Python EXE逆向工程完全指南:使用python-exe-unpacker快速反编译打包程序 【免费下载链接】python-exe-unpacker A helper script for unpacking and decompiling EXEs compiled from python code. 项目地址: https://gitcode.com/gh_mirrors/py/python-exe-unpa…...

Pure Live:3大平台聚合,打造你的专属纯净直播空间

Pure Live:3大平台聚合,打造你的专属纯净直播空间 【免费下载链接】pure_live A Flutter project can make you watch live with ease. 项目地址: https://gitcode.com/gh_mirrors/pu/pure_live 你是否厌倦了在多个直播应用间来回切换&#xff1f…...

【RK3588-AI-004】RK3588 AI专属依赖环境预装(Python、OpenCV、基础编译工具)

📖 专栏介绍 本专栏为RK3588 端侧AI开发零基础实战教程,专为嵌入式AI入门、模型部署、视觉开发学习者打造。全程实操、无废话、避坑优化,从零搭建RK3588专属AI开发环境,手把手教学,新手也能轻松上手。 ✅ 硬件适配&am…...

深入拆解 MySQL InnoDB 隔离级别:从 MVCC 到临键锁

前言 关于 MySQL InnoDB 的事务隔离级别,90% 的开发者都存在至少一个致命误区: 误区1:RR(可重复读) 临键锁 彻底解决了幻读误区2:Serializable 只是比 RR 加的锁更多,本质还是用 MVCC误区3&a…...

2026.5.12【芯片设计面试经验分享】上海车载芯片设计公司

一、主管面试 1、介绍下负责的cpu的九级流水线都有哪级? 指令预取、PC取指、指令译码、发射(双发射)、执行1(alu、运算)、执行2(乘法、移位)、访存、写回、提交/重排 2、负责的spyglass cdc 一般…...

编译和链接+预处理

编译(compile)和链接(link)在以前我们提到过,C语言是一门编译型的计算机语言,C语言的源代码都是文本文件,文本文件本身无法运行,电脑不能执行C语言代码,计算机能够执行的…...

数分-MySQL基础01

数分-MySQL基础01基础概念MySQL数据库对象MySQL的架构MySQL客户端和服务器端连接方式命令行连接方式图形化客户端连接SQL语言分类通用语法(所有数据库)DDL语句数据库DDL数据表DDL表字段DDL数据类型字段约束基础概念 数据库(Database, DB&…...

Spring AI Alibaba 1.x 系列【55】Interrupts 中断机制:静态中断源码分析

文章目录 1. interruptBefore 模式1.1 中断判断逻辑1.2 构建中断元数据1.3 返回中断响应1.4 初始化【中断执行】上下文1.5 合并状态(BUG)1.6 执行结束 2. interruptsAfter 模式2.1 设置 INTERRUPT_AFTER 标记2.2 动态计算下一个节点 3. 中断时机对比 1. …...

【Linux驱动开发】第11天:设备树(Device Tree)超详细全解:从诞生背景到工作原理

一、设备树的诞生背景:传统驱动的致命痛点 在设备树出现之前(Linux 3.0之前),Linux内核采用硬编码的方式描述所有硬件信息。这意味着: 每一个开发板的寄存器地址、中断号、GPIO号,都直接写死在驱动代码里换…...

【Linux驱动开发】第10天:设备树零基础入门——DTS/DTB/DTC全解+编译流程

目录 为什么需要设备树?传统驱动的终极痛点DTS/DTB/DTC 大白话定义核心区别三者关系完整编译流程图最简单的DTS示例语法解析设备树编译与反编译实操命令内核如何加载和使用设备树核心总结面试必背考点 1. 为什么需要设备树?传统驱动的终极痛点 在设备树…...

TowerPersonalProperty.cs

TowerPersonalProperty 是塔的标准化攻击组件,攻击节奏(CD管理)子弹发射(从对象池获取并配置)视觉表现(旋转、动画、音效、特效)经济交互(升级/出售价格计算与金币变更)它…...

[工具] 数学题库生成器(小学,初中,高中全包括) 面向中小学数学教学的自动出题工具,覆盖从小学一年级到高中三年级共 7 个学段、33 种题型

数学题库生成器(小学,初中,高中全包括) 基本覆盖各个年级的重点题型生成,并导出为word,可以显示解题步骤。# 数学题库生成器 MathMaster 数学题库生成器(MathMaster)是一款面向中小学…...

硬件工程师,每天5分钟(5)——为什么 DDR5 最怕地不好?回流路径,才是高速设计真正的灵魂

讲透: 回流路径 为什么 Split Plane 最危险 为什么加地孔有时候能救命 为什么 GPS 会被 DDR 干扰 为什么 EMC 挂的根因常是地 🚗《硬件工程师,每天5分钟》第5篇 🔥《为什么 DDR5 最怕地不好?回流路径,才是高…...

11.三层网络VXLAN

先把之前基于flat模式创建的虚机,全部删除 控制节点配置:1.修改配置文件/etc/neutron/neutron.conf 将[DEFAULT]区域 core_plugin ml2 service_plugins 修改为 core_plugin ml2 service_plugins router allow_overlapping_ips True2.修改/etc/neutro…...

数采网关的应用与特点

摘要在工业自动化、智能制造和物联网(IoT)快速发展的背景下,数据采集网关(数采网关)作为连接现场设备与上层管理系统的关键枢纽,发挥着至关重要的作用。它能够实现工业设备数据的实时采集、协议转换、边缘计…...

第2章:文档加载与智能分块——RAG的第一步

本章你将收获:支持PDF(含表格)、Word、Markdown、网页、CSV等10+格式的完整加载代码;五种分块策略的深度对比(固定大小、递归字符、语义、文档结构、按标题);元数据保留与增强的工程方法;处理100页混合格式技术手册的完整实战;以及分块参数调优的最佳实践。 📌 本章…...

西门子PLC对接须知:从通信到编程的实战指南

在工业自动化领域,西门子S7系列PLC凭借强大的功能和广泛的兼容性,成为众多企业的首选。无论是设备集成、数据采集还是系统升级,掌握PLC对接的核心要点,是保障项目高效落地的关键。本文将从通信连接、编程架构、数据处理三个维度&a…...