<?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>网络 on 大飞的博客</title>
    <link>https://www.dafei.me/tags/%E7%BD%91%E7%BB%9C/</link>
    <description>Recent content in 网络 on 大飞的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Tue, 02 Jun 2026 11:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.dafei.me/tags/%E7%BD%91%E7%BB%9C/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从零写OS（四十五）：TCP 服务端——bind/listen/accept</title>
      <link>https://www.dafei.me/posts/os-45-tcp-server/</link>
      <pubDate>Tue, 02 Jun 2026 11:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-45-tcp-server/</guid>
      <description>&lt;p&gt;前面几章都是做 TCP 客户端——发 SYN、连接、发请求、收响应。这章反过来，让内核能&lt;strong&gt;作为 TCP 服务端&lt;/strong&gt;：监听端口，接受连接，响应请求。&lt;/p&gt;
&lt;h2 id=&#34;客户端-vs-服务端的区别&#34;&gt;客户端 vs 服务端的区别&lt;/h2&gt;
&lt;p&gt;客户端：主动发 SYN，等待 SYN-ACK。&lt;/p&gt;
&lt;p&gt;服务端：被动等待 SYN，收到后发 SYN-ACK，等 ACK 完成三次握手，然后通过 &lt;code&gt;accept()&lt;/code&gt; 把新连接交给应用层。&lt;/p&gt;
&lt;p&gt;关键区别在于：服务端有两种 socket 角色——&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;角色&lt;/th&gt;
          &lt;th&gt;职责&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;监听 socket（listen fd）&lt;/td&gt;
          &lt;td&gt;绑定端口，等待连接请求&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;连接 socket（accept fd）&lt;/td&gt;
          &lt;td&gt;每个客户端连接对应一个，负责实际数据收发&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;三步接口&#34;&gt;三步接口&lt;/h2&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:#a6e22e&#34;&gt;bind&lt;/span&gt;(fd, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;sa, len)    &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:#a6e22e&#34;&gt;listen&lt;/span&gt;(fd, backlog)   &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:#a6e22e&#34;&gt;accept&lt;/span&gt;(fd, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;sa, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;len) &lt;span style=&#34;color:#75715e&#34;&gt;// 阻塞，等到有连接，返回新 fd
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;数据结构&#34;&gt;数据结构&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tcp_sock_t&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;  is_listener;
&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;  accept_queue[&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;];   &lt;span style=&#34;color:#75715e&#34;&gt;// 存已完成三次握手的连接 index
&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;uint32_t&lt;/span&gt; accept_head, accept_tail;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;还新增了两个状态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TCP_LISTEN&lt;/code&gt;：监听中，等待 SYN&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TCP_SYN_RECV&lt;/code&gt;：收到 SYN 已回 SYN-ACK，等最终 ACK&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;核心流程&#34;&gt;核心流程&lt;/h2&gt;
&lt;h3 id=&#34;1-收到-syn&#34;&gt;1. 收到 SYN&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;tcp_handle()&lt;/code&gt; 里单独处理 SYN：&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;if&lt;/span&gt; ((tcp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;flags &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; TCP_SYN) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;(tcp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;flags &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; TCP_ACK)) {
&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;// 找到监听该端口的 socket
&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;// 分配新连接 slot
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;state       &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; TCP_SYN_RECV;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;local_port  &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dport;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;remote_port &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sport;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;remote_ip   &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; src_ip;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;ack         &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ntohl&lt;/span&gt;(tcp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;seq) &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:#75715e&#34;&gt;// 下次要 ACK 的序号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;syn_seq     &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xABCD1234&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_una &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_nxt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;syn_seq;
&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;tcp_send_seg&lt;/span&gt;(ns, ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;syn_seq, TCP_SYN &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; TCP_ACK, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-收到最终-ack完成三次握手&#34;&gt;2. 收到最终 ACK（完成三次握手）&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;if&lt;/span&gt; (s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;state &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; TCP_SYN_RECV &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; (tcp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;flags &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; TCP_ACK)) {
&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; (ack_val &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;syn_seq &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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_una &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_nxt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ack_val;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; TCP_ESTABLISHED;
&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;// 放入监听 socket 的 accept queue
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_queue[ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_tail &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; i;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_tail&lt;span style=&#34;color:#f92672&#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;h3 id=&#34;3-accept-取连接&#34;&gt;3. accept() 取连接&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;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tcp_accept&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; s, ...) {
&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;tcp_sock_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;ls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;tcp_socks[s];
&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;while&lt;/span&gt; (ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_head &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_tail) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sti; &lt;span style=&#34;color:#a6e22e&#34;&gt;net_poll&lt;/span&gt;(); cli;   &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 style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; ni &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_queue[ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_head&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&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; ni;
&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;h2 id=&#34;测试&#34;&gt;测试&lt;/h2&gt;
&lt;p&gt;写了一个简单的 HTTP 服务端程序：&lt;/p&gt;</description>
    </item>
    <item>
      <title>从零写OS（四十四）：UDP Socket &#43; DNS 解析</title>
      <link>https://www.dafei.me/posts/os-44-udp-dns/</link>
      <pubDate>Tue, 02 Jun 2026 10:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-44-udp-dns/</guid>
      <description>&lt;p&gt;上一章（四十三）把 TCP 重传做好了。这一章加两个新功能：&lt;strong&gt;UDP socket 用户空间接口&lt;/strong&gt; 和 &lt;strong&gt;DNS 解析 syscall&lt;/strong&gt;，让用户程序可以用域名来连接服务器。&lt;/p&gt;
&lt;h2 id=&#34;目标&#34;&gt;目标&lt;/h2&gt;
&lt;p&gt;内核里其实早就有 &lt;code&gt;udp_send&lt;/code&gt; 和 &lt;code&gt;dns_resolve&lt;/code&gt; 这两个函数了，但用户程序用不到，因为没有对应的 syscall。这章要做的就是把这两个内核能力&amp;quot;打通&amp;quot;到用户空间：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;socket(AF_INET, SOCK_DGRAM, 0)&lt;/code&gt; → 分配一个 UDP socket，返回 fd 200+&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sendto&lt;/code&gt; / &lt;code&gt;recvfrom&lt;/code&gt; → 通过 UDP socket 收发数据&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SYS_DNS_RESOLVE&lt;/code&gt;（自定义 syscall 500）→ 通过域名查 IP&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;udp-socket-设计&#34;&gt;UDP Socket 设计&lt;/h2&gt;
&lt;p&gt;UDP 比 TCP 简单很多：无连接、无状态机、无重传。核心是一个&lt;strong&gt;接收队列&lt;/strong&gt;：内核收到 UDP 包时，把它放进对应 socket 的队列，用户程序再来取。&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;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&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;uint32_t&lt;/span&gt; src_ip;
&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;uint16_t&lt;/span&gt; src_port;
&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;uint16_t&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:#66d9ef&#34;&gt;uint8_t&lt;/span&gt;  data[&lt;span style=&#34;color:#ae81ff&#34;&gt;512&lt;/span&gt;];      &lt;span style=&#34;color:#75715e&#34;&gt;// 每个包最多 512 字节
&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;udp_pkt_entry_t&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;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&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;              used;
&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;uint16_t&lt;/span&gt;         local_port;
&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;udp_pkt_entry_t&lt;/span&gt;  queue[&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;];  &lt;span style=&#34;color:#75715e&#34;&gt;// 最多暂存 4 个包
&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;uint32_t&lt;/span&gt;         qhead, qtail;
&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;udp_sock_t&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;队列用无限增长的 &lt;code&gt;qhead/qtail&lt;/code&gt; 计数（不是环形下标），访问时用 &lt;code&gt;% UDP_PKT_MAX&lt;/code&gt;，满了就丢包：&lt;/p&gt;</description>
    </item>
    <item>
      <title>从零写OS（四十三）：TCP 重传 —— 丢包了也能传完</title>
      <link>https://www.dafei.me/posts/os-43-tcp-retransmit/</link>
      <pubDate>Mon, 01 Jun 2026 10:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-43-tcp-retransmit/</guid>
      <description>&lt;p&gt;上一章（四十二）修了一堆 busybox 相关的 bug，让 &lt;code&gt;ls&lt;/code&gt;/&lt;code&gt;exec&lt;/code&gt;/&lt;code&gt;wait&lt;/code&gt; 都能正常工作，&lt;code&gt;wget_test&lt;/code&gt; 也能跑通 HTTP。但那时的 TCP 实现有个隐患：&lt;strong&gt;一旦丢包，传输就会永远卡住&lt;/strong&gt;。这一章把重传机制做完整。&lt;/p&gt;
&lt;h2 id=&#34;原代码的问题&#34;&gt;原代码的问题&lt;/h2&gt;
&lt;p&gt;ch42 的 &lt;code&gt;tcp_send_raw&lt;/code&gt; 函数身兼数职：构造 TCP 包、发送、存入发送缓冲区、推进 &lt;code&gt;snd_nxt&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;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tcp_send_raw&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;tcp_sock_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;s, &lt;span style=&#34;color:#66d9ef&#34;&gt;uint8_t&lt;/span&gt; flags, &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;data, &lt;span style=&#34;color:#66d9ef&#34;&gt;uint16_t&lt;/span&gt; dlen) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...&lt;span style=&#34;color:#960050;background-color:#1e0010&#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; (dlen) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ...&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;存&lt;/span&gt; sbuf...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_nxt &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; dlen;
&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;if&lt;/span&gt; (flags &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; (TCP_SYN &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; TCP_FIN)) s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;seq&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;;   &lt;span style=&#34;color:#75715e&#34;&gt;// 推进 seq
&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; (dlen) s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;seq &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; dlen;                     &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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;问题一：&lt;code&gt;seq&lt;/code&gt; 和 &lt;code&gt;snd_nxt&lt;/code&gt; 是两个字段，但都在追踪&amp;quot;下一个要发的序号&amp;quot;，语义重复，而且 &lt;strong&gt;&lt;code&gt;seq&lt;/code&gt; 被推进了两次&lt;/strong&gt;（SYN/FIN 一次、数据一次）。&lt;/p&gt;
&lt;p&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;uint32_t&lt;/span&gt; saved_seq &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;seq;
&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;uint32_t&lt;/span&gt; saved_nxt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_nxt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;seq     &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_una;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_nxt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_una;
&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;tcp_send_raw&lt;/span&gt;(s, TCP_PSH &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; TCP_ACK, rtbuf, chunk);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_nxt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; saved_nxt;   &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;s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;seq     &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; saved_seq;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种&amp;quot;临时改状态再恢复&amp;quot;的方式容易出错，而且没有指数退避。&lt;/p&gt;</description>
    </item>
    <item>
      <title>从零写OS（三十七）：网络栈 —— e1000 驱动 &#43; ARP/IP/TCP &#43; Socket</title>
      <link>https://www.dafei.me/posts/os-37-network/</link>
      <pubDate>Fri, 22 May 2026 08:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-37-network/</guid>
      <description>&lt;p&gt;文件系统能读写了，这一章加网络支持。目标是实现一个最小 TCP/IP 栈，让用户程序能通过 socket 发送 HTTP 请求并收到响应。&lt;/p&gt;
&lt;p&gt;验证方式：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/ # wget_test
wget_test start
connecting...
connected!
request sent
HTTP/1.0 200 OK
...
DONE
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;架构概览&#34;&gt;架构概览&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;用户程序（wget_test）
  ↕ syscall: socket/connect/write/read/close
内核 socket 层（net.c）
  ↕ tcp_connect / tcp_send / tcp_recv / tcp_close
TCP 层（net.c）
  ↕ 以太网帧收发
ARP 层（net.c）
  ↕ e1000_send / e1000_recv
e1000 网卡驱动（e1000.c）
  ↕ MMIO + DMA
QEMU e1000 虚拟网卡（-netdev user,id=net0 -device e1000,netdev=net0）
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;pci-枚举&#34;&gt;PCI 枚举&lt;/h2&gt;
&lt;p&gt;e1000 通过 PCI 总线连接。内核在启动时枚举 PCI 设备：&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;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pci_enumerate&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:#66d9ef&#34;&gt;for&lt;/span&gt; (bus&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; bus&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;256&lt;/span&gt;; bus&lt;span style=&#34;color:#f92672&#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;for&lt;/span&gt; (dev&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; dev&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt;; dev&lt;span style=&#34;color:#f92672&#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;for&lt;/span&gt; (fn&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; fn&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;; fn&lt;span style=&#34;color:#f92672&#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;uint16_t&lt;/span&gt; vendor &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pci_read16&lt;/span&gt;(bus, dev, fn, &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:#66d9ef&#34;&gt;if&lt;/span&gt; (vendor &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xFFFF&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&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;// 记录 vendor/device/bar0
&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;e1000 的 &lt;code&gt;vendor=0x8086&lt;/code&gt;，&lt;code&gt;device=0x100E&lt;/code&gt;。BAR0 是 MMIO 基地址（通常 &lt;code&gt;0xFEBC0000&lt;/code&gt;）。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
