<?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>零拷贝与网络 IO on Dorkytiger 的小屋</title>
    <link>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/05-zero-copy-io/</link>
    <description>Recent content in 零拷贝与网络 IO on Dorkytiger 的小屋</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <atom:link href="https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/05-zero-copy-io/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>传统 IO 的四次拷贝与上下文切换痛点</title>
      <link>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/05-zero-copy-io/01-traditional-io/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/05-zero-copy-io/01-traditional-io/</guid>
      <description>&lt;h3 id=&#34;1-传统-io-读取一个文件的完整硬件旅程&#34;&gt;1. 传统 IO 读取一个文件的完整硬件旅程&lt;/h3&gt;
&lt;p&gt;以 Linux 上执行 &lt;code&gt;read(fd, buf, count)&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-text&#34; data-lang=&#34;text&#34;&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;    ├─① 执行 syscall (int 0x80 / syscall 指令)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   └─ CPU 从 Ring 3 切换到 Ring 0，陷入内核
&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;[内核 VFS 层]
&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;    ├─② 检查文件是否已在页缓存 (Page Cache) 中
&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&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├─③ 构造 I/O 请求，提交给磁盘驱动程序
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   └─ 驱动配置 DMA 控制器，告诉它：把磁盘扇区数据搬运到主存 [物理地址]
&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&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;    ├─④ 磁盘控制器通过 DMA 将数据直接写入主存（内核缓冲区）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   └─ DMA 传输期间，CPU 完全空闲，可以执行其他任务
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   └─ 传输完成后，磁盘控制器发出硬中断 (IRQ)
&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&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;    ├─⑤ 中断处理程序唤醒等待该 I/O 的进程
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    │   └─ 将数据从页缓存 (内核空间) CPU 拷贝到用户缓冲区 (用户空间)
&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&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;    ├─⑥ syscall 返回，CPU 从 Ring 0 切回 Ring 3
&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;hr&gt;
&lt;h3 id=&#34;2-四次拷贝的真正定义按硬件参与者区分&#34;&gt;2. 四次拷贝的真正定义（按硬件参与者区分）&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;编号&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;拷贝路径&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;执行者&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;是否消耗 CPU 周期&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;数据形态&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;①&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;磁盘扇区 → 主存（内核缓冲区）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;DMA 控制器&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;❌ 不消耗&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;物理内存写入&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;②&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;内核缓冲区 → 用户缓冲区&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;CPU&lt;/strong&gt;（&lt;code&gt;memcpy&lt;/code&gt;）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;✅ 消耗&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;物理内存→物理内存&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;③&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;用户缓冲区 → Socket 缓冲区（内核）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;CPU&lt;/strong&gt;（&lt;code&gt;memcpy&lt;/code&gt;）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;✅ 消耗&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;物理内存→物理内存&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;④&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Socket 缓冲区 → 网卡 DMA 缓冲区&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;DMA 控制器&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;❌ 不消耗&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;主存→网卡缓存&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>零拷贝核心武器 1：`mmap` 内存映射与 DMA</title>
      <link>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/05-zero-copy-io/02-mmap/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/05-zero-copy-io/02-mmap/</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;code&gt;mmap&lt;/code&gt;&lt;/strong&gt; 是零拷贝技术的第一把核心武器。它解决的是传统 IO 中&lt;strong&gt;最痛苦的一步&lt;/strong&gt;：&lt;strong&gt;内核缓冲区 → 用户缓冲区 的 CPU 拷贝&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它的核心思想极其简单，但硬件实现极其精妙：&lt;strong&gt;让用户态程序直接看到内核的 Page Cache，而不是把数据拷贝一份给用户。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;1-传统-read-的内存布局-vs-mmap-的内存布局&#34;&gt;1. 传统 read() 的内存布局 vs mmap 的内存布局&lt;/h3&gt;
&lt;h4 id=&#34;传统-read数据被拷贝了一次&#34;&gt;传统 &lt;code&gt;read()&lt;/code&gt;：数据被拷贝了一次&lt;/h4&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-text&#34; data-lang=&#34;text&#34;&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&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;|  | 用户缓冲区   |  |          |  | Page Cache  |  |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  | (buf)       |  |          |  | (磁盘数据)   |  |
&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;|         +---- CPU 拷贝 (memcpy) ----+         |
&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&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;数据路径&lt;/strong&gt;：磁盘 → (DMA) → Page Cache → (CPU 拷贝) → 用户缓冲区&lt;/p&gt;</description>
    </item>
    <item>
      <title>零拷贝核心武器 2：Linux 新一代 `io_uring`</title>
      <link>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/05-zero-copy-io/03-io-uring/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/05-zero-copy-io/03-io-uring/</guid>
      <description>&lt;p&gt;&lt;code&gt;io_uring&lt;/code&gt; 可以理解为 Linux 在 I/O 模型上的一个分水岭。它设计的目标，就是彻底解决 epoll 等传统模型在应对海量 I/O 时的根本性能痛点，为&lt;strong&gt;无锁队列&lt;/strong&gt;和&lt;strong&gt;零拷贝&lt;/strong&gt;提供了一条直达内核的“绿色通道”。&lt;/p&gt;
&lt;h3 id=&#34;1-破局从系统调用到共享队列&#34;&gt;1. 破局：从“系统调用”到“共享队列”&lt;/h3&gt;
&lt;p&gt;传统 I/O 模型（如 epoll）的性能瓶颈在于，每次发起和完成 I/O 操作，都需要通过系统调用陷入内核，这伴随着昂贵的上下文切换和 CPU 缓存污染。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;io_uring&lt;/code&gt; 最核心的变革在于：&lt;strong&gt;它创建了两个在用户态和内核态之间共享的环形缓冲区（Ring Buffer）&lt;/strong&gt;，作为通信的主通道，从而极大减少了系统调用次数。&lt;/p&gt;
&lt;h3 id=&#34;2-架构sq-与-cq-双环形缓冲区&#34;&gt;2. 架构：SQ 与 CQ 双环形缓冲区&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;io_uring&lt;/code&gt; 的核心就是两个通过 &lt;code&gt;mmap&lt;/code&gt; 共享的队列：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;提交队列（Submission Queue, SQ）&lt;/strong&gt;：这是用户态向内核“派发任务”的地方。你只需要将描述 I/O 操作的**提交队列条目（SQE）**放入 SQ 的尾部。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;完成队列（Completion Queue, CQ）&lt;/strong&gt;：这是内核向用户态“返回结果”的地方。内核完成 I/O 操作后，会将包含结果（如返回码）的**完成队列条目（CQE）**放入 CQ 的尾部，供用户态程序读取。&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-text&#34; data-lang=&#34;text&#34;&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  +-----------------+  mmap映射  +---------------------+    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  | 提交队列 (SQ)   |&amp;lt;----------&amp;gt;| 内核处理 SQ 中的请求 |    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  |  [SQE] [SQE]    |            |                     |    |
&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|         v                              v                    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  +-----------------+  mmap映射  +---------------------+    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  | 完成队列 (CQ)   |&amp;lt;----------&amp;gt;| 内核将结果写入 CQ    |    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  |  [CQE] [CQE]    |            |                     |    |
&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;3-零拷贝的硬件基础&#34;&gt;3. 零拷贝的硬件基础&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;io_uring&lt;/code&gt; 实现高性能的核心在于其与硬件的协同工作：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
