sk buff整理筆記(三 記憶體申請和釋放)

2021-06-23 08:19:36 字數 4411 閱讀 5608

第一、sk_buff結構的記憶體申請:

static inline struct sk_buff *alloc_skb(unsigned int size,

gfp_t priority)

static inline struct sk_buff *alloc_skb_fclone(unsigned int size,

gfp_t priority)

struct sk_buff *dev_alloc_skb(unsigned int length)

static inline struct sk_buff *__dev_alloc_skb(unsigned int length,

gfp_t gfp_mask)

這幾個函式都是在linux-2.6.32.63\include\linux\sk_buff.h檔案中的,其實就函式也可以看得出這是內聯函式。不記得在前面講過沒,對於簡潔常用的函式一般都定義為內聯函式,這樣做是為了提高cpu工作效率和記憶體利用率。一般的函式呼叫要儲存現場(在堆疊中儲存呼叫函式時現場狀態,包括位址,執行狀態等);當呼叫函式完後,又要恢復現場狀態(把開始儲存的狀態資料從堆疊中讀取出來)。在呼叫函式和返回時,浪費了cpu很多時間,再個在儲存時也會出現些碎片。所以再使用簡潔函式時,一般定義為內聯函式,在函式前面加個inline關鍵字。

講了內聯函式細節後,來看下上面這幾個記憶體申請函式。發現這幾個函式其實本質上都是封裝了 __alloc_skb();函式,那麼首先就要來分析下__alloc_skb()函式,然後再去分析下這幾個函式的異同點,以方便在申請sk_buff結構時,該用哪個函式去申請。

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;

}

首先來看下函式引數:第乙個引數 unsigned int size,資料區的大小;第二個引數 gfp_t gfp_mask,有些blog說這是優先順序,個人覺得是不對的。我們都知道核心動態分配函式kmalloc()裡面要有兩個引數,第乙個是要分配的空間大小,第二個是記憶體分配方式(這裡我們一般用gfp_kernel)。所以這裡的gfp_t gfp_mask應該是核心動態分配方式的乙個掩碼(就是各種申請方式的集合);第三個引數 int fclone,表示在哪塊分配器上分配;第四個引數 int node,用來表示用哪種區域去分配空間。

第9行**:cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;由傳入的引數來決定用哪個緩衝區中的記憶體來分配(一般是用skbuff_head_cache快取池來分配)。說到這裡就插入的講下快取池的概念。

核心對於sk_buff結構的記憶體分配不是和一般的結構動態記憶體申請一樣:只分配指定大小的記憶體空間。而是在開始的時候,在初始化函式skb_init()中就分配了兩段記憶體(skbuff_head_cache和skbuff_fclone_cache )來供sk_buff後期申請時用,所以後期要為sk_buff結構動態申請記憶體時,都會從這兩段記憶體中來申請(其實這不叫申請了,因為這兩段記憶體開始就申請好了的,只是根據你要的記憶體大小從某個你選定的記憶體段中還回個指標給你罷了)。如果在這個記憶體段中申請失敗,則再用核心中用最低層,最基本的kmalloc()來申請記憶體了(這才是真正的申請)。釋放時也一樣,並不會真正的釋放,只是把資料清零,然後放回記憶體段中,以供下次sk_buff結構的申請。這是核心動態申請的一種策略,專門為那些經常要申請和釋放的結構設計的,這種策略不僅可以提高申請和釋放時的效率,而且還可以減少記憶體碎片的。(注意:上面提到的記憶體段中的段不是指記憶體管理中的段、頁的段,而是表示塊,就是兩塊比較大的記憶體)

第13行**:skb = kmem_cache_alloc_node(cache, gfp_mask & ~__gfp_dma, node);從指定段中為skb分配記憶體,分配的方式是去除在dma記憶體中分配,因為dma記憶體比較小,且有特定的作用,一般不用來分配skb。

第17行**是調整sk_buff結構指向的資料區的大小。

第19行**:data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),

gfp_mask, node);這也是關鍵性**之一(這裡和上面分配skb的分配方式不一樣,這裡允許有些資料可以用dma記憶體來分配)。這也是從特殊的快取池中分配記憶體的,如果看函式裡面的引數不難發現,要分配的空間大小為:size + sizeof(struct skb_shared_info),前面的size是指skb結構體指向的資料區大小,而sizeof(struct skb_shared_info)則是為分片資料分配空間。因為分片結構就是在skb結構指向的資料區的下面,就是end指標的下乙個位元組,所以就一起分配。

第29行到42行**則是為sk_buff的資料區初始化,第44行到第53行則是為分片結構進行初始化。

第56行**就好理解了:用child結構體變數來指向第二sk_buff結構體記憶體位址。第57行**就是獲取到引用計數器,因為引用計數器是在第二個sk_buff結構體內存下乙個位元組空間開始的,所以用(child + 1)來獲取到引用計數器的開始位址。後面的**就比較好理解了,無非就是些設定性引數了。

好了,基本函式__alloc_skb()已經分析過了,那麼現在來看下開始的那幾個函式的異同點吧。

alloc_skb():是用來分配單純的sk_buff結構記憶體的,一般都是使用這個;alloc_skb_fclone():這是用來分配轉殖sk_buff結構的,因為這個分配函式會分配乙個子skb用來後期轉殖使用,所以如果能預見要轉殖skb_buff結構,則使用這種方法會方便些。dev_alloc_skb():其實這個函式實質上是呼叫了alloc_skb()函式單純分配了乙個sk_buff記憶體。但是通常來說這是在驅動程式中申請sk_buff結構時,用到的申請函式,和一般的申請記憶體函式有點不一樣,它是用gfp_atomic的記憶體分配方式來申請的(一般我們用gfp_kernel),這是個原子操作,表示申請時不能被中斷。其實還有個申請函式:netdev_alloc_skb(),這個沒怎麼研究,估計是專門為網路裝置中使用sk_buff時,用來記憶體分配的吧。

第二、sk_buff結構的記憶體釋放:

void kfree_skb(struct sk_buff *skb)

void __kfree_skb(struct sk_buff *skb)

static void skb_release_all(struct sk_buff *skb)

首先還是來說下

dev_kfree_skb()函式吧,這個函式和dev_alloc_skb()相對應的,一般也是在驅動程式中使用,其實現也是對kfree_skb()進行封裝的,所以重點還是kfree_skb()函式。

kfree_skb()函式首先是獲取到skb->users成員字段,這是個引用計數器,當只有skb->users == 1是才能真正釋放空間記憶體(也不是釋放,而是放回到快取池中)。如果不為1的話,那麼kfree_skb()函式只是簡單的skb->users減去個1而已。skb->users表示有多少個人正在引用這個結構體,如果不為1表示還有其他人在引用他,不能釋放掉這

個結構體,否則會讓引用者出現野指標、非法操作記憶體等錯誤。這種情況下只需要skb->users減去個1即可,表明我不再引用這個結構體了。如果skb->users == 1,則表明是最後乙個引用該結構體的,所以可以呼叫_kfree_skb()函式直接釋放掉了。當skb釋放掉後,dst_release同樣會被呼叫以減小相關dst_entry資料結構的引用計數。

如果destructor(skb的析構函式)被初始化過,相應的函式會在此時被呼叫。還有分片結構體(skb_shared_info)也會相應的被釋放掉,然後把所有記憶體空間全部返還到skbuff_head_cache快取池中,這些操作都是由kfree_skbmem()函式來完成的。這裡分片的釋放涉及到了轉殖問題:如果skb沒有被轉殖,資料區也沒有其他skb引用,則直接釋放即可;如果是轉殖了skb結構,則當轉殖數計數為1時,才能釋放skb結構體;如果分片結構被轉殖了,那麼也要等到分片轉殖計數為1時,才能釋放掉分片資料結構。如果skb是從skbuff_fclone_cache快取池中申請的記憶體時,則要仔細銷毀過程了,因為從這個快取池中申請的記憶體,會返還2個skb結構體和乙個引用計數器。所以銷毀時不僅要考慮轉殖問題還要考慮2個skb的釋放順序。銷毀過程見下圖(原圖來自《深入理解linux網路技術內幕》):

sk buff整理筆記(兩 操作函式)

承接上一 sk buff 整理筆記 一 資料結構 這一篇要講的是核心為sk buff結構提供的一些操作函式。第一 首先要講的是sk buff中的四大指標 四大指標各自是 head data tail end 這四個指標都是指向了資料區的。資料區中存放的是協議頭和資料。head和end指標是每乙個資料...

Oracle整理筆記(三)

day04 資料庫物件 同義詞 就是給資料庫物件乙個別名。序列 oracle 中實現增長的物件。檢視 預定義的查詢,作為表一樣的查詢使用,是一張虛擬表。索引 對資料庫表中的某些列進行排序,便於提高查詢效率。同義詞 在 oracle 中對使用者的管理是使用許可權的方式來管理的,如果我們想使用資料庫,我...

oracle筆記整理三 配置

1 oracle net配置 1 win下 開始 程式 oracle home name 網路管理 oracle net configuration assistant 2 建立監聽程式 如果選擇tcp ip協議進行監聽,要設定埠為1521 僅伺服器端 3 listener.ora檔案 該檔案每個伺...