x86-64内核启动:Multiboot协议与QEMU加载机制详解

简介:本资源是中科大操作系统课程实验一的完整实现包,面向刚接触操作系统开发的初学者,聚焦Multiboot协议启动机制的实践入门。内容涵盖Multiboot Header编写、VGA显存直写“helloworld”、串口输出“HELLOWORLD”等核心功能,并配套Ubuntu环境下QEMU模拟运行、共享文件夹配置及Makefile与链接脚本(.ld)的实操说明,帮助学习者打通从汇编编码、链接到镜像生成的完整启动链路。压缩包共10个文件,含汇编源码(.S)、链接描述文件(.ld)、可执行二进制(.bin)、实验报告PDF、构建脚本(Makefile)及VSCode配置等,类型丰富且结构紧凑,整体仅778KB,轻量易解压复现。目前已有293人学习下载,适合OS新手理解实模式启动流程、掌握基础x86汇编与系统级构建工具链的入门实践。

1. USTC OS CLX Lab1:用 QEMU + Multiboot 启动一个“能喘气”的内核,不是 Hello World 那种

刚接触操作系统开发的人,常以为第一个目标是打印 “Hello, OS!”——但真正卡住你的,从来不是 printf,而是让 CPU 在保护模式下稳稳接住控制权、让 GDT 加载不报错、让 Multiboot 头被 QEMU 正确识别、让内核入口地址对齐到 4KB 边界……USTC OS CLX Lab1 就是这样一个“反直觉起点”:它不教你写调度器,却逼你手写汇编段描述符、手动构造 Multiboot 信息结构体、在链接脚本里硬控节布局。这不是玩具实验,它是真实 x86-64 启动链的最小可运行切片——跑通它,你才算摸到了 OS 内核加载流程的物理边界;卡在这里,说明你还没真正理解「谁在初始化谁」「谁信任谁」「谁校验谁」。适合正在啃《Orange'S》《MIT 6.828》或某高校操作系统课程实验的初学者,也适合想补全启动环节知识断层的嵌入式/Linux 驱动开发者。别急着写进程管理,先让 CPU 别在第一条指令就 triple-fault。


2. 启动流程拆解:为什么非得用 Multiboot?QEMU 又凭什么信你?

Multiboot 不是协议,是契约。它定义了 bootloader(比如 GRUB)和 kernel 之间必须遵守的二进制接口规范——不是靠约定俗成,而是靠固定内存布局、固定 magic number、固定对齐要求来强制对齐行为。QEMU 本身不解析 Multiboot,但它通过-kernel参数启动时,会模拟 GRUB 的行为:把内核镜像当做一个符合 Multiboot 规范的 ELF 文件加载,并在 0x100000(1MB)处预留一块内存,把 Multiboot info structure 放进去。如果你的内核没放对 magic、没对齐、没声明支持的 Multiboot 版本,QEMU 就直接报multiboot: invalid header然后静默退出——连错误码都不给你看。这不是 bug,是设计:它只认标准,不认解释。

所以 Lab1 的核心,不是“怎么写 C”,而是“怎么让汇编+链接脚本+ELF 属性三者咬合”。你写的_start是入口,但_start能不能被执行,取决于前面 8KB 内有没有一个被 QEMU 扫描到的、magic 值为0x1BADB002的 Multiboot header;而这个 header 能不能被识别,又取决于它是否落在.text段开头、是否 4 字节对齐、是否后面紧跟着 4 字节 flags(必须含0x00000001表示“有地址标签”)。漏掉任意一环,QEMU 就当它不存在。

2.1 Multiboot Header 的硬编码位置与字段语义

Multiboot header 必须位于内核镜像的前 8192 字节内,且起始地址必须是 4 字节对齐(通常放在.text段最开头)。标准 header 结构如下(按顺序):

偏移字段名类型值(Lab1 常用)说明
0magicu320x1BADB002唯一标识,QEMU 扫描时只认这个
4flagsu320x00000001表示“header 后紧跟地址标签”,必须置位
8checksumu32-(magic + flags)校验和,必须使四字之和为 0

注意:checksum不是 CRC,是简单负和。很多初学者手算错,导致 QEMU 直接跳过整个 header。

下面这段汇编就是 Lab1 最常见的 header 实现(放在boot.s开头):

.section .text .global _start _start: .align 4 .long 0x1BADB002 # magic .long 0x00000001 # flags: requires address tag .long -(0x1BADB002 + 0x00000001) # checksum = -(magic + flags)

提示:.align 4是铁律。如果前面有.code32或其他伪指令没对齐,header 就会错位,QEMU 扫不到。建议把 header 单独提成一个.multiboot段并显式对齐,后续调试更可控。

2.2 QEMU 启动参数背后的内存映射逻辑

QEMU 的-kernel参数不是简单加载文件,而是执行一套模拟 GRUB 的加载流程:

  • 把内核 ELF 解析,提取所有PT_LOAD段;
  • 按p_vaddr(虚拟地址)将各段加载到对应物理地址(如.text→0x100000);
  • 在0x100000附近分配一页(4KB)内存,构建multiboot_info_t结构体;
  • 把该结构体地址作为第二个参数(%rsi),调用_start(第一个参数%rdi是 magic,固定为0x2BADB002)。

这意味着:你写的_start函数签名必须是:

void _start(unsigned long magic, unsigned long multiboot_ptr);

而不是void main()。如果你在 C 文件里写了main()并指望它被调用,那永远等不到——因为_start是唯一由 QEMU/GRUB 调用的入口,main是你自己的函数,必须由_start显式跳转过去。

常见错误是忽略第二个参数multiboot_ptr。它指向一块关键内存,里面存着内存大小、模块列表、命令行字符串等。Lab1 虽然初期不用读它,但如果你后续要获取可用内存范围(比如为页表分配空间),这就是唯一合法来源。

2.3 链接脚本:控制段布局才是真正的“启动开关”

GCC 默认链接脚本会把.text放在0x400000,但 Multiboot 要求内核从0x100000开始加载。所以你必须写自定义链接脚本linker.ld,强制指定入口和段地址:

ENTRY(_start) SECTIONS { . = 0x100000; .text ALIGN(0x1000) : { *(.multiboot) *(.text) } .data ALIGN(0x1000) : { *(.data) *(.rodata) } .bss ALIGN(0x1000) : { *(.bss) . = ALIGN(0x1000); __bss_end = .; } /DISCARD/ : { *(.comment) *(.note.*) } }

关键点:

  • ENTRY(_start):告诉链接器_start是程序入口,不是main;
  • . = 0x100000:设置链接基址,所有段从此开始布局;
  • *(.multiboot)必须放在.text最开头:确保 Multiboot header 在镜像偏移 0 处;
  • ALIGN(0x1000):保证每个段都按 4KB 对齐,这是 Multiboot 和后续分页初始化的前提。

如果你没写链接脚本,或者忘了加ENTRY,GCC 会用默认脚本,.text被放到0x400000,QEMU 加载后发现0x100000处没有 magic,直接失败。


3. 编译与调试闭环:从 asm → elf → qemu → gdb,每一步都可验证

光写对代码不够,得让每一步输出都可观察、可打断、可回溯。Lab1 最容易陷入“改了十次,不知道哪步挂了”的状态。我一般把构建流程拆成四阶验证:汇编检查 → ELF 结构检查 → QEMU 启动日志 → GDB 单步跟踪。漏掉任何一阶,都是在赌运气。

3.1 汇编阶段:用 objdump 确认 header 真实存在且对齐

编译生成.o后,立刻用objdump -d boot.o查看反汇编,确认_start地址是0x0,且前三条指令就是三个.long:

$ objdump -d boot.o boot.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <_start>: 0: 02 bb b0 02 1b ad add %bl,-0x52e4fd50(%rbx) 6: 01 00 add %eax,(%rax) 8: 00 00 add %al,(%rax)

这看起来像乱码?别慌——这是把.long当指令反汇编了。重点看地址0x0,说明 header 确实在段首。再用readelf -x .text boot.o查看原始字节:

$ readelf -x .text boot.o Hex dump of section '.text': 0x00000000 02b0ad1b 00000001 00000000 00000000 ................

注意字节序:x86 是小端,0x1BADB002存储为02 b0 ad 1b,和 dump 一致。如果这里看到00 00 00 00,说明.align 4没生效或 header 被挤到后面了。

3.2 ELF 阶段:用 readelf 验证 program header 与入口地址

链接成kernel.bin后,必须检查 ELF 是否满足 Multiboot 加载要求:

$ readelf -h kernel.bin # 看 ELF 类型和入口 $ readelf -l kernel.bin # 看 program headers,重点关注 p_vaddr 和 p_paddr

正确输出应包含:

  • Entry point address: 0x100000(由ENTRY(_start)和链接脚本共同决定);
  • 一个LOADsegment,p_vaddr == p_paddr == 0x100000,p_filesz > 0;
  • p_flags包含R E(可读可执行)。

如果Entry point是0x400000或0x0,说明链接脚本没生效;如果p_vaddr是0x0,说明段没重定位,QEMU 加载后会把代码塞到低地址,触发保护异常。

3.3 QEMU 阶段:启用详细日志,捕获启动瞬间的判决依据

不要直接qemu-system-x86_64 -kernel kernel.bin。加两个关键参数:

qemu-system-x86_64 \ -kernel kernel.bin \ -nographic \ -d int,mmu,cpu_reset \ -D qemu.log
  • -d int,mmu,cpu_reset:打开中断、MMU、CPU 复位日志;
  • -D qemu.log:把所有调试输出写入文件,避免刷屏丢失。

启动后立刻查qemu.log,搜索multiboot:

multiboot: loading kernel at 0x00100000 len=0x00001234 multiboot: loaded 1 modules multiboot: info struct at 0x00100000 + 0x00001234 = 0x00101234

如果看到multiboot: invalid header或压根没multiboot:日志,说明 header 未被识别。此时回去检查readelf -x .text kernel.bin,看0x0处是不是 magic。

注意:-nographic是必须的。否则 QEMU 会开图形窗口,而你的内核还没初始化 VGA,屏幕黑屏无法判断是卡住还是真跑飞了。

3.4 GDB 阶段:在第一条指令设断点,确认 CPU 真的跳进来了

QEMU 支持 GDB stub,这是 Lab1 调试的生命线:

qemu-system-x86_64 \ -kernel kernel.bin \ -S -s \ # -S 暂停启动,-s 监听 localhost:1234 -nographic

另开终端:

gdb kernel.bin (gdb) target remote :1234 (gdb) break *_start (gdb) continue

如果 GDB 停在_start,说明 Multiboot 成功,CPU 控制权已移交;如果continue后卡住或报Remote 'g' packet reply is too long,大概率是_start里执行了非法指令(比如在 32 位模式下用了 64 位寄存器,或没关中断就访问 PIC)。

我习惯在_start第一条指令后加一句:

cli # 关中断,避免 IRQ 打断初始化 cld # 清方向标志,防止 movsb 出错

这是血泪经验:某次因没cli,键盘中断在 GDT 加载中途触发,直接 triple-fault。


4. 避坑指南:五个让 USTC OS CLX Lab1 卡死三天的真实问题

这些不是理论假设,是我在带某高校操作系统实验时,学生提交的 137 份 Lab1 代码里高频出现的硬伤。每一条都对应一个qemu.log里的具体错误码或 GDB 里的寄存器快照。

4.1 现象:QEMU 启动无输出,qemu.log里没有multiboot:日志

原因:Multiboot header 不在 ELF 文件偏移 0 处,或.multiboot段被链接器优化掉了。常见于:

  • 汇编文件里 header 前有.code32但没.align 4,导致 header 起始地址为0x2;
  • 链接脚本里没写*(.multiboot),或写了但.multiboot段名拼错(如.multiboot_header);
  • 使用gcc -c boot.s编译时加了-m64,但 header 是 32 位 magic,QEMU 只扫描 32 位 header。
    解决:readelf -x .text kernel.bin | head -n 5,确认0x0处是02 b0 ad 1b;若不是,检查.s文件开头是否有空指令或注释占位。

4.2 现象:GDB 连上后break *_start提示Function '_start' not defined

原因:链接器没把_start当作符号导出,或符号被 strip 掉。常见于:

  • 链接时用了-nostdlib -static但忘了加-Wl,--no-as-needed,导致链接器丢弃未引用的符号;
  • gcc命令里-T linker.ld写成了-Tlinker.ld(少空格),链接脚本失效,回退到默认脚本;
  • _start声明为.hidden或.local,GDB 看不到。
    解决:nm kernel.bin | grep _start,必须看到T _start(T 表示文本段全局符号);若为U _start(undefined),说明链接失败;若无输出,说明符号被删。

4.3 现象:QEMU 报ERROR: inconsistent multiboot information

原因:Multiboot info structure 地址非法,通常是multiboot_ptr参数被覆盖。常见于:

  • _start函数里用push %rbp; mov %rsp,%rbp建立栈帧,但没初始化%rsp,导致栈操作破坏multiboot_ptr;
  • 在_start里调用 C 函数前,没把multiboot_ptr保存到安全位置(如.data段变量),而 C 函数的局部变量覆盖了它。
    解决:在_start开头立即把第二个参数存起来:
_start: mov %rsi, multiboot_ptr # 假设 .data 里有 multiboot_ptr: quad 0 cli ... .data multiboot_ptr: .quad 0

4.4 现象:GDB 中stepi执行第一条指令后卡死,info registers显示rip=0xffffffffffffffff

原因:_start执行了非法跳转,如jmp *%rax时%rax是 0,或call了一个未定义函数。常见于:

  • C 代码里写了main();但没实现main,链接器用__libc_start_main替代,而该函数依赖 glibc,内核里不存在;
  • 汇编里call print_hello,但print_hello是 C 函数,没加extern声明,链接时解析为 0。
    解决:objdump -t kernel.bin | grep print_hello,确认符号类型是F(function)且值非 0;若为*UND*,说明未定义。

4.5 现象:QEMU 启动后黑屏,qemu.log里有cpu_reset: reset vector=0xfffffff0

原因:CPU 复位后跳转到0xfffffff0(实模式复位向量),说明 Multiboot 完全没生效,QEMU 回退到 BIOS 启动流程。根本原因是:

  • kernel.bin不是 ELF 文件,而是 raw binary(如用ld -o kernel.bin -Ttext 0x100000 boot.o生成);
  • QEMU 的-kernel参数只接受 ELF,对 raw binary 会静默忽略 Multiboot 流程。
    解决:file kernel.bin必须输出ELF 64-bit LSB executable, x86-64;若为data,说明链接时用了-o而非-o kernel.elf,且没指定-e _start。

5. 进阶验证:用 QEMU Monitor 和自检代码确认启动链完整性

跑通_start只是起点。真正的“启动成功”,是内核能主动向 QEMU 报告自身状态。Lab1 后续会涉及 GDT、IDT、内存探测,但在此之前,你可以用两个轻量级手段交叉验证:一是用 QEMU Monitor 查询 CPU 状态,二是写一段自检代码,把关键寄存器值输出到串口(COM1),用screen或minicom捕获。

5.1 QEMU Monitor:实时查看 CPU 模式与寄存器快照

启动 QEMU 时加-monitor stdio,启动后按Ctrl+AC进入 monitor:

qemu-system-x86_64 -kernel kernel.bin -nographic -monitor stdio # 启动后按 Ctrl+A, C (qemu) info registers ... RIP=0000000000100000 CR0=80010033 CR2=0000000000000000 CR3=0000000000000000 CR4=0000000000000668 ... (qemu) info cpus * CPU #0: pc=0x0000000000100000 (halted) thread_id=12345

重点看:

  • RIP=0x0000000000100000:确认 CPU 真的停在_start(即0x100000);
  • CR0=...33:末两位11表示 PE(Protection Enable)和 MP(Monitoring)已置位,说明已进入保护模式;
  • CR4=...668:bit 4(PSE)和bit 5(PAE)若为 1,说明支持大页和物理地址扩展,为后续分页打基础。

如果RIP是0xfffffff0或0x0,说明没进 Multiboot 流程;如果CR0末位是0,说明还在实模式,GDT 加载失败。

5.2 串口自检:把 multiboot_ptr 和 RIP 写入 COM1,绕过 VGA 依赖

不用初始化显卡,也能“看到”输出。x86 的 COM1(I/O port0x3f8)是内核调试的黄金通道。写一段极简串口初始化和发送函数:

# 在 boot.s 里添加 serial_init: movb $0x80, %al outb %al, $0x3f8 # 设置 DLAB=1 movb $0x01, %al outb %al, $0x3f8 # 波特率除数低字节 (115200) movb $0x00, %al outb %al, $0x3f9 # 波特率除数高字节 movb $0x03, %al outb %al, $0x3f8 # 8N1, DLAB=0 movb $0xc7, %al outb %al, $0x3f9 # 使能 FIFO ret serial_putc: push %rbp mov %rsp, %rbp mov %rdi, %rax # char in %rdi wait: inb $0x3fd, %al # 读线路状态寄存器 testb $0x20, %al # 检查 THRE 位 jz wait outb %al, $0x3f8 pop %rbp ret

然后在_start末尾调用:

call serial_init mov $'A', %rdi call serial_putc mov $'B', %rdi call serial_putc

启动前开串口监听:

# 终端1 qemu-system-x86_64 -kernel kernel.bin -nographic -monitor stdio # 终端2 stty -F /dev/ttyS0 115200 raw -echo cat /dev/ttyS0

如果看到AB,说明:

  • _start执行到了最后;
  • I/O 指令没被特权级拦截(说明 CPL=0);
  • outb指令被 QEMU 正确模拟。

这是比“屏幕亮了”更底层、更可靠的启动成功信号。

5.3 用 GDB 验证 multiboot_ptr 内容:确认 QEMU 真给了你信息

在 GDB 中停在_start后,直接读multiboot_ptr指向的内存:

(gdb) x/10xg $rsi 0x101234: 0x0000000000000000 0x0000000000000000 0x101244: 0x0000000000000000 0x0000000000000000 0x101254: 0x0000000000000000 0x0000000000000000 0x101264: 0x0000000000000000 0x0000000000000000 0x101274: 0x0000000000000000 0x0000000000000000

全 0?说明 QEMU 没写入信息——但更可能是你没等 QEMU 完成初始化就执行了x命令。正确做法是:

(gdb) stepi # 执行一条,让 QEMU 把 info struct 写好 (gdb) x/10xg $rsi

正常应看到:

  • 0x0处是 magic0x2BADB002;
  • 0x8处是内存下限(如0x00000000);
  • 0x10处是内存上限(如0x80000000,即 2GB)。

这才是 Multiboot 启动链完整闭合的证据:QEMU 读取了你的 header,加载了你的代码,又把系统信息回填给你。不是单向加载,而是双向契约。

从那以后我每次写 OS Lab,都会在_start开头加三行:

cli cld mov %rsi, multiboot_ptr

然后立刻用 GDBx/4xg multiboot_ptr确认——这三行是我给自己留的后悔药,也是启动链上最短的验证路径。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

千问实战:从Function Calling到Agent,AI如何真正“干活”?

千问实战:从Function Calling到Agent,AI如何真正“干活”?

前两年我们夸一个大模型&#xff0c;说它“真能聊”&#xff0c;基本就是最高评价了。现在风向变了。最近一阵子&#xff0c;我观察到的现象是&#xff0c;包括千问在内的主流大模型&#xff0c;越来越强调“干活”而不是“对话”——它们开始调用工具、操作文件、跑流程&#…

2026/10/20 4:44:48 阅读更多 →
Vim制表符设置与命令模式高效编辑实战指南

Vim制表符设置与命令模式高效编辑实战指南

1. 为什么一个制表符设置能决定你写代码的效率和协作体验Vim制表符设置及命令模式下常用命令——这看起来像一句教科书目录&#xff0c;但对每天用Vim写代码、改配置、审日志的开发者来说&#xff0c;它其实是真实工作流里最常被低估的“隐形瓶颈”。我带过三届某高校Linux系统…

2026/10/20 4:44:48 阅读更多 →
知识工作流插件组合:从捕获到输出的效率提升实战指南

知识工作流插件组合:从捕获到输出的效率提升实战指南

做知识工作的人&#xff0c;电脑里一定有一堆工具&#xff1a;浏览器、编辑器、笔记软件、文档、表格&#xff0c;可能还有各种在线平台。工具多不是问题&#xff0c;真正的问题是它们彼此不连通。我查网页资料要复制粘贴到文档里&#xff0c;格式乱了重新排&#xff1b;写代码…

2026/10/20 4:44:48 阅读更多 →

最新新闻

智能支持员实战:从意图识别到多轮诊断的AI排障系统搭建

智能支持员实战:从意图识别到多轮诊断的AI排障系统搭建

做了这么多年技术支持&#xff0c;我对一件事特别有感触&#xff1a;真正占时间的不是那些疑难杂症&#xff0c;而是每天反复出现的“老三样”——服务起不来、配置不生效、报错信息看不懂。团队决定做“智能支持员”这个项目的时候&#xff0c;我的第一反应不是“又要上机器人…

2026/10/20 5:20:04 阅读更多 →
大模型推理性能优化:Prefill与Decode的瓶颈拆解与KV Cache调优

大模型推理性能优化:Prefill与Decode的瓶颈拆解与KV Cache调优

1. 把 Prefill 和 Decode 当成两个物种来看&#xff0c;是这套笔记第一个想讲明白的事聊到高级调优&#xff0c;核心其实就两个字&#xff1a;边界。Prefill 阶段和 Decode 阶段在计算形态、访存特性、延迟目标上完全是两个物种&#xff0c;却挤在同一套显存和同一个调度器里&a…

2026/10/20 5:20:04 阅读更多 →
从20%转化异常到1,833.56分方案:Coomia DIP决策闭环全解析

从20%转化异常到1,833.56分方案:Coomia DIP决策闭环全解析

1. 一次转化异常背后的完整决策链条1.1 从数据异常到决策闭环的起点转化率从预期的基准线掉到20%附近&#xff0c;这个信号在任何业务场景里都足够刺眼。我见过不少团队遇到这种情况的第一反应是“赶紧改页面”“赶紧换素材”&#xff0c;但真正有效的做法是先停下来&#xff0…

2026/10/20 5:20:04 阅读更多 →
AI编程模型迭代加速:从Argon到Carbon,什么是真正的代码生成“手感”?

AI编程模型迭代加速:从Argon到Carbon,什么是真正的代码生成“手感”?

最近圈子里有个消息挺有意思&#xff1a;某头部科技公司还没把内部代号Argon的编程模型推到全员使用&#xff0c;下一代Carbon就已经开始小范围内测了。参与内测的人给出的评价很耐人寻味——他们说Carbon的编程“手感”已经接近内部基准模型Opus 5.5。这话听起来像一句随口的评…

2026/10/20 5:20:04 阅读更多 →
ResidualQuant:面向循环Transformer KV Cache的2-bit残差量化方案

ResidualQuant:面向循环Transformer KV Cache的2-bit残差量化方案

做模型推理优化这几年&#xff0c;我最大的一个体感是&#xff1a;真正卡住长上下文生成的&#xff0c;往往不是算力&#xff0c;而是内存带宽和缓存容量。模型权重可以离线反复压缩&#xff0c;已经卷到 4-bit、甚至更低&#xff1b;可 KV Cache 是运行时动态增长的数据&#…

2026/10/20 5:20:04 阅读更多 →
FunctionCall函数调用全解析:从原理到实战,让大模型真正“动手干活”

FunctionCall函数调用全解析:从原理到实战,让大模型真正“动手干活”

如果你正在做AI对话类产品&#xff0c;不管是客服助手、内容助手还是企业内部的知识问答系统&#xff0c;大概率会遇到这样一个让人头疼的瞬间&#xff1a;用户问“帮我查一下昨天订单的物流状态”&#xff0c;模型回了一句“很抱歉&#xff0c;我无法访问你的订单系统”。不是…

2026/10/20 5:19:03 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →