alloc_skb()用於分配緩衝區的函式。由於"資料緩衝區"和"緩衝區的描述結構"(sk_buff結構)是兩種不同的實體,這就意味著,在分配乙個緩衝區時,需要分配兩塊記憶體(乙個是緩衝區,乙個是緩衝區的描述結構sk_buff)。
首先看alloc_skb
static inline struct sk_buff *alloc_skb(unsigned int size,
gfp_t priority)
這個函式比較簡單,引數中的size不用解釋,為skb資料段的大小,但是第二個引數priority名字比較奇怪。叫優先順序,實際上則是gfp mask巨集,如gfp_kernel,gfp_atomic等。
__alloc_skb()呼叫kmem_cache_alloc()從快取中獲取乙個sk_buff結構,並呼叫kmalloc_track_caller分配緩衝區
接下來看__alloc_skb
/*引數:
size:skb的資料大小
gfp_mask:不用解釋
fclone:表示從哪個cache中分配
當fclone為1時,從skbuff_fclone_cache上分配
當fclone為0時,從skbuff_head_cache上分配
node: numa節點
*/struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
int fclone, int node)
out:
return skb;
nodata:
kmem_cache_free(cache, skb);
skb = null;
goto out;
}這裡有兩個cache,skbuff_fclone_cache和skbuff_head_cache。它們兩個的區別是前者是每兩個skb為一組。當從skbuff_fclone_cache分配skb時,會兩個連續的skb一起分配,但是釋放的時候可以分別釋放。也就是說當呼叫者知道需要兩個skb時,如後面的操作很可能使用skb_clone時,那麼從skbuff_fclone_cache上分配skb會更高效一些。
skb的分配細節
1. 關於 skb 的分配細節.
linux 中 skb 的分配最終是由函式 : struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,int fclone) 來完成.
skb 可以分為 skb 描述符與 skb 資料區兩個部分,其中描述符必須從 cache 中來分配 : 或者從skbuff_fclone_cache 中分配,或者從 skbuff_head_cache 中來分配.
如果從分配描述符失敗,則直接反回 null,表示 skb 分配失敗.
skb 描述符分配成功後,即可分配資料區.
在具體分配資料區之前首先要對資料區的長度進行 align 操作, 通過巨集 skb_data_align 來重新確定 size 大小. 然後呼叫 kmalloc 函式分配資料區 :
data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
需要注意的是資料區的大小是 size 的大小加上 skb_shared_info 結構的大小.
資料區分配成功後,便對 skb 描述符進行與此資料區相關的賦值操作 :
memset(skb, 0, offsetof(struct sk_buff, truesize));
skb->truesize = size + sizeof(struct sk_buff);
atomic_set(&skb->users, 1);
skb->head = data;
skb->data = data;
skb->tail = data;
skb->end = data + size;
需要主意的是, skb 的 truesize 的大小並不包含 skb_shared_info 結構的大小. 另外,skb 的 end 成員指標也就事 skb_shared_info 結構的起始指標,系統用
乙個巨集 : skb_shinfo 來完成尋找 skb_shared_info 結構指標的操作.
最後,系統初始化 skb_shared_info 結構的成員變數 :
atomic_set(&(skb_shinfo(skb)->dataref), 1);
skb_shinfo(skb)->nr_frags = 0;
skb_shinfo(skb)->tso_size = 0;
skb_shinfo(skb)->tso_segs = 0;
skb_shinfo(skb)->frag_list = null;
skb_shinfo(skb)->ufo_size = 0;
skb_shinfo(skb)->ip6_frag_id = 0;
最後,返回 skb 的指標.
2. skb 的分配時機
skb 的分配時機主要有兩種,最常見的一種是在網絡卡的中斷中,有資料報到達的時,系統分配 skb 包進行包處理; 第二種情況是主動分配 skb 包用於各種除錯或者其他處理環境.
3. skb 的 reserve 操作
skb 在分配的過程中使用了乙個小技巧 : 即在資料區中預留了 128 個位元組大小的空間作為協議頭使用, 通過移動 skb 的 data 與 tail 指標的位置來實現這個功能.
4. skb 的 put 操作
put 操作是 skb 中乙個非常頻繁也是非常重要的操作, 膽識, skb_put()函式其實什麼也沒做!
它只是根據資料的長度移動了 tail 指標並改寫了 skb->len 的值,其他的什麼都沒做,然後就返回了 skb->data 指標(就是 tail 指標在移動之前的位置). 看上去此函式彷彿要拷貝資料到 skb 的資料區中,其實這事兒是 insl 這個函式幹的,跟 skb_put() 函式毫不相關,不過它仍然很重要.
5. 中斷環境下 skb 的分配流程
當資料到達網絡卡後,會觸發網絡卡的中斷,從而進入 isr 中,系統會在 isr 中計算出此次接收到的資料的位元組數 : pkt_len, 然後呼叫 skb 分配函式來分配 skb :
skb = dev_alloc_skb(pkt_len+5);
我們可以看到, 實際上傳入的資料區的長度還要比實際接收到的位元組數多,這實際上是一種保護機制. 實際上,在 dev_alloc_skb 函式呼叫 __dev_alloc_skb 函式,而 __dev_alloc_skb 函式又呼叫 alloc_skb 函式 時,其資料區的大小又增加了 128 位元組, 這 128 位元組就事前面我們所說的 reserve 機制預留的 header 空間.
OSTaskCreate()函式分析
int8u ostaskcreate void task void pd void p arg,os stk ptos,int8u prio 函式返回乙個8位的整型數,呼叫該函式需要四個引數。第乙個引數乙個指標,也就是使用者 的首位址,在平時使用中我們把自己建立的任務的名字作為這個引數就可以了 第三...
getopt函式分析
函式getopt主要用於拆分命令列引數,用這個函式就不自己寫命令列引數解析程式了,以下 摘自tcpdump原始碼,對這個函式比較感興趣,故對此進行分析注釋,因水平實在不敢恭維,不足之處希望能一起 函式getopt 有三個引數,nargc,nargv就是命令列傳過來的argc和argv字串ostr,它...
uCOS OSTaskCreate()函式分析
int8u ostaskcreate void task void pd void p arg,os stk ptos,int8u prio 函式返回乙個8位的整型數,呼叫該函式需要四個引數。第乙個引數乙個指標,也就是使用者 的首位址,在平時使用中我們把自己建立的任務的名字作為這個引數就可以了 第三...