W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
我們現(xiàn)在已經(jīng)涵蓋到了大部分關(guān)于網(wǎng)絡(luò)接口的問題. 還缺乏的是對 sk_buff 結(jié)構(gòu)的詳細(xì)描述.這個結(jié)構(gòu)處于 Linux 內(nèi)核網(wǎng)絡(luò)子系統(tǒng)的核心, 我們現(xiàn)在介紹這個結(jié)構(gòu)的重要成員和操作它們的函數(shù).
盡管沒有嚴(yán)格要求去理解 sk_buff 的內(nèi)部, 能夠查看它的內(nèi)容的能力在你追蹤問題和試圖優(yōu)化代碼時是有幫助的. 例如, 如果你看 loopback.c, 你會發(fā)現(xiàn)一個基于對 sk_buff 內(nèi)部了解的優(yōu)化. 這里適用的通常的警告是: 如果你編寫利用 sk_buff 結(jié)構(gòu)的知識的代碼, 你應(yīng)當(dāng)準(zhǔn)備好在以后內(nèi)核發(fā)行中它壞掉. 仍然, 有時性能優(yōu)勢值得額外的維護(hù)開銷.
我們這里不會描述整個結(jié)構(gòu), 只是那些在驅(qū)動里可能用到的. 如果你想看到更多, 你可以查看 <linux/skbuff.h>, 那里定義了結(jié)構(gòu)和函數(shù)原型. 關(guān)于如何使用這些成員和函數(shù)的額外的細(xì)節(jié)可以通過搜索內(nèi)核源碼很容易獲取.
這里介紹的成員是驅(qū)動可能需要存取的. 以非特別的順序列出它們.
struct net_device *dev;
接收或發(fā)送這個緩存的設(shè)備
union { / ... / } h;union { / ... / } nh;union { /... /} mac;
指向報文中包含的各級的頭的指針. union 中的某個成員都是一個不同數(shù)據(jù)結(jié)構(gòu)類型的指針. h 含有傳輸層頭部指針(例如, struct tcphdr th); nh 包含網(wǎng)絡(luò)層頭部(例如 struct iphdr iph); 以及 mac 包含鏈路層頭部指針(例如 struct ethkr * ethernet).
如果你的驅(qū)動需要查看 TCP 報文的源和目的地址, 可以在 skb->h.th 中找到. 看頭文件來找到全部的可以這樣存取的頭部類型.
注意網(wǎng)絡(luò)驅(qū)動負(fù)責(zé)設(shè)置進(jìn)入報文的 mac 指針. 這個任務(wù)正常是由 eth_type_trans 處理, 但是 非以太網(wǎng)驅(qū)動不得不直接設(shè)置 skb->mac.raw, 如同"非以太網(wǎng)頭部"一節(jié)所示.
unsigned char head;unsigned char data;unsigned char tail;unsigned char end;
用來尋址報文中數(shù)據(jù)的指針. head 指向分配內(nèi)存的開始, data 是有效字節(jié)的開始(并且常常稍微比 head 大一些), tail 是有效字節(jié)的結(jié)尾, end 指向 tail 能夠到達(dá)的最大地址. 查看它的另一個方法是可用緩存空間是 skb->end - skb->head, 當(dāng)前使用的空間是 skb->tail - skb->data.
unsigned int len;unsigned int data_len;
len 是報文中全部數(shù)據(jù)的長度, 而 data_len 是報文存儲于單個片中的部分的長度. 除非使用發(fā)散/匯聚 I/O, data_len 成員的值為 0.
unsigned char ip_summed;
這個報文的校驗和策略. 由驅(qū)動在進(jìn)入報文上設(shè)置這個成員, 如在"報文接收"一節(jié)中描述的.
unsigned char pkt_type;
在遞送中使用的報文分類. 驅(qū)動負(fù)責(zé)設(shè)置它為 PACKET_HOST (報文是給自己的), PACKET_OTHERHOST (不, 這個報文不是給我的), PACKET_BROADCAST, 或者 PACKET_MULTICAST. 以太網(wǎng)驅(qū)動不顯式修改 pkt_type, 因為 eth_type_trans 為它們做.
shinfo(struct sk_buff *skb);unsigned int shinfo(skb)->nr_frags;skb_frag_t shinfo(skb)->frags;
由于性能的原因, 有些 skb 信息存儲于一個分開的結(jié)構(gòu)中, 它在內(nèi)存中緊接著 skb. 這個"shared info"(這樣命名是因為它可以在網(wǎng)絡(luò)代碼中多個 skb 拷貝中共享)必須通過 shinfo 宏定義來存取. 這個結(jié)構(gòu)中有幾個成員, 但是大部分超出本書的范圍. 我們在"發(fā)散/匯聚 I/O"一節(jié)中見過 nr_frags 和 frags.
在結(jié)構(gòu)中剩下的成員不是特別有趣. 它們用來維護(hù)緩存列表, 來統(tǒng)計 socket 擁有的緩存大小, 等等.
使用一個 sk_buff 結(jié)構(gòu)的網(wǎng)絡(luò)驅(qū)動利用正式接口函數(shù)來操作它. 許多函數(shù)操作一個 socket 緩存; 這里是最有趣的幾個:
struct sk_buff alloc_skb(unsigned int len, int priority);struct sk_buff dev_alloc_skb(unsigned int len);
分配一個緩存區(qū). alloc_skb 函數(shù)分配一個緩存并且將 skb->data 和 skb->tail 都初始化成 skb->head. dev_alloc_skb 函數(shù)是使用 GFP_ATOMIC 優(yōu)先級調(diào)用 alloc_skb 的快捷方法, 并且在 skb->head 和 skb->data 之間保留了一些空間. 這個數(shù)據(jù)空間用在網(wǎng)絡(luò)層之間的優(yōu)化, 驅(qū)動不要動它.
void kfree_skb(struct sk_buff skb);void dev_kfree_skb(struct sk_buff skb);void dev_kfree_skb_irq(struct sk_buff skb);void dev_kfree_skb_any(struct sk_buff skb);
釋放緩存. kfree_skb 調(diào)用由內(nèi)核在內(nèi)部使用. 一個驅(qū)動應(yīng)當(dāng)使用一種 dev_kfree_skb 的變體: 在非中斷上下文中使用 dev_kfree_skb, 在中斷上下文中使用 dev_kfree_skb_irq, 或者 dev_kfree_skb_any 在任何 2 種情況下.
unsigned char skb_put(struct sk_buff skb, int len);unsigned char __skb_put(struct sk_buff skb, int len);
更新 sk_buff 結(jié)構(gòu)中的 tail 和 len 成員; 它們用來增加數(shù)據(jù)到緩存的結(jié)尾, 每個函數(shù)的返回值是 skb->tail 的前一個值(換句話說, 它指向剛剛創(chuàng)建的數(shù)據(jù)空間). 驅(qū)動可以使用返回值通過引用 memcpy(skb_put(...), data, len) 來拷貝數(shù)據(jù)或者一個等同的東東. 兩個函數(shù)的區(qū)別在于 skb_put 檢查以確認(rèn)數(shù)據(jù)適合緩存, 而 __skb_put 省略這個檢查.
unsigned char skb_push(struct sk_buff skb, int len);unsigned char __skb_push(struct sk_buff skb, int len);
遞減 skb->data 和遞增 skb->len 的函數(shù). 它們與 skb_put 相似, 除了數(shù)據(jù)是添加到報文的開始而不是結(jié)尾. 返回值指向剛剛創(chuàng)建的數(shù)據(jù)空間. 這些函數(shù)用來在發(fā)送報文之前添加一個硬件頭部. 又一次, __skb_push 不同在它不檢查空間是否足夠.
int skb_tailroom(struct sk_buff *skb);
返回可以在緩存中放置數(shù)據(jù)的可用空間數(shù)量. 如果驅(qū)動放了多于它能持有的數(shù)據(jù)到緩存中, 系統(tǒng)傻掉. 盡管你可能反對說一個 printk 會足夠來標(biāo)識出這個錯誤, 內(nèi)存破壞對系統(tǒng)是非常有害的以至于開發(fā)者決定采取確定的動作. 實際中, 你不該需要檢查可用空間, 如果緩存被正確地分配了. 因為驅(qū)動常常在分配緩存前獲知報文的大小, 只有一個嚴(yán)重壞掉的驅(qū)動會在緩存中安放太多的數(shù)據(jù), 這樣出亂子就可當(dāng)作一個應(yīng)得的懲罰.
int skb_headroom(struct sk_buff *skb);
返回 data 前面的可用空間數(shù)量, 就是, 可以 "push" 給緩存多少字節(jié).
void skb_reserve(struct sk_buff *skb, int len);
遞增 data 和 tail. 這個函數(shù)可用來在填充數(shù)據(jù)前保留空間. 大部分以太網(wǎng)接口保留 2 個字節(jié)在報文的前面; 因此, IP 頭對齊到 16 字節(jié), 在 14 字節(jié)的以太網(wǎng)頭后面. snull 也這樣做, 盡管沒有在"報文接收"一節(jié)中展現(xiàn)這個指令以避免在那時引入過多概念.
unsigned char skb_pull(struct sk_buff skb, int len);
從報文的頭部去除數(shù)據(jù). 驅(qū)動不會需要使用這個函數(shù), 但是為完整而包含在這兒. 它遞減 skb->len 和遞增 skb->data; 這是硬件頭如何從進(jìn)入報文開始被剝離.
int skb_is_nonlinear(struct sk_buff *skb);
返回一個真值, 如果這個 skb 分離為多個片為發(fā)散/匯聚 I/O.
int skb_headlen(struct sk_buff *skb);
返回 skb 的第一個片的長度(由 skb->data 指著).
void kmap_skb_frag(skb_frag_t frag);void kunmap_skb_frag(void *vaddr);
如果你必須從內(nèi)核中的一個非線性 skb 直接存取片, 這些函數(shù)為你映射以及去映射它們. 使用一個原子性 kmap, 因此你不能一次映射多于一個片.
內(nèi)核定義了幾個其他的作用于 socket 緩存的函數(shù), 但是它們是打算用于高層網(wǎng)絡(luò)代碼, 驅(qū)動不需要它們.
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: