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