<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>TTY on 大飞的博客</title>
    <link>https://www.dafei.me/tags/tty/</link>
    <description>Recent content in TTY on 大飞的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Fri, 22 May 2026 07:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.dafei.me/tags/tty/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从零写OS（三十六）：ext2 写操作 —— 文件创建、引用计数与 tty 字符设备</title>
      <link>https://www.dafei.me/posts/os-36-ext2-write/</link>
      <pubDate>Fri, 22 May 2026 07:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-36-ext2-write/</guid>
      <description>&lt;p&gt;前几章文件系统都是只读的。这一章实现 ext2 的写路径，让 &lt;code&gt;echo hello &amp;gt; /tmp/a.txt &amp;amp;&amp;amp; cat /tmp/a.txt&lt;/code&gt; 能正常工作，且重启后文件仍然存在。过程中还修了 fd 引用计数和 tty 字符设备两个问题。&lt;/p&gt;
&lt;h2 id=&#34;ext2-磁盘结构回顾&#34;&gt;ext2 磁盘结构回顾&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;块组 0:
  [超级块][组描述符][block bitmap][inode bitmap][inode table][数据块...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;关键字段：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sb-&amp;gt;s_free_inodes_count&lt;/code&gt; / &lt;code&gt;gd-&amp;gt;bg_free_inodes_count&lt;/code&gt;：空闲 inode 计数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sb-&amp;gt;s_free_blocks_count&lt;/code&gt; / &lt;code&gt;gd-&amp;gt;bg_free_blocks_count&lt;/code&gt;：空闲 block 计数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gd-&amp;gt;bg_inode_bitmap&lt;/code&gt;：inode bitmap 所在块号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gd-&amp;gt;bg_block_bitmap&lt;/code&gt;：block bitmap 所在块号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gd-&amp;gt;bg_inode_table&lt;/code&gt;：inode table 起始块号&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;inode 号从 1 开始；前 10 个是系统保留的（root=2，lost+found=11）。&lt;/p&gt;
&lt;h2 id=&#34;alloc_inode--alloc_block&#34;&gt;alloc_inode / alloc_block&lt;/h2&gt;
&lt;h3 id=&#34;alloc_inode&#34;&gt;alloc_inode&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;alloc_inode&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 1. 读 inode bitmap 块
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 2. 找第一个为 0 的位（bit i → inode i+1）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 3. 置位，写回 bitmap
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 4. 更新超级块和组描述符的 free_inodes_count
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 5. 返回 inode 号（从 1 开始）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;alloc_block&#34;&gt;alloc_block&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;alloc_block&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 1. 读 block bitmap 块
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 2. 找第一个为 0 的位，跳过 block 0（保留）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 3. 置位，写回 bitmap
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 4. 更新超级块和组描述符的 free_blocks_count
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 5. 清零新分配的块（避免垃圾数据）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 6. 返回块号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意：block bitmap 的 bit 0 对应 block 0，必须跳过（block 0 不存在）。实际第一个可分配的块由文件系统布局决定（本项目中是 610）。&lt;/p&gt;</description>
    </item>
    <item>
      <title>从零写OS（三十三）：阻塞式 TTY —— read 不再忙等</title>
      <link>https://www.dafei.me/posts/os-33-blocking-tty/</link>
      <pubDate>Fri, 22 May 2026 04:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-33-blocking-tty/</guid>
      <description>&lt;p&gt;上一章 busybox sh 成功显示了 &lt;code&gt;/ #&lt;/code&gt; 提示符，但 shell 拿不到任何输入——因为 &lt;code&gt;tty_read&lt;/code&gt; 是忙轮询的，没有字符就直接返回 0，shell 以为收到 EOF，立刻退出。这一章实现真正的阻塞式 TTY，让 shell 能等待用户输入。&lt;/p&gt;
&lt;h2 id=&#34;之前的问题&#34;&gt;之前的问题&lt;/h2&gt;
&lt;p&gt;之前的 &lt;code&gt;tty_read&lt;/code&gt; 大概是这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tty_read&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;buf, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; len) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 轮询串口状态寄存器
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;inb&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x3F8&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;   &lt;span style=&#34;color:#75715e&#34;&gt;// 没数据就返回 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;buf &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inb&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x3F8&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这有两个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;没有输入时返回 0，上层程序（shell）以为是 EOF&lt;/li&gt;
&lt;li&gt;如果上层在循环里调这个，CPU 100% 占用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;正确做法：没有输入时&lt;strong&gt;挂起进程&lt;/strong&gt;，等串口中断来了再唤醒。&lt;/p&gt;
&lt;h2 id=&#34;串口中断irq4&#34;&gt;串口中断（IRQ4）&lt;/h2&gt;
&lt;p&gt;COM1 对应 IRQ4，接在 PIC 主片的 IR4 引脚。要用串口中断，需要两步：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 在 PIC 上 unmask IRQ4&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// PIC 主片 IMR 寄存器：0 表示开放，1 表示屏蔽
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;uint8_t&lt;/span&gt; mask &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;inb&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x21&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mask &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;~&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;);   &lt;span style=&#34;color:#75715e&#34;&gt;// 清除 bit4，开放 IRQ4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;outb&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0x21&lt;/span&gt;, mask);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. 开启串口的接收中断&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>从零写OS（二十六）：poll —— 同时等待多个 fd</title>
      <link>https://www.dafei.me/posts/os-26-poll/</link>
      <pubDate>Thu, 14 May 2026 06:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-26-poll/</guid>
      <description>&lt;p&gt;上一章实现了 TTY 输入，但用户程序只能这样等输入：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-nasm&#34; data-lang=&#34;nasm&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.read_loop:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;syscall&lt;/span&gt; SYS_READ(fd&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, buf, &lt;span style=&#34;color:#ae81ff&#34;&gt;64&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;cmp&lt;/span&gt; rax, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;jle&lt;/span&gt; .read_loop
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;问题很明显：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CPU 全速空转&lt;/strong&gt;：没有输入时 &lt;code&gt;SYS_READ&lt;/code&gt; 每次返回 0，进程白白占用 CPU&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无法同时等多个 fd&lt;/strong&gt;：如果既要等 stdin，又要等 pipe，必须串行轮询，任一 fd 都可能饿死&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;poll&lt;/code&gt; 解决这两个问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;poll-是什么&#34;&gt;poll 是什么&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;poll(fds[], nfds, timeout)&lt;/code&gt; 是一个系统调用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传入一组 &lt;code&gt;pollfd_t&lt;/code&gt; 结构，每个描述&amp;quot;我关心 fd X 的什么事件&amp;quot;&lt;/li&gt;
&lt;li&gt;内核检查每个 fd 是否满足条件&lt;/li&gt;
&lt;li&gt;返回就绪的 fd 数量，并在 &lt;code&gt;revents&lt;/code&gt; 里标记哪些事件发生了&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; pollfd {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;   fd;       &lt;span style=&#34;color:#75715e&#34;&gt;// 监听哪个 fd
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;short&lt;/span&gt; events;   &lt;span style=&#34;color:#75715e&#34;&gt;// 关心：POLLIN（可读）/ POLLOUT（可写）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;short&lt;/span&gt; revents;  &lt;span style=&#34;color:#75715e&#34;&gt;// 内核填写：实际发生了什么
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;poll&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; pollfd &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;fds, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; nfds, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; timeout_ms);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有了 poll，程序可以：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;poll([{fd=0, POLLIN}, {fd=pipe_r, POLLIN}], 2, -1)
  → 等到任意一个有数据，才返回
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;实现架构&#34;&gt;实现架构&lt;/h2&gt;
&lt;p&gt;整体分四层：&lt;/p&gt;</description>
    </item>
    <item>
      <title>从零写OS（二十五）：TTY 终端输入</title>
      <link>https://www.dafei.me/posts/os-25-tty/</link>
      <pubDate>Thu, 14 May 2026 05:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-25-tty/</guid>
      <description>&lt;p&gt;到目前为止，进程只能输出，没有办法接收用户输入。&lt;code&gt;SYS_READ&lt;/code&gt; 形同虚设，键盘按了也没反应。&lt;/p&gt;
&lt;p&gt;这一章实现 &lt;strong&gt;TTY&lt;/strong&gt;——Unix 对终端设备的最初抽象，让进程能以行为单位从串口读取输入。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;tty-是什么&#34;&gt;TTY 是什么&lt;/h2&gt;
&lt;p&gt;TTY 原本是 teletypewriter（电传打字机）的缩写。在 Unix 里，它泛指&amp;quot;终端&amp;quot;——一个字符设备，既能接收键盘输入，又能输出文字。&lt;/p&gt;
&lt;p&gt;现代 Linux 里的 &lt;code&gt;/dev/tty&lt;/code&gt;、&lt;code&gt;/dev/pts/0&lt;/code&gt; 都是 TTY 的后代。我们这里用串口 COM1 模拟一个最简单的 TTY。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;整体架构&#34;&gt;整体架构&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;用户键盘输入
   ↓ 串口硬件触发 IRQ4
isr_handler → tty_recv(c)   ← 回显 + 写入 ring buffer
                ↓ 遇到 &amp;#39;\n&amp;#39;
           line_ready = 1
                ↓ 进程 syscall SYS_READ(fd=0)
           tty_read(buf, len)  ← 把一行搬到用户 buf
                ↓
           用户程序处理输入
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;三个关键组件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Ring Buffer&lt;/strong&gt;：固定大小的环形缓冲区，存放还未被读走的字符&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;行规程（line discipline）&lt;/strong&gt;：积累字符，遇 &lt;code&gt;\n&lt;/code&gt; 才通知&amp;quot;行就绪&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IRQ4 中断处理&lt;/strong&gt;：从串口读一个字节，交给 TTY&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;ring-buffer&#34;&gt;Ring Buffer&lt;/h2&gt;
&lt;p&gt;环形缓冲区是一个固定数组，加上读指针和写指针：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[ _ _ _ _ h e l l o \n _ _ ]
           ↑                 ↑
         rx_read          rx_write
rx_len = 6
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;写字符：&lt;code&gt;rx_buf[rx_write] = c; rx_write = (rx_write + 1) % TTY_BUF_SIZE; rx_len++&lt;/code&gt;&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
