首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Linux 內核 TCP 發送路徑深度剖析:tcp_sendmsg 核心邏輯與錯誤碼映射

     編輯:Java 綜合教學

Linux 內核 tcp_sendmsg() 是 TCP 發送數據的核心入口。本文基於內核源碼(以 3.x/4.x 為例)逐段解讀發送流程、內存管理、阻塞/非阻塞處理、錯誤碼返回路徑,幫助排查「發送卡住、斷連、EAGAIN/EPIPE」等疑難雜症。

核心流程圖解

  1. 參數檢查與連接狀態等待:非阻塞檢查 MSG_DONTWAIT,等待連接建立(sk_stream_wait_connect),處理 ERESTARTSYS/EINTR/EPIPE/EAGAIN。
  2. 發送緩衝區與錯誤狀態檢查:sk->sk_err、SEND_SHUTDOWN 觸發 EPIPE。
  3. 數據分段拷貝到 sk_buff:
    • 優先填充寫隊列尾部 skb 剩餘空間(skb_tailroom)。
    • 不足則分配新 skb(sk_stream_alloc_skb),支持 CHECKSUM_PARTIAL 硬件校驗、SG (Scatter-Gather) 分頁模式。
    • 分頁模式下嘗試合併頁片(skb_can_coalesce),否則新增 page frag。
  4. 內存壓力處理:
    • sk_stream_memory_free / sk_wmem_schedule 檢查發送緩衝區/內存水位。
    • 不足時 wait_for_sndbuf/wait_for_memory 休眠等待,超時返回錯誤。
  5. 推送策略:
    • Nagle 算法、MSG_MORE、TCP_CORK 決定何時調用 tcp_push/__tcp_push_pending_frames 真正下發網卡。
  6. 返回值:成功返回已拷貝字節數(copied);失敗返回負錯誤碼(經 sk_stream_error 轉換)。

關鍵錯誤碼映射表

ERESTARTSYS (512)
錯誤碼觸發場景應用層表現排查方向
EPIPE (32)連接斷開/關閉寫端/對端 RSTsend() 返回 -1, errno=EPIPE; SIGPIPE 信號檢查對端是否崩潰/主動關閉、keepalive 設置
EAGAIN/EWOULDBLOCK (11)非阻塞發送緩衝區滿send() 返回 -1, errno=EAGAIN應用層需 epoll/poll 等待可寫、或調大 SO_SNDBUF
ENOMEM (12)內核內存不足分配 skb/page 失敗同 EAGAIN,但系統級內存壓力/proc/sys/net/ipv4/tcp_wmem、vm.min_free_kbytes
ECONNRESET (104)收到 RST連接重置對端異常關閉、防火牆截斷
ETIMEDOUT (110)重試超時無 ACK發送超時網絡丟包、對端不回 ACK、tcp_retries2
信號中斷休眠內部重試,通常不返應用層信號處理邏輯

源碼關鍵片段註釋

// 1. 非阻塞檢查
timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); // 非阻塞 timeo=0

// 2. 等待連接建立
if ((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
    if ((err = sk_stream_wait_connect(sk, &timeo)) != 0) goto out_err;

// 3. 發送緩衝區/錯誤檢查
if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) { err = -EPIPE; goto out_err; }

// 4. 循環拷貝 iovec
while (--iovlen >= 0) {
    // ... 分配/填充 skb ...
    if (!sk_stream_memory_free(sk)) goto wait_for_sndbuf;
    if (!sk_wmem_schedule(sk, copy)) goto wait_for_memory;
    // ... 拷貝數據 ...
    copied += copy;
    // ... 推送判斷 ...
}

// 5. 內存等待
wait_for_sndbuf: set_bit(SOCK_NOSPACE, ...);
wait_for_memory:
    if (copied) tcp_push(...); // 已有數據先推送
    if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) goto do_error;
    mss_now = tcp_send_mss(sk, &size_goal, flags); // 重新計算 MSS

實戰排查清單

  1. 發送端報 EAGAIN:增大 SO_SNDBUF、啟用 TCP_NOTSENT_LOWAT、應用層背壓控制。
  2. 發送端報 EPIPE:抓包確認是否收到 RST/FIN,排查對端崩潰、超時、防火牆。
  3. 發送延遲高:檢查 Nagle (TCP_NODELAY)、TCP_CORK、tcp_autocorking、網卡隊列長度。
  4. 內存洩漏/OOM:監控 /proc/net/sockstat、slabinfo 中 skbuff_head_cache、tcp_sendmsg 分配失敗計數。

讀懂 tcp_sendmsg,就能從內核視角精準定位 90% 的 TCP 發送異常。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved