这篇文章上次修改于 765 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

1 网络模型

TCP/IP 模型:

  • 应用层,负责向用户提供一组应用程序,如 HTTP、FTP、DNS 等。
  • 传输层,负责端到端的通信,如 TCP、UDP 等。
  • 网络层,负责网络包的封装、寻址和路由,如 IP、ICMP 等。
  • 网络接口层,负责网络包在物理网络中的传输,如 MAC 寻址,错误侦测和通过网卡传输网络帧等。

2 Linux 网络栈

进行网络传输时,会按照协议栈对上层传过来的数据包封装上本层的头,然后传递给下一层:

  • 传输层会在应用数据前添加 TCP 头。
  • 网络层在 TCP 数据包前添加 IP 头。
  • 网络接口层在 IP 数据包前后添加帧头和帧尾。

MTU 为网络接口配置的最大传输单元,规定了最大 IP 包大小,超过该大小会被分片。

Linux 网络栈:

  • 最上层的应用需要通过系统调用与套接字接口交互。
  • 套接字下面,是传输层、网络层和网络接口层。
  • 最底层,是网卡驱动和物理网卡设备。

3 Linux 网络包收发流程

3.1 网络包接收流程

  • 当网络帧到达网卡后,网卡通过 DMA 方式将网络包放到收包队列中。然后通过硬中断通知中断处理程序收到了网络包。
  • 网络中断处理程序会为网络帧分配内核数据结构(sk_buff),并将其拷贝到 sk_buff 缓冲区中。然后通过软中断通知内核收到了新的网络帧。
  • 网络协议栈从缓冲区中取出网络帧,并通过网络协议栈从下到逐层处理:内核从缓冲区中取出网络帧,并通过网络协议栈从下到逐层处理:

    • 链路层检查报文的合法性,找出上层协议的类型(IPV4 或 IPV6),然后去掉帧头和帧尾,交给网络层。
    • 网络层取出 IP 头,找出上层协议类型(UDP 或 TCP),判断是需要转发还是还是交给上层处理。如果是交给上层处理,则去掉 IP 头,交给传输层处理。
    • 传输层取出 TCP 或 UDP 头后,根据 <源 IP,源端口,目的 IP,目的端口> 找到对应的 Socket,并将数据拷贝到 Socket 的接收缓存中。

3.2 网络包发送流程

  • 应用程序调用 Socket API 发送网络包。该系统调用会陷入到内核态的套接字层中。套接字层会将数据包放到 Socket 的发送缓冲区中。
  • 网络协议栈从 Socket 发送缓冲区中取出数据包,再按照 TCP/IP 协议栈从上到下逐层处理。比如

    • 传输层和网络层分别为数据包添加 TCP 头和 IP 头,执行路由查找确认下一跳的 IP,并按照 MTU 大小进行分片。
    • 分片后的网络包送到网络接口层,进行 MAC 寻址,然后添加帧头和帧尾,放到发包队列中。
  • 会有软中断通知驱动程序,发包队列中有新的网络包需要发送。
  • 驱动程序通过 DMA,从发包队列读出网络帧,并通过物理网卡发送出去。

4 性能指标

  • 带宽,链路最大传输速率,单位通常为 b/s
  • 吞吐量,单位时间内成功传输的数据量,单位通常为 b/s 或 B/s
  • 延迟,从网络请求发出后,到收到远端响应的时间。
  • PPS,以网络包为单位的传输速率。通常用来评估网络的转发能力。
  • 其他,如网络可用性、并发连接数、丢包率、重传率。

5 网络配置

$ ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
      inet 10.240.0.30 netmask 255.240.0.0 broadcast 10.255.255.255
      inet6 fe80::20d:3aff:fe07:cf2a prefixlen 64 scopeid 0x20<link>
      ether 78:0d:3a:07:cf:3a txqueuelen 1000 (Ethernet)
      RX packets 40809142 bytes 9542369803 (9.5 GB)
      RX errors 0 dropped 0 overruns 0 frame 0
      TX packets 32637401 bytes 4815573306 (4.8 GB)
      TX errors 0 dropped 0 overruns 0 carrier 0 collisions 
  • 网络接口状态标志。RUNNING 表示网络是连通的,即网卡已经查到了交换机或路由器中。
  • MTU 大小,默认是 1500。
  • 网络的 IP 地址、子网及 MAC 地址。
  • 网络的收发字节数、包数、错误数及丢包情况。RX、TX 的 errors、dropped、overruns、carrier、collisions 等不为 0 时,通常表示出现了网络 I/O 问题。

    • erros,表示发生错误包数,如校验错误、帧同步错误等。
    • dropped,表示丢弃的网络包数,即数据已经收到了 Ring Buffer 中,但因内存不足等原因丢弃。
    • overruns,表示超限网络包数,即网络 I/O 过快,Ring Buffer 中的数据包来不及处理而导致的丢包。
    • carrier,如因双工模式不匹配,物理电缆出现问题等。
    • collisions,碰撞包数。

6 套接字信息

ss 比 netstat 速度更快。

# head -n 3 表示只显示前面3行
# -l 表示只显示监听套接字
# -n 表示显示数字地址和端口(而不是名字)
# -p 表示显示进程信息
$ netstat -nlp | head -n 3
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      840/systemd-resolve

# -l 表示只显示监听套接字
# -t 表示只显示 TCP 套接字
# -n 表示显示数字地址和端口(而不是名字)
# -p 表示显示进程信息
$ ss -ltnp | head -n 3
State    Recv-Q    Send-Q        Local Address:Port        Peer Address:Port
LISTEN   0         128           127.0.0.53%lo:53               0.0.0.0:*        users:(("systemd-resolve",pid=840,fd=13))
LISTEN   0         128                 0.0.0.0:22               0.0.0.0:*        users:(("sshd",pid=1459,fd=3))

当套接字处于 Established 状态时:

  • Recv-Q 表示接收队列长度。套接字缓冲区中还没有被应用程序取走的字节数。
  • Send-Q 表示发送队列长度。还没有被远端主机确认的字节数。

当套接字处于 Listening 状态时:

  • Recv-Q 表示全连接队列长度。完成 TCP 三次握手的连接数。
  • Send-Q 表示全连接队列最大长度。

7 协议栈统计信息

$ netstat -s
...
Tcp:
    3244906 active connection openings
    23143 passive connection openings
    115732 failed connection attempts
    2964 connection resets received
    1 connections established
    13025010 segments received
    17606946 segments sent out
    44438 segments retransmitted
    42 bad segments received
    5315 resets sent
    InCsumErrors: 42
...

$ ss -s
Total: 186 (kernel 1446)
TCP:   4 (estab 1, closed 0, orphaned 0, synrecv 0, timewait 0/0), ports 0

Transport Total     IP        IPv6
*    1446      -         -
RAW    2         1         1
UDP    2         2         0
TCP    4         3         1
...

ss 只提供了已连接、关闭、孤儿套接字信息。

netstat 还提供了主动连接、被动连接、失败重试、发送和接收的分段数量等信息。

8 网络吞吐和 PPS

# 数字1表示每隔1秒输出一组数据
$ sar -n DEV 1
Linux 4.15.0-1035-azure (ubuntu)   01/06/19   _x86_64_  (2 CPU)

13:21:40        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
13:21:41         eth0     18.00     20.00      5.79      4.25      0.00      0.00      0.00      0.00
13:21:41      docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
13:21:41           lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
  • rxpck/s、txpck/s 分别为接收、发送 PPS,单位为 包/s
  • txcmp/s、rxcmp/s 分别为发送、接收的压缩数据包数,单位为包/s
  • %ftuil 为网口利用率,单工模式下为(rxkB/s + txkB/s) / Bandwith,全双工模式下为 max(rxkB/s, txkB/s) / Bandwith

查看 Bandwith:

$ ethtool eth0 | grep Speed
  Speed: 1000Mb/s

9 连通性和延迟

# -c3表示发送三次ICMP包后停止
$ ping -c3 114.114.114.114
PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.
64 bytes from 114.114.114.114: icmp_seq=1 ttl=54 time=244 ms
64 bytes from 114.114.114.114: icmp_seq=2 ttl=47 time=244 ms
64 bytes from 114.114.114.114: icmp_seq=3 ttl=67 time=244 ms

--- 114.114.114.114 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 244.023/244.070/244.105/0.034 ms

包括 ICMP 序列号,TTL(生存时间或跳数)和往返延时。

参考

倪朋飞. Linux 性能优化实战.