---
title: TCP/IP 协议栈
url: https://doc.liz6.com/linux-kernel/06-network-subsystem/02-TCP-IP
locale: zh
area: linux-kernel
tags:
- linux-kernel
- 网络子系统
date: 2026-06-30
modified: 2026-07-11
description: '覆盖: IPv4/IPv6 收发路径 → TCP 状态机 → 拥塞控制 (CUBIC/BBR) → 流控 (滑动窗口) → socket buffer (sk_wmem/sk_rmem) → TCP fast path 内核版本: 2.6 ~ 6.x'
---

# TCP/IP 协议栈

> 覆盖: IPv4/IPv6 收发路径 → TCP 状态机 → 拥塞控制 (CUBIC/BBR) → 流控 (滑动窗口) → socket buffer (sk_wmem/sk_rmem) → TCP fast path
> 内核版本: 2.6 ~ 6.x

## 概述

Linux TCP/IP 栈是内核中优化最极致的子系统之一。在高速路径 (established sockets, 顺序到达的数据包)，每个包的处理只有几十条指令。本文将聚焦 TCP 的接收快速路径和拥塞控制。

## IPv4 收发

### ip_rcv → ip_local_deliver (收包)

```c
// net/ipv4/ip_input.c
ip_rcv()
  ├─ 验证: version==4, checksum OK, 长度合法
  ├─ netfilter NF_INET_PRE_ROUTING (iptables raw/mangle)
  └─ ip_rcv_finish()
      ├─ 查路由表: fib_lookup() → dst_entry
      │   → RTN_LOCAL: 给本机的 → ip_local_deliver()
      │   → RTN_UNICAST: 转发 → ip_forward()
      └─ ip_local_deliver()
          ├─ netfilter NF_INET_LOCAL_IN (iptables filter)
          └─ ip_local_deliver_finish()
              └─ ipprot->handler → tcp_v4_rcv() / udp_rcv()
```

### ip_queue_xmit (发包)

```c
// net/ipv4/ip_output.c
__ip_queue_xmit(sk)
  ├─ 查路由: ip_route_output_ports() → dst_entry + 源 IP
  ├─ 构造 IP header: protocol, TTL, DF flag, ... 
  ├─ netfilter NF_INET_LOCAL_OUT
  └─ ip_local_out()
      ├─ netfilter NF_INET_POST_ROUTING
      └─ dev_queue_xmit() → QoS qdisc → 网卡驱动 ndo_start_xmit()
```

## TCP 快速路径: tcp_rcv_established

```c
// net/ipv4/tcp_input.c
// 这是 TCP 收包的最热路径 (established socket, 顺序包)

tcp_rcv_established(sk, skb)
  // fast path (90%+ 的包走这里):
  ├─ 检查 header prediction:
  │   ├─ PSH bit? → 跳过
  │   ├─ 顺序到达? (seq == rcv_nxt) ✓
  │   ├─ 窗口非零? ✓
  │   └─ URG/RST/SYN? → 跳过
  │
  ├─ 拷贝数据到 receive queue: tcp_queue_rcv()
  │   └─ skb_copy_datagram_msg() → 直接到用户缓冲区? (如果是 MSG_DONTWAIT)
  │
  ├─ 更新 rcv_nxt, 窗口通告
  │
  └─ 如果数据已入 queue → sk_data_ready() → 唤醒 epoll/read

  // slow path:
  └─ OOO (乱序) / SACK / 窗口满 / FIN / RST → tcp_validate_incoming()
```

## TCP 状态机

<svg viewBox="0 0 720 420" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="TCP 状态机:从三次握手到主动/被动关闭">
  <defs>
    <marker id="tcpFsmArrow" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker>
  </defs>
  <rect width="720" height="420" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">TCP 状态机:从三次握手到主动/被动关闭</text>

  <!-- row1: 建连路径 -->
  <rect x="20" y="54" width="90" height="36" rx="8" fill="#e2e8f0"/>
  <text x="65" y="77" text-anchor="middle" font-size="12" font-weight="700" fill="#334155">CLOSED</text>
  <rect x="140" y="54" width="100" height="36" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="190" y="77" text-anchor="middle" font-size="12" font-weight="700" fill="#3730a3">LISTEN</text>
  <rect x="270" y="54" width="110" height="36" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="325" y="77" text-anchor="middle" font-size="12" font-weight="700" fill="#3730a3">SYN_RECV</text>
  <rect x="410" y="54" width="150" height="36" rx="8" fill="#4f46e5"/>
  <text x="485" y="77" text-anchor="middle" font-size="12" font-weight="700" fill="#ffffff">ESTABLISHED</text>

  <line x1="110" y1="72" x2="137" y2="72" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>
  <text x="125" y="46" text-anchor="middle" font-size="10.5" fill="#64748b">bind + listen</text>
  <line x1="240" y1="72" x2="267" y2="72" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>
  <text x="255" y="46" text-anchor="middle" font-size="10.5" fill="#64748b">accept</text>
  <line x1="380" y1="72" x2="407" y2="72" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>
  <text x="395" y="46" text-anchor="middle" font-size="10.5" fill="#64748b">SYN</text>

  <!-- ESTABLISHED 分两路 -->
  <line x1="485" y1="90" x2="310" y2="147" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>
  <text x="395" y="118" text-anchor="middle" font-size="10.5" fill="#64748b">active close (FIN)</text>
  <line x1="485" y1="90" x2="617" y2="147" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>

  <!-- row2: 关闭分支 -->
  <rect x="250" y="150" width="110" height="36" rx="8" fill="#ffedd5"/>
  <text x="305" y="173" text-anchor="middle" font-size="12" font-weight="700" fill="#9a3412">FIN_WAIT1</text>
  <rect x="390" y="150" width="110" height="36" rx="8" fill="#ffedd5"/>
  <text x="445" y="173" text-anchor="middle" font-size="12" font-weight="700" fill="#9a3412">FIN_WAIT2</text>
  <rect x="540" y="150" width="160" height="44" rx="8" fill="#ffedd5"/>
  <text x="620" y="169" text-anchor="middle" font-size="12" font-weight="700" fill="#9a3412">CLOSE_WAIT</text>
  <text x="620" y="184" text-anchor="middle" font-size="9.5" fill="#c2410c">(对方关了, 本地还没关)</text>

  <line x1="360" y1="168" x2="387" y2="168" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>
  <text x="373" y="160" text-anchor="middle" font-size="10.5" fill="#64748b">ACK</text>

  <!-- row3: 收尾 -->
  <rect x="250" y="230" width="110" height="36" rx="8" fill="#ffedd5"/>
  <text x="305" y="253" text-anchor="middle" font-size="12" font-weight="700" fill="#9a3412">CLOSING</text>
  <rect x="390" y="230" width="140" height="36" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="460" y="253" text-anchor="middle" font-size="12" font-weight="700" fill="#115e59">TIME_WAIT (2MSL)</text>
  <rect x="580" y="230" width="110" height="36" rx="8" fill="#ffedd5"/>
  <text x="635" y="253" text-anchor="middle" font-size="12" font-weight="700" fill="#9a3412">LAST_ACK</text>

  <line x1="305" y1="186" x2="305" y2="227" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>
  <line x1="445" y1="186" x2="459" y2="227" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>
  <line x1="360" y1="248" x2="387" y2="248" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>
  <line x1="620" y1="194" x2="635" y2="227" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>

  <!-- row4: 被动关闭终态 -->
  <rect x="580" y="310" width="110" height="36" rx="8" fill="#e2e8f0"/>
  <text x="635" y="333" text-anchor="middle" font-size="12" font-weight="700" fill="#334155">CLOSED</text>
  <line x1="635" y1="266" x2="635" y2="307" stroke="#475569" stroke-width="1.6" marker-end="url(#tcpFsmArrow)"/>

  <rect x="20" y="356" width="680" height="50" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="36" y="376" font-size="12.5" fill="#3730a3">ESTABLISHED 之后两条路:主动关闭走 FIN_WAIT1→FIN_WAIT2→TIME_WAIT(2MSL),或同时关闭走 CLOSING;</text>
  <text x="36" y="396" font-size="12.5" fill="#3730a3">被动关闭(对方先发 FIN)走 CLOSE_WAIT→LAST_ACK→CLOSED。</text>
</svg>

## 拥塞控制

```c
// net/ipv4/tcp_cong.c
// 可插拔的拥塞控制模块:
struct tcp_congestion_ops {
    void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked);
    void (*ssthresh)(struct sock *sk);    // 丢包时重设慢启动门限
    u32  (*undo_cwnd)(struct sock *sk);   // 虚假重传检测 → 回滚 cwnd
};

// CUBIC (默认):
//   cwnd = C * (t - K)^3 + W_max
//   K = cube_root(W_max * beta / C)
//   优点: 快速恢复, 公平, 在高速链路上表现好

// BBR (Bottleneck Bandwidth and RTT, Google):
//   不基于丢包，基于 BDP (Bandwidth-Delay Product)
//   测量: max BW (最近 10 轮) 和 min RTT (最近 10 秒)
//   目标: 保持 inflight ≈ BDP
//   优点: 在 bufferbloated 链路上远优于 CUBIC
```

## TCP Socket Buffer 调优

```bash
# 发送缓冲区 (默认 16KB, 自动增长到 max)
cat /proc/sys/net/ipv4/tcp_wmem  # min default max
# 接收缓冲区
cat /proc/sys/net/ipv4/tcp_rmem

# 自动调优 (默认开启):
#   sk_wmem 和 sk_rmem 根据 RTT 和带宽探测自动增长
#   → 高 BDP 链路不需要手动设置大 buffer
```

## 调试

```bash
# TCP 连接状态
ss -tianp   # 含 cwnd, rtt, rcv_mss, 所有统计

# 拥塞控制
sysctl net.ipv4.tcp_congestion_control
cat /proc/sys/net/ipv4/tcp_available_congestion_control

# 丢包/重传统计
nstat -a | grep -E 'TcpRetrans|TcpExt'
cat /proc/net/snmp | grep Tcp

# 追踪 TCP 状态
bpftrace -e 'kprobe:tcp_set_state { printf("%s: %d -> %d\n", comm, arg1, arg2); }'
```

## 参考

- **源码**: `net/ipv4/tcp_input.c`, `net/ipv4/tcp_output.c`, `net/ipv4/tcp_cong.c`, `net/ipv4/tcp_cubic.c`, `net/ipv4/tcp_bbr.c`
- **RFC**: RFC 793 (TCP), RFC 5681 (Congestion Control), RFC 8312 (CUBIC)
- **LWN**: "TCP fast path", "BBR congestion control"

*关键词: tcp_rcv_established, fast path, CUBIC, BBR, congestion control, socket buffer, TCP state machine*
