sk_buff(簡稱 skb)是 Linux 核心網路子系統中最核心的數據結構,貫穿從驅動層到傳輸層的所有數據流轉。本文詳細解析 sk_buff 的結構佈局、指針語義以及常用操作函數的實現邏輯。
Linux 2.6.21 中 sk_buff 定義(include/linux/skbuff.h)包含以下關鍵字段:
struct sk_buff {
struct sk_buff *next; // 鏈表下一個
struct sk_buff *prev; // 鏈表上一個
struct sk_buff_head *list; // 所屬鏈表頭
struct sock *sk; // 關聯 socket(傳輸層)
struct timeval stamp; // 時間戳
atomic_t users; // 引用計數
// ...
}
struct net_device *dev; // 發送/接收設備
struct net_device *input_dev; // 接收設備
struct net_device *real_dev; // 真實物理設備(虛擬設備場景)
// 傳輸層頭部指針
union {
struct tcphdr *th;
struct udphdr *uh;
struct icmphdr *icmph;
// ...
} h;
// 網絡層頭部指針
union {
struct iphdr *iph;
struct ipv6hdr *ipv6h;
struct arphdr *arph;
} nh;
// 鏈路層頭部指針
union {
unsigned char *raw;
} mac;
unsigned char *head; // 分配緩衝區起始地址
unsigned char *data; // 有效數據起始地址(頭部空間預留)
unsigned char *tail; // 有效數據結束地址
unsigned char *end; // 分配緩衝區結束地址
unsigned int len; // 數據總長度 (data 到 tail)
unsigned int data_len; // 分片數據長度
unsigned int mac_len; // 鏈路層頭部長度
unsigned int truesize; // 實際佔用內存大小
內存佈局關係:
head ------------------------------------------- end | | | | | headroom | 數據區域 | tailroom | | | (data 到 tail) | | data ---------------------------- tail
| 函數 | 功能 | 關鍵行為 |
|---|---|---|
alloc_skb(size, gfp_mask) | 分配新 SKB | 分配 head 緩衝區,data=tail=head+headroom |
skb_clone(skb, gfp_mask) | 克隆 SKB | 共享 head/data/tail/end 緩衝區,增加 users 引用計數,標記 cloned=1 |
skb_copy(skb, gfp_mask) | 深度拷貝 | 分配新緩衝區,完整拷貝數據與元數據,獨立引用計數 |
kfree_skb(skb) | 釋放 SKB | 遞減 users,歸零時釋放緩衝區與結構體 |
// 尾部擴展:tail 向後移動 len,返回舊 tail 位置(用於填充數據)
unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
// 頭部擴展:data 向前移動 len,返回新 data 位置(用於添加頭部)
unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
// 頭部預留:data 和 tail 同時向後移動 len(預留頭部空間)
void skb_reserve(struct sk_buff *skb, unsigned int len)
// 尾部縮減:tail 向前移動 len
void skb_trim(struct sk_buff *skb, unsigned int len)
// 頭部縮減:data 向後移動 len(移除頭部)
unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)
int skb_headroom(const struct sk_buff *skb) // data - head:頭部可用空間
int skb_tailroom(const struct sk_buff *skb) // end - tail:尾部可用空間
skb_reserve(skb, NET_IP_ALIGN) 預留 2 字節對齊,DMA 填充數據區,skb_put(skb, len) 更新尾部,skb->protocol = eth_type_trans(skb, dev) 解析協議類型並移動 data 指針跳過 MAC 頭。headroom 供驅動添加硬件頭部,驅動調用 skb_push 添加封裝頭,最終調用 dev->hard_start_xmit。skb_shinfo(skb)->frags 管理分片頁面,data_len 記錄分片總長,len = head_len + data_len。char cb[40]; // 各層私有控制信息(跨層需 skb_clone)
unsigned char pkt_type; // PACKET_HOST/BROADCAST/MULTICAST/OTHERHOST
unsigned char ip_summed; // CHECKSUM_NONE/UNNECESSARY/COMPLETE/HARDWARE
__u32 priority; // QoS 優先級
unsigned short protocol; // 協議類型 (ETH_P_*)
// Netfilter 相關
unsigned long nfmark;
struct nf_conntrack *nfct;
struct nf_bridge_info *nf_bridge;
// 流控相關
#ifdef CONFIG_NET_SCHED
__u32 tc_index; // 流控分類索引
__u32 tc_verd; // 流控裁決
__u32 tc_classid; // 流控類別 ID
#endif
sk_buff 以靈活的指針佈局實現了零拷貝、分層解析、分片管理等核心能力。掌握 head/data/tail/end 四指針語義與 skb_put/push/pull/reserve 操作語義,是理解 Linux 網路子系統數據流轉的基礎。