Linux 0.11终端设备控制实验:从内核态裁剪到用户态重写

先说明一下,这个实验在我当年做的时候,就和“哈工大的操作系统实验”深度绑定,核心是基于Linux 0.11内核源码去改终端设备的读写流程。实验七的标题叫“终端设备控制”,但我更愿意把它理解成“把终端这层原来由内核干的活,搬到用户态自己干一遍”。这活儿放到现在的Linux上想都不要想,但放到0.11这个教学内核上,反而能把底层逻辑看得很透。

这篇文章我会完全按我当时的实操路径来讲,从实验设计思路到具体改哪些函数、怎么调试、踩过哪些坑,全部交代清楚。如果你正好在做这个实验,或者对Linux 0.11的终端子系统感兴趣,可以照着这个思路去复现一遍。

1. 实验到底要干什么:把终端控制权从内核手里“抢”回来

1.1 教学内核里的终端设备控制是什么状态

Linux 0.11内核的终端子系统,大概是整个内核里“最拧巴”的一块代码。它既要管键盘中断,又要管屏幕输出,还要维护一大堆缓冲区、控制字符状态、行规则(line discipline)逻辑。说白了,你在终端里敲下的每一个字符,从键盘中断触发,到最终显示在屏幕上,中间要经过一条非常长的处理链:

  • 键盘中断处理程序把扫描码转成ASCII码,放入键盘缓冲区
  • 内核的tty层从缓冲区读取字符,做回显、行缓冲、特殊字符(退格、Ctrl+C、Ctrl+D等)处理
  • 处理完的字符再通过tty_write写到显示缓冲区,由显卡驱动刷新到屏幕

在Linux 0.11里,这套逻辑由kernel/chr_drv/tty_io.c、keyboard.S、console.c这几个文件共同完成。tty_read和tty_write这两个函数,就是用户程序通过read和write系统调用访问终端时最终会到达的地方。

实验七的主题,简单讲就是:把上层这些控制逻辑从内核态剥离开,放到用户态重新实现一遍。内核只保留最基本的硬件读写接口,剩下的字符处理、行编辑、回显、信号生成,全部由用户程序自己接管。

1.2 这个实验适合谁、解决什么问题

如果你正在跟着某高校的操作系统课程做Linux 0.11系列实验,实验七大概率是你第一次从“改内核”切换到“写用户态程序”的转折点。前面几个实验可能都在内核里加系统调用、改进程调度,到了实验七,反而要求你“少动内核”,把精力放在用户态的终端控制逻辑上。

这其实非常考验你对内核和用户态边界的理解。很多人做完前面几个实验,对“内核态能干什么”“用户态不能干什么”还停留在背概念层面。实验七会逼着你搞清楚一件事:用户程序到底是怎么通过系统调用拿到终端数据的?中间的缓冲区是谁维护的?控制字符是在哪一层被拦截的?

所以这篇文章不仅写代码,也会把整条链路画出来,帮你看清楚每个环节都在干嘛。

1.3 我的整体方案选型

当时我查了不少资料,也问了一些师兄,发现做这个实验大致有三条路:

  • 方案一:完全重写tty_io.c,把内核里的行规则全部剪掉,只保留一个极简的字符队列。
  • 方案二:在内核里新增一个系统调用,专门返回原始键盘字节,然后在用户态写一个终端模拟器。
  • 方案三:不改内核,直接把用户态shell程序扩展成“自己处理终端输入输出”。

我最后用的是方案一的变体:先在内核侧裁剪tty_read,把对控制字符、行缓冲的处理全部停用,让它退化成纯粹从底层缓冲区搬数据;然后在用户态新增一个终端控制程序,负责从stdin读取原始字符,做解析、回显、信号触发,再把最终结果显示到屏幕。

选这条路的原因也很朴素:

  1. Linux 0.11的tty_io.c本身就不算长,裁剪逻辑改动量可控,风险小。
  2. 这个方案能最大程度保留实验指导里期待的“内核态/用户态职责重新划分”的教学效果。
  3. 用户态程序调试起来比内核态舒服太多——至少不用担心一个野指针直接把系统搞挂。

2. 动手之前必须吃透的内核终端处理链路

2.1 Linux 0.11的终端设备树长什么样

Linux 0.11里,终端设备的抽象层次大概是这样的:

  • 最底层是硬件:键盘控制器(8042)、显示控制器(VGA文本模式)
  • 往上一层是中断处理:keyboard.S中的键盘中断汇编码,把扫描码翻译成ASCII或组合键事件
  • 再往上是字符设备驱动层:console.c管理显示内存映射、光标位置,rs_io.s处理串口
  • 最上层是tty_io.c提供的文件操作接口:tty_read、tty_write,它们被挂到文件系统的设备节点上

当你在用户态执行read(0, buf, size)时,系统调用会走到内核的sys_read,根据文件描述符找到对应的file结构体,然后调用file->f_op->read。对于标准输入(fd=0),在没有重定向的情况下,这个f_op最终指向的就是tty_io.c里的tty_read。

所以关键点来了:用户态程序能读到的内容,完全取决于tty_read往里塞了什么。默认情况下tty_read已经帮你把行缓冲、回显、退格处理都干完了,你读到的是一行干净的内容。实验七要做的就是把这一层“贴心服务”拆掉,让你自己实现。

2.2 内核里默认的tty_read到底做了什么

先看一段简化后的tty_read逻辑(具体代码位置在kernel/chr_drv/tty_io.c):

int tty_read(unsigned int channel, char *buf, int nr) { struct tty_struct *tty = &tty_table[channel]; int chars_read = 0; char c; while (chars_read < nr) { // 等待输入,如果队列为空则进入睡眠 while (EMPTY(tty->read_q)) { schedule(); } c = GETCH(tty->read_q); // 处理行规则:如果是换行符,则本次读取结束 if (c == '\n' || c == '\r') { break; } // 处理退格符 if (c == 127 || c == 8) { if (chars_read > 0) { buf[--chars_read] = 0; // 回显退格 echo_char(tty, c); } continue; } put_user(c, buf + chars_read); chars_read++; // 如果开启了canonical模式,回显字符 echo_char(tty, c); } return chars_read; }

注意这里面有几个行为是内核帮你做掉的:

  • 行缓冲:没读到换行符之前,read不会返回用户态,进程一直在内核里睡眠等待。
  • 退格处理:敲退格时,不是简单地把字符推回队列,而是直接从用户缓冲区里把上一个字符“擦掉”。
  • 回显:每读到一个字符,内核会同步把它写到屏幕上,不用用户程序自己管。

实验七要求的效果,就是把这些行为全部从tty_read里去掉,让每一个原始字符(包括退格、Ctrl组合键)都原封不动地送到用户态。

2.3 裁剪内核时的边界把握

裁剪tty_read不是把代码删光,而是要做到“内核不解释、只搬运”。我当时保留的部分是:

int tty_read(unsigned int channel, char *buf, int nr) { struct tty_struct *tty = &tty_table[channel]; int chars_read = 0; while (chars_read < nr) { while (EMPTY(tty->read_q)) { schedule(); } *(buf + chars_read) = GETCH(tty->read_q); chars_read++; } return chars_read; }

这段代码看起来简单,但有几个坑:

  1. 不要关中断。schedule()在进程睡眠时依赖时钟中断来唤醒,把中断关了系统直接死锁。
  2. 一次读取的字节数要可控。如果用户程序一次请求读1个字节,那就循环一次就返回,不吞多余的数据。
  3. 反馈速度。裁剪后少了回显逻辑,用户程序必须自己处理“敲一个字符立刻显示一个字符”这件事,否则终端用起来极其别扭。

我当时踩过一个坑:裁剪完tty_read后,没有同步去掉tty_write里的行规则残留逻辑,导致屏幕输出偶尔多出奇怪的控制序列。后来查了半天才发现,tty_write对于\n会自动补一个\r,这在用户态重新实现后反而变成负担,需要一并关掉。

3. 用户态终端控制程序的核心设计

3.1 程序架构:别把终端控制写成一座孤岛

我在用户态写控制程序时,没有把所有逻辑堆在一个main里,而是按照“输入解析、回显控制、信号处理、输出刷新”四个模块来拆:

用户态终端控制程序 ├── 输入循环:从 fd=0 逐字节读取 ├── 字符解析器:识别普通字符、退格、Ctrl+C、Ctrl+D、换行 ├── 行缓冲区:维护当前行内容,支持退格回溯 ├── 回显控制器:决定哪些字符需要写回屏幕 └── 信号处理器:模拟终端产生的SIGINT/SIGQUIT行为

这样拆的好处是,每个模块都能单独测试。比如你可以在不接键盘的情况下,用管道往程序里灌测试数据,验证字符解析器的正确性,再用交互式终端去验回显效果。

3.2 输入循环与底层读接口的配合

内核裁剪后,用户态程序读一个字符会直接返回。这里有个性能问题:如果每个字符都走一次read系统调用,上下文切换开销会比较大,但Linux 0.11是教学内核,跑的又是模拟器,完全能接受。所以不用过度优化,逐字节读取是合理选择。

我当时的输入循环核心结构:

while (1) { char c; int n = read(0, &c, 1); if (n <= 0) break; // 交给字符解析器处理 int result = handle_char(c); if (result == TERM_EXIT) { break; } }

注意read(0, &c, 1)这里的0是标准输入文件描述符。实验里如果做了文件描述符重定向,这里要跟着变,否则程序读不到键盘输入。

3.3 字符解析器的核心逻辑

字符解析器是我整个用户态程序里最费心思的部分。它要处理的情况有很多:

输入字符预期行为
可见字符(字母、数字、符号)存入行缓冲,回显到屏幕
\n结束当前行,提交给应用层处理
\b或\x7f(退格)删除行缓冲末尾字符,光标左移并清除屏幕字符
\x03(Ctrl+C)发送SIGINT信号,清空当前行
\x04(Ctrl+D)触发EOF,返回0
\x1b(ESC)进入转义序列解析,处理方向键等

每个分支我都有对应的处理函数。这里给一个当时实现的简版退格处理逻辑:

static int handle_backspace(term_state *ts) { if (ts->line_len == 0) { // 行缓冲空,退格无效 return 0; } ts->line_len--; // 光标左移一格 write(1, "\b \b", 3); return 0; }

这个"\b \b"是终端控制里的经典三段式:先退格、打空格覆盖原字符、再退格回到正确位置。如果不这么做,屏幕上会残留被“删除”的字符。

3.4 回显控制的细节

内核的tty_read被裁剪后,回显的职责完全落在用户程序上。我的回显策略是:

  • 普通可见字符:直接写回标准输出
  • 退格:不做普通回显,而是执行“退格、空格、退格”的清除序列
  • 控制字符:回显为脱字符形式,比如Ctrl+C显示为^C

脱字符回显是一个容易被忽略的细节。如果不做这个转换,终端会直接显示原始的ASCII控制字符,有些会把屏幕搞花。举个例子,原始的\x03(ETX)如果直接写回屏幕,在很多终端模拟器里会被当作控制指令处理,而不是显示成^C。

3.5 信号产生:用户态模拟内核的信号行为

Linux 0.11的终端驱动在收到特定控制字符时会向前台进程组发送信号。实验七既然要接管终端控制,这个逻辑也得在用户态重建。我当时用的是kill()系统调用。

关键代码是这样:

static int handle_ctrl_c(term_state *ts) { // 向当前进程组发送SIGINT kill(0, SIGINT); // 清空当前行数据 ts->line_len = 0; // 换行并重新输出提示符 write(1, "\n$ ", 3); return 0; }

这里kill(0, SIGINT)是向调用进程所在进程组广播信号。在模拟器里跑Shell场景时,这个行为能很好地模拟Ctrl+C中断前台程序的真实效果。

4. 实操过程:从改内核到用户态程序跑通的完整记录

4.1 环境准备与初始验证

我当时的实验环境是Linux 0.11源码加Bochs模拟器。如果没有现成镜像,需要先编译内核、制作根文件系统镜像。

推荐先做一步验证:在改任何代码之前,先确认默认内核能跑起来,并且能通过read从终端读到输入。我习惯在init或shell里临时加一段代码:

char buf[128]; int n = read(0, buf, sizeof(buf)); write(1, "debug read: ", 12); write(1, buf, n);

这一步如果正常,说明整个IO链路是通的,问题只会在后面的裁剪中引入。

4.2 裁剪内核tty_io.c的具体操作

我先把kernel/chr_drv/tty_io.c里tty_read函数整体替换成极简版本(见2.3节),同时把tty_write里的\n转\r\n逻辑也关掉了,因为用户态程序自己会处理换行序列。

这里要注意一个细节:tty_write里的\n\r转换如果关掉,串口终端和虚拟控制台都会受影响。我当时只在tty_write里加了一个条件判断,用一个全局开关控制转换是否启用,方便来回切换调试。

编译步骤:

# 在Linux 0.11源码根目录 cd linux-0.11 make clean make # 生成新内核映像

Bochs启动后如果能看到Shell提示符,说明内核裁剪至少没有让系统崩溃。如果直接黑屏或panic,优先查是不是tty_read里的sleep_on逻辑被改坏了。

4.3 用户态终端控制程序的编写与编译

用户态程序不是非得叫termctl,我当时叫它ttyuser。代码全放在根文件系统的/usr/root目录下,交叉编译方式跟普通Linux用户态程序一样(因为Linux 0.11用的是i386架构,直接用配套的gcc编译即可)。

程序的主框架就是3.2节那个循环。编译命令:

gcc -o ttyuser ttyuser.c cp ttyuser /mnt/rootfs/usr/root/

把编译好的文件放进根文件系统镜像,再启动Bochs。进入系统后在Shell里执行/usr/root/ttyuser。

4.4 联调过程中发现的几个关键问题

第一版程序跑起来后,我碰到一个非常诡异的现象:在ttyuser里按退格键,行缓冲长度确实在减少,但屏幕上的字符没有消失。排查了半天,发现是回显的清除序列问题。我在回显退格时用的是\b \b,但当时终端光标位置在行尾,\b只移动一格,如果前面已经输入了多个字符,需要先移动多格再清除。后续我把回显逻辑改成了“根据当前行长度,重新输出整行格式化内容”的方案,问题才真正解决。

这个方案是这样的:每次字符变化时,把整行内容保存在缓冲区里,然后输出\r回车,再重画一遍当前行和提示符。原理很简单,但效果很稳——不需要关心光标具体在哪个位置,只管覆盖重画。

另一个坑是关于read返回值的。裁剪后的tty_read即使在收到EOF时,返回的也可能是0而不是负数,所以用户程序里判断EOF要兼容n == 0的情况。

还有一个问题是波特率不匹配导致的输入串扰。这个不是Linux 0.11特有的,是串口终端的老问题。在Bochs里如果用默认配置,偶尔会出现按一个键出来两个字符。解法是检查虚拟机的串口/键盘控制器配置,把键盘类型设为AT键盘而不是XT键盘。

4.5 新增一个小功能:方向键支持的实验

实验做得顺了以后,我顺手扩展了一个方向键支持的实验性功能,其实是把ANSI转义序列解析器加进来。当收到\x1b[A时,认为是上箭头,本例里把它当成“回显上次历史命令”。

static int handle_escape_seq(term_state *ts) { char seq[2]; read(0, &seq[0], 1); read(0, &seq[1], 1); if (seq[0] == '[' && seq[1] == 'A') { // 上箭头:回显历史命令 write(1, ts->history, ts->history_len); return 0; } return 0; }

这属于锦上添花,不一定满足严格的实验要求,但如果想做扩展,这给了一个很好的方向。

5. 常见问题诊断与避坑清单

5.1 问题速查表

现象可能原因排查思路
串口或控制台完全没有输入回显tty_read被裁剪后,没有用户态回显逻辑检查用户程序是否在read后马上write
退格可以移动光标但不删字符回显清除序列不对改成整行重画法
Ctrl+C没有中断前台进程用户态没有发送kill(0, SIGINT)检查信号处理分支是否触发
按一个键出现两个字符键盘中断处理或Bochs配置问题核对键盘类型与中断处理
\n在屏幕上输出独特形状tty_write没有做\r\n转换,终端独占下游控制用户程序自行输出\r\n,或恢复内核转换
终端偶尔进入死循环tty_read中睡眠等待队列被破坏查read_q的空判断与竞争条件

5.2 地表最强调试技巧:用Bochs的日志和断点

如果说做Linux 0.11实验有什么“隐藏兵器”,那就是Bochs的调试功能。我在做实验七时最常用的是两个:

第一个是printk。内核态代码用printk打印,注意这不是用户态的printf。裁剪tty_io.c时,我每改动一个判断分支都会加一行printk,用来确认代码执行路径。等逻辑稳定后再删掉。

第二个是Bochs的暂停功能。在Bochs调试模式下,可以直接暂停CPU,查看当前执行的指令地址,反汇编找到运行到哪条指令。这个方法在处理死循环和诡异挂起时特别有用。

5.3 一个极易被忽视的坑:输入缓冲区与进程调度

Linux 0.11里的tty缓冲区是环形队列,在有多个进程同时在读终端时(比如Shell启动前后台进程),裁剪后的tty_read如果没处理好多进程竞争,会导致一个进程把另一个进程的数据读走。

我当时遇到的现象是:在Shell里执行ttyuser后,程序读到的第一个字符是上一次Shell命令残留的字符。这是因为Shell的前一次读取并没有把缓冲区全清干净,而裁剪后的tty_read又不会帮忙吞掉多余数据。

解决方法是静止的:在Shell退出前,主动清空tty读缓冲,或者让用户态程序在启动时有一个“清空输入缓冲区”的入口。这个细节我不会写在实验报告里,但调试时帮了大忙。

6. 一些个人体会与扩展思路

做完实验七,我对“什么是终端”“什么是设备控制”这两个概念有了不一样的认知。以前总觉得终端就是那个黑框框,敲进去字显示出来,天经地义。但真正把内核那层逻辑搬到自己程序里后,你才会发现:原来每一层逻辑都是有人特意设计的,不是凭空出现的。

如果你还有余力,这个实验可以往几个方向扩展:

  • 给用户态终端控制程序加上“行编辑”的完整能力,包括插入、删除、光标移动、历史命令回滚。
  • 把信号处理的细节补全,比如Ctrl+Z、Ctrl+\,以及作业控制相关的内容。
  • 尝试把控制台和串口做成两套独立的字符策略,在用户态区分设备类型。

我个人实验做完后,最大的收获其实不是代码本身,而是debug的过程。当你裁剪了内核的关键路径之后,系统的每一个异常都在向你传递信息,关键是你要能听懂。很多同学在做实验时一看到内核panic就慌,其实只要你把裁减的部分一点点加回去,或者把用户态的逻辑一步步简化,问题很快就会定位。

实验七并不难,难的是你愿不愿意在没头绪的时候,先喝口水,然后把tty_io.c从头到尾再读一遍。相信我,读到第三遍的时候,很多问题自己就消失了。

相关新闻

AI辅助开题报告写作:从构思到初稿的实操全攻略

AI辅助开题报告写作:从构思到初稿的实操全攻略

深夜十一点多&#xff0c;某高校的硕士生A同学盯着空白的文档&#xff0c;光标停在“选题背景及意义”这一行已经整整二十分钟。导师白天刚丢下一句“这周把开题报告初稿给我”&#xff0c;而他现在连研究现状都理不清楚。这种场景在每年毕业季都会上演——开题报告不算严格意义…

2026/10/20 9:10:50 阅读更多 →
编辑看IThenticate查重报告:不只看总相似率,这5点才是关键

编辑看IThenticate查重报告:不只看总相似率,这5点才是关键

投过SCI的都知道&#xff0c;IThenticate查重报告出来那一瞬间的心情。总相似率低&#xff0c;心头大石落地&#xff1b;相似率一高&#xff0c;立刻开始逐句对照&#xff0c;琢磨怎么降重。但有一个问题很多人没想明白&#xff1a;编辑看这份报告&#xff0c;和你自己看这份报…

2026/10/20 9:09:49 阅读更多 →
发票抬头查询API:企业开票信息自动校验与补全实践指南

发票抬头查询API:企业开票信息自动校验与补全实践指南

做财务系统、报销平台或者电商后台的开发者&#xff0c;大概率都遇到过这种场景&#xff1a;用户提交的开票抬头五花八门&#xff0c;“XX科技有限公司”写成了“XX科技责任有限公司”&#xff0c;或者漏掉了“&#xff08;中国&#xff09;”后缀&#xff0c;结果发票开出后客…

2026/10/20 9:09:49 阅读更多 →

最新新闻

AI课程矩阵实战路线:从提示词工程到API调用与批量部署

AI课程矩阵实战路线:从提示词工程到API调用与批量部署

AI 课程现在不难找&#xff0c;难的是判断一门课在你的成长路径里到底处于哪个位置。市面上大量课程要么只教提示词&#xff0c;要么直接堆 ComfyUI 节点&#xff0c;学完能复现案例&#xff0c;但换个场景就不会用了。“河南元力共生 AI 课程矩阵”这个提法&#xff0c;核心是…

2026/10/20 9:45:00 阅读更多 →
秋招十月下旬还没回音:剩下六周怎么排

秋招十月下旬还没回音:剩下六周怎么排

版权与内容来源声明 本文为原创整理。文中涉及官方文档、开源仓库、论文与公开报道的内容&#xff0c;均在附表 A 中标注来源&#xff1b;引用官方原文保持原样&#xff0c;不作改写。文中命令、版本号与界面截图以本文成文时的实测/核验结果为准&#xff0c;标注「待验证」的部…

2026/10/20 9:45:00 阅读更多 →
Matlab GUI实时均衡器:IIR滤波器零极点与音频信号流协同设计

Matlab GUI实时均衡器:IIR滤波器零极点与音频信号流协同设计

简介&#xff1a;本资源是一份面向数字信号处理初学者与Matlab GUI入门者的实践型教学项目&#xff0c;聚焦音频信号频率响应调节这一典型应用场景&#xff0c;帮助学习者掌握IIR滤波器设计原理与交互式界面开发方法。压缩包共3个文件&#xff08;2个.m主程序与辅助函数、1个.f…

2026/10/20 9:45:00 阅读更多 →
企业网盘选型实测:安全管控与协作效率深度对比

企业网盘选型实测:安全管控与协作效率深度对比

测评类文章最容易犯的毛病&#xff0c;就是把配置参数罗列一通&#xff0c;最后丢一句“按需选择”就完事。这篇不一样&#xff0c;我从企业实际使用的角度出发&#xff0c;把真正影响日常协作和数据安全的关键点都拆开讲清楚&#xff0c;包括那些厂商不会主动告诉你的限制和坑…

2026/10/20 9:45:00 阅读更多 →
AI日报系统设计与实现:从RSS聚合到智能摘要生成

AI日报系统设计与实现:从RSS聚合到智能摘要生成

我无法基于当前输入生成符合要求的博文。原因如下&#xff1a;输入中项目标题为“AI 日报&#xff08;2026年10月10日&#xff09;”&#xff0c;但该标题本身不具备可拆解的实质性项目属性——它是一个时间标记型信息聚合名称&#xff0c;而非具体技术实现、工具开发、系统搭建…

2026/10/20 9:45:00 阅读更多 →
FPGA+AD/DA协同设计:H题信号发生器与频谱分析的时序工程实践

FPGA+AD/DA协同设计:H题信号发生器与频谱分析的时序工程实践

简介&#xff1a;本资源为2024年全国大学生电子设计竞赛H题完整参赛代码工程包&#xff0c;面向电子类、自动化、通信等专业本科生及嵌入式开发初学者&#xff0c;聚焦高实时性、强稳定性嵌入式系统实现场景。压缩包含180个文件&#xff0c;主体为C语言源码&#xff08;c/h&…

2026/10/20 9:43:58 阅读更多 →

日新闻

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 阅读更多 →