Linux 本机网络通信机制深度解析:Loopback 设备原理

前言

在服务器开发中,许多高性能服务如 Redis、MySQL、Nginx 默认监听 127.0.0.1 进行本机通信。这种通信方式究竟与跨机通信有何不同?数据是否真的走了物理网卡?

本文将深入剖析 Linux 内核中 Loopback(回环)设备的工作原理,揭示本机网络通信 "左手倒右手" 的精妙设计。


第一章 跨机通信回顾

1.1 完整接收路径概览

在深入本机通信之前,我们需要回顾跨机通信的完整流程:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              跨机通信完整路径                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  阶段一:硬件接收                                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  网卡通过 DMA 将数据写入 Ring Buffer                  │   │
│  │       │                                              │   │
│  │       │ 硬中断 (IRQ)                                  │   │
│  │       ▼                                              │   │
│  │  igb_msix_ring() → napi_schedule()                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│  阶段二:软中断处理                                        │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  ksoftirqd 执行 net_rx_action()                     │   │
│  │       │                                              │   │
│  │       ├──► igb_poll() 从 RingBuffer 取包           │   │
│  │       │                                              │   │
│  │       └──► netif_receive_skb()                     │   │
│  │               │                                      │   │
│  │               ├──► ip_rcv() (IP层)                 │   │
│  │               └──► tcp_v4_rcv() (TCP层)           │   │
│  │                       │                              │   │
│  │                       ▼                              │   │
│  │               数据入队 sk_receive_queue              │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│  阶段三:用户态接收                                        │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  recvfrom() 被唤醒                                   │   │
│  │       │                                              │   │
│  │       └──► tcp_recvmsg()                            │   │
│  │               │                                      │   │
│  │               ├──► 从 Socket 接收队列取数据         │   │
│  │               └──► 拷贝到用户缓冲区                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2 跨机通信的核心开销

跨机通信涉及多个昂贵的操作:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              跨机通信的性能开销                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  开销类型              │  原因                      │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  DMA 内存映射          │  需要分配一致性内存         │   │
│  │  硬件中断 (IRQ)        │  网卡通知 CPU 有数据到达    │   │
│  │  上下文切换            │  用户态 → 内核态 → 用户态   │   │
│  │  PCIe 总线传输        │  数据在总线上来回搬运       │   │
│  │  物理信号传输          │  电信号/光信号在线路上传播  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第二章 本机通信的核心问题

2.1 一个经典的误解

当我们访问本机服务时,直觉上会有两种选择:

访问方式 代码示例 直觉判断
Loopback 地址 telnet 127.0.0.1 6379 走虚拟设备
本机局域网 IP telnet 192.168.1.100 6379 可能走物理网卡

然而事实是:两者都不走物理网卡,都会走虚拟的 Loopback 设备。

2.2 实验验证

有一个精彩的实验来证明这一点:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              tcpdump 实验验证                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  实验一:监听物理网卡                                        │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  $ tcpdump -i eth0                                    │   │
│  │  $ telnet 192.168.1.100 6379                         │   │
│  │                                                       │   │
│  │  结果:没有任何数据包                                  │   │
│  │  结论:数据没有经过物理网卡                           │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  实验二:监听 Loopback 设备                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  $ tcpdump -i lo                                      │   │
│  │  $ telnet 192.168.1.100 6379                         │   │
│  │                                                       │   │
│  │  结果:抓到了完整的 TCP 握手包                        │   │
│  │  结论:数据走了 Loopback 设备                       │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第三章 路由表的判决机制

3.1 路由查找的优先级

Linux 内核维护多张路由表,查找顺序如下:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              路由表查找顺序                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  fib_lookup() 执行顺序:                                    │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  1. RT_TABLE_LOCAL (本地路由表)                      │   │
│  │     优先级最高,匹配本机 IP 和 127.0.0.0/8          │   │
│  │          │                                            │   │
│  │          ▼                                            │   │
│  │     如果匹配 → res.type == RTN_LOCAL                │   │
│  │          │                                            │   │
│  │          ▼                                            │   │
│  │     dev_out = net->loopback_dev  // 强制指定 lo     │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  2. RT_TABLE_MAIN (主路由表)                        │   │
│  │     匹配外部目标,确定下一跳网关                      │   │
│  │     匹配成功 → dev_out = eth0 (物理网卡)            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.2 关键代码解析

复制代码
// __ip_route_output_key 函数核心逻辑
int ip_route_output_ports(...) 
{
    // 查找路由
    fib_lookup(&fl, &res);
    
    // 关键判决
    if (res.type == RTN_LOCAL) {
        // 目标为本机,强制使用 Loopback 设备
        dev_out = net->loopback_dev;
    } else {
        // 目标为外部,使用物理网卡
        dev_out = fib_dev(&res);
    }
}

核心结论 :无论目标是 127.0.0.1 还是本机配置的局域网 IP,只要内核判断目标是本机地址,都会强制使用 loopback_dev


第四章 Loopback 设备详解

4.1 Loopback 设备的本质

Loopback(lo)是一个纯软件实现的虚拟网卡:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              Loopback 设备 vs 物理网卡                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              物理网卡 (eth0)                         │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  有真实硬件 (网卡芯片)                              │   │
│  │  有 DMA 控制器                                     │   │
│  │  有 PCIe 总线连接                                  │   │
│  │  有物理中断 (IRQ)                                  │   │
│  │  需要驱动程序                                       │   │
│  │  数据发送到网线                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              虚拟网卡 (lo)                            │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  纯软件实现,无任何硬件                             │   │
│  │  无 DMA 控制器                                      │   │
│  │  无 PCIe 总线                                       │   │
│  │  无物理中断                                         │   │
│  │  简单的内核函数实现                                 │   │
│  │  "发送"即"接收"                                    │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.2 MTU 的巨大差异

复制代码
┌─────────────────────────────────────────────────────────────┐
│              MTU 对比                                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  物理网卡 MTU                                        │   │
│  │  通常为 1500 字节                                    │   │
│  │  受以太网标准限制                                    │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Loopback 设备 MTU                                   │   │
│  │  通常为 65535 字节                                   │   │
│  │  虚拟设备,无物理限制                                │   │
│  │  可以传输巨大的数据包而无需分片                       │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  影响:                                                     │
│  本机通信极少触发 IP 分片,除非发送超过 64KB 的单次请求      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第五章 Loopback 发送流程

5.1 物理网卡 vs Loopback 的发送对比

复制代码
┌─────────────────────────────────────────────────────────────┐
│              发送流程对比                                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  物理网卡发送 (igb_xmit_frame):                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  1. 获取 TX 描述符                                    │   │
│  │  2. DMA 映射 (dma_map_single)                       │   │
│  │  3. 填写描述符                                       │   │
│  │  4. 写寄存器"踢门铃"                                 │   │
│  │  5. 网卡通过 DMA 取走数据                           │   │
│  │  6. 网卡发送电信号到网线                             │   │
│  │  复杂度:极高                                       │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  Loopback 发送 (loopback_xmit):                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  1. skb_orphan(skb) 剥离 Socket 关联               │   │
│  │  2. netif_rx(skb) 直接进入接收流程                 │   │
│  │  复杂度:极低                                       │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.2 loopback_xmit 核心实现

复制代码
// Loopback 设备的发送函数
static netdev_tx_t loopback_xmit(struct sk_buff *skb,
                                   struct net_device *dev)
{
    // 步骤 1:剥离 Socket 关联
    // 节省开销,不再需要维护发送方的 Socket 状态
    skb_orphan(skb);
    
    // 步骤 2:直接进入接收流程
    // 没有硬件操作,直接调用网络核心的接收函数
    netif_rx(skb);
    
    return NETDEV_TX_OK;
}

关键点loopback_xmit 并没有真正 "发送" 任何东西,而是直接调用了接收函数。


第六章 软中断触发机制

6.1 netif_rx 的内部处理

loopback_xmit 调用 netif_rx() 后,内核执行以下操作:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              netif_rx 执行流程                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  netif_rx(struct sk_buff *skb)                            │
│       │                                                    │
│       ▼                                                    │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  local_bh_disable()                                 │   │
│  │  禁用本地软中断,防止嵌套处理                        │   │
│  └─────────────────────────────────────────────────────┘   │
│       │                                                    │
│       ▼                                                    │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  放入 CPU 输入队列                                   │   │
│  │  softnet_data.input_pkt_queue                      │   │
│  └─────────────────────────────────────────────────────┘   │
│       │                                                    │
│       ▼                                                    │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  触发软中断                                          │   │
│  │  __napi_schedule(&softnet_data.backlog)            │   │
│  │       │                                              │   │
│  │       ▼                                              │   │
│  │  __raise_softirq_irqoff(NET_RX_SOFTIRQ)            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

6.2 enqueue_to_backlog 函数解析

复制代码
static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
                               unsigned int *qtail)
{
    // 获取目标 CPU 的 softnet_data
    struct softnet_data *sd = &softnet_data[cpu];
    
    // 将 SKB 加入输入队列
    __skb_queue_tail(&sd->input_pkt_queue, skb);
    
    // 触发 NET_RX_SOFTIRQ 软中断
    // 通知内核:"有数据包需要处理"
    __napi_schedule(&sd->backlog);
    
    return NET_RX_SUCCESS;
}

6.3 软中断处理的特殊性

复制代码
┌─────────────────────────────────────────────────────────────┐
│              Loopback 的软中断处理                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  物理网卡场景:                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  硬中断 → 软中断 → net_rx_action()                  │   │
│  │       │              │                              │   │
│  │       │              ├──► 调用驱动 poll 函数        │   │
│  │       │              │    从 RingBuffer 取包         │   │
│  │       │              │                              │   │
│  │       │              └──► __netif_receive_skb()    │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  Loopback 场景:                                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  直接触发软中断 (无硬中断)                            │   │
│  │       │                                              │   │
│  │       │              │                              │   │
│  │       │              ├──► process_backlog()          │   │
│  │       │              │    从 input_pkt_queue 取包   │   │
│  │       │              │                              │   │
│  │       │              └──► __netif_receive_skb()    │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

6.4 process_backlog 函数

复制代码
static int process_backlog(struct napi_struct *napi, int quota)
{
    struct softnet_data *sd = container_of(napi, struct softnet_data,
                                            backlog);
    int work = 0;
    
    // 循环处理队列中的数据包
    while (work < quota) {
        struct sk_buff *skb;
        
        // 从输入队列取出数据包
        skb = __skb_dequeue(&sd->input_pkt_queue);
        if (!skb)
            break;
        
        // 送入协议栈处理
        // 后续流程与物理网卡完全相同
        netif_receive_skb(skb);
        
        work++;
    }
    
    return work;
}

第七章 完整数据流对比

7.1 跨机通信完整流程

复制代码
┌─────────────────────────────────────────────────────────────┐
│              跨机通信完整路径                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  发送方:                                                   │
│  应用 send() → TCP 层 → IP 层 → 路由查找 → 邻居 ARP        │
│       → QDisc → 驱动层 → DMA 映射 → 写网卡寄存器           │
│                                                             │
│  物理传输:                                                 │
│  网卡 DMA 取数据 → 串行化 → 电信号/光信号 → 发送至网线     │
│                                                             │
│  接收方:                                                   │
│  硬中断 (IRQ) → 软中断 → 驱动 Poll → IP 层 → TCP 层      │
│       → Socket 接收队列 → 用户 recv()                      │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  涉及:硬件中断、PCIe 总线、物理传输、上下文切换      │   │
│  │  延迟:微秒级到毫秒级                                │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

7.2 本机通信完整流程

复制代码
┌─────────────────────────────────────────────────────────────┐
│              本机通信完整路径                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  发送方:                                                   │
│  应用 send() → TCP 层 → IP 层 → 路由查找 (RTN_LOCAL)       │
│       → Loopback 设备 → loopback_xmit()                    │
│                                                             │
│  软中断触发:                                               │
│  netif_rx() → 入 input_pkt_queue → NET_RX_SOFTIRQ        │
│                                                             │
│  接收方:                                                   │
│  软中断处理 → process_backlog() → IP 层 → TCP 层          │
│       → Socket 接收队列 → 用户 recv()                      │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  涉及:纯软中断处理,无硬件交互                       │   │
│  │  延迟:亚微秒级                                      │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

7.3 对比总结

复制代码
┌─────────────────────────────────────────────────────────────┐
│              跨机通信 vs 本机通信                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              特性              │  跨机    │  本机    │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  硬件中断                     │   ✓      │    ✗     │   │
│  │  DMA 映射                     │   ✓      │    ✗     │   │
│  │  PCIe 总线传输                │   ✓      │    ✗     │   │
│  │  物理信号传输                 │   ✓      │    ✗     │   │
│  │  软中断处理                   │   ✓      │    ✓     │   │
│  │  IP 分片                      │ 可能发生  │ 极少发生  │   │
│  │  延迟                         │   高     │    低     │   │
│  │  吞吐量                       │   中     │    高     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第八章 "左手倒右手" 的本质

8.1 Loopback 的核心设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│              Loopback "左手倒右手"                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│           应用层发送                                         │
│               │                                              │
│               ▼                                              │
│  ┌───────────────────────────────┐                         │
│  │      协议栈处理               │                         │
│  │  TCP 头、IP 头封装           │                         │
│  └───────────────────────────────┘                         │
│               │                                              │
│               ▼                                              │
│  ┌───────────────────────────────┐                         │
│  │      路由判决                 │                         │
│  │  RT_TABLE_LOCAL → RTN_LOCAL  │                         │
│  │  dev_out = loopback_dev      │                         │
│  └───────────────────────────────┘                         │
│               │                                              │
│               ▼                                              │
│  ┌───────────────────────────────┐                         │
│  │      loopback_xmit()          │                         │
│  │  skb_orphan() 剥离 Socket     │                         │
│  │  netif_rx() 直接进接收       │                         │
│  └───────────────────────────────┘                         │
│               │                                              │
│               ▼                                              │
│  ┌───────────────────────────────┐                         │
│  │      input_pkt_queue         │  ◄── "左口袋"          │
│  │  数据包暂存队列               │                         │
│  └───────────────────────────────┘                         │
│               │                                              │
│               │ 触发 NET_RX_SOFTIRQ                         │
│               ▼                                              │
│  ┌───────────────────────────────┐                         │
│  │      process_backlog()       │                         │
│  │  从队列取出数据包             │                         │
│  └───────────────────────────────┘                         │
│               │                                              │
│               ▼                                              │
│  ┌───────────────────────────────┐                         │
│  │      协议栈接收               │                         │
│  │  IP 层 → TCP 层 → Socket 队列 │                         │
│  └───────────────────────────────┘                         │
│               │                                              │
│               ▼                                              │
│           应用层接收                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

8.2 skb_orphan 的意义

复制代码
// 为什么需要 skb_orphan()?
//
// 发送时的 SKB 与发送方 Socket 有关联:
// - skb->sk 指向发送方的 struct sock
// - 需要维护发送状态、序列号等信息
//
// 进入 Loopback 接收流程后:
// - SKB 不再需要发送方的状态
// - skb_orphan() 剥离这个关联
// - skb->sk = NULL
//
// 好处:
// - 节省内存,不再需要维护发送方状态
// - 简化处理,不需要追踪发送上下文

8.3 性能差异的本质原因

复制代码
┌─────────────────────────────────────────────────────────────┐
│              本机通信性能高的原因                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  省去了物理网卡的所有开销:                                  │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  1. 无 DMA 内存映射                                  │   │
│  │     不需要分配一致性内存                              │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  2. 无硬件中断                                       │   │
│  │     不需要 CPU 响应网卡 IRQ                          │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  3. 无 PCIe 总线传输                                 │   │
│  │     数据不经过总线                                   │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  4. 无物理信号传输                                   │   │
│  │     没有电信号/光信号的转换和传输                    │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  5. 无需 ARP 解析                                    │   │
│  │     本机通信不需要 MAC 地址解析                       │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  6. 大 MTU                                           │   │
│  │     65535 字节,极少分片                             │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  唯一开销:软中断处理                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第九章 统一性与差异性

9.1 协议栈的统一性

Linux 网络栈最精妙的设计之一是统一性:无论数据来自物理网卡还是 Loopback 设备,最终都走相同的协议栈代码:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              协议栈的统一处理                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  物理网卡接收:                                             │
│  igb_poll() → netif_receive_skb() → ip_rcv() → tcp_v4_rcv()
│                                                             │
│  Loopback 接收:                                           │
│  process_backlog() → netif_receive_skb() → ip_rcv() → tcp_v4_rcv()
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │       从 __netif_receive_skb() 开始,                 │   │
│  │       两条路径完全相同                                 │   │
│  │                                                         │   │
│  │       内核无需关心数据来自哪里                         │   │
│  │       统一走 IP 层 → TCP 层 → Socket                  │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

9.2 差异仅在入口

复制代码
┌─────────────────────────────────────────────────────────────┐
│              入口点的差异                                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              数据来源          │      入口函数        │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  物理网卡                    │  驱动 poll 函数      │   │
│  │  (igb_poll, e1000_poll)    │                      │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  VLAN 设备                  │  vlan_dev_poll       │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  Bonding 设备              │  bonding_poll        │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  Bridge 设备               │  br_handle_frame     │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  Loopback 设备             │  process_backlog     │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  Tunnel 设备               │  ip_tunnel_rcv       │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  共同点:最终都调用 netif_receive_skb() 进入协议栈         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

9.3 路由决定一切

复制代码
┌─────────────────────────────────────────────────────────────┐
│              路由是分流的起点                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ip_route_output_ports() 决定数据包的出口设备:             │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │       目的地址匹配 RT_TABLE_LOCAL                    │   │
│  │               │                                        │   │
│  │               ▼                                        │   │
│  │       res.type == RTN_LOCAL                          │   │
│  │               │                                        │   │
│  │               ▼                                        │   │
│  │       dev_out = net->loopback_dev  ──► Loopback 设备  │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │       目的地址匹配 RT_TABLE_MAIN                     │   │
│  │               │                                        │   │
│  │               ▼                                        │   │
│  │       fib_dev(res)  ──► 物理网卡 (eth0)              │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第十章 实际应用场景

10.1 为什么 Redis 默认监听 127.0.0.1

复制代码
┌─────────────────────────────────────────────────────────────┐
│              Redis 本机通信优化                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  配置:                                                     │
│  bind 127.0.0.1                                            │
│                                                             │
│  优势:                                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  1. 极致性能                                        │   │
│  │     省去所有物理层开销                              │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  2. 安全隔离                                        │   │
│  │     仅本机进程可访问,外部无法连接                   │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  3. 简化网络配置                                    │   │
│  │     不依赖物理网卡状态                              │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  注意:                                                     │
│  - 同一台机器上的应用服务(如 PHP-FPM、Nginx)访问 Redis    │
│    使用 127.0.0.1 可获得最高性能                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

10.2 本机通信的延迟测量

复制代码
┌─────────────────────────────────────────────────────────────┐
│              典型延迟对比                                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  通信类型              │  典型延迟                  │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  Loopback (127.0.0.1)  │  0.5 - 2 微秒            │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  本机物理网卡          │  5 - 20 微秒              │   │
│  │  (同一子网)            │                          │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  跨路由器               │  1 - 10 毫秒              │   │
│  │  (同城网络)            │                          │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  跨地域                 │  20 - 200 毫秒           │   │
│  │  (不同城市)            │                          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  结论:Loopback 比物理网卡快 10-100 倍                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

10.3 连接本机服务的正确方式

复制代码
┌─────────────────────────────────────────────────────────────┐
│              应用层访问方式选择                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  推荐做法:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │  # Redis / MySQL / Memcached                        │   │
│  │  # 配置监听 127.0.0.1                                │   │
│  │  bind 127.0.0.1                                     │   │
│  │                                                         │   │
│  │  # 客户端连接字符串                                   │   │
│  │  redis-cli -h 127.0.0.1 -p 6379                     │   │
│  │                                                         │   │
│  │  # 或使用 localhost                                  │   │
│  │  redis-cli -h localhost                             │   │
│  │  # /etc/hosts 中 localhost 解析为 127.0.0.1          │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  避免做法:                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │  # 不要使用机器的公网/内网 IP                        │   │
│  │  redis-cli -h 192.168.1.100 -p 6379                │   │
│  │                                                         │   │
│  │  原因:                                                │   │
│  │  - 虽然最终也走 Loopback,但多了路由判决的开销        │   │
│  │  - 需要确保防火墙允许该端口                            │   │
│  │  - 可能有安全风险                                      │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

0voice · GitHub

相关推荐
難釋懷1 小时前
Redis网络模型-用户空间和内核态空间
网络·arm开发·redis
heimeiyingwang1 小时前
【架构实战】VPC网络与跨域通信:构建安全可控的云上网络
网络·安全·架构
wangl_921 小时前
Modbus RTU 与 Modbus TCP 深入指南-安全加固方案
网络·网络协议·tcp/ip·安全·tcp·modbus·rtu
一只小bit1 小时前
Docker 镜像制作:包含自定义镜像及常用命令
运维·docker·容器
网络工程小王1 小时前
【LangGraph 子图(Subgraph)详解】学习笔记
java·服务器·数据库·人工智能·langchain
源远流长jerry2 小时前
Linux 网络性能优化:从应用到内核
linux·运维·服务器·网络·网络协议·性能优化
goyeer2 小时前
【ITIL】指导原则
linux·运维·服务器·数字化·itil
顶点多余2 小时前
自定义协议、序列化、反序列化实现
java·linux·开发语言·c++·tcp/ip
Bruce_kaizy2 小时前
c++ linux环境编程——从应用层到linux内核深入了解文件io的调用机制(爆肝)
linux·c++·c·嵌入式linux·文件io