1. 程式人生 > >網路驅動移植之sk_buff結構體及其相關操作函式(下)

網路驅動移植之sk_buff結構體及其相關操作函式(下)

    2、結構體相關操作函式

    (1)、dev_alloc_skb

    實際上,函式dev_alloc_skb最終是呼叫__alloc_skb函式來分配資料緩衝區和sk_buff結構體的,如下圖:

 

    從dev_alloc_skb到__alloc_skb所涉及的原始碼如下: 

/* linux-2.6.38.8/net/core/skbuff.c */
struct sk_buff *dev_alloc_skb(unsigned int length)
{
	/*
	 * There is more code here than it seems:
	 * __dev_alloc_skb is an inline
	 */
	return __dev_alloc_skb(length, GFP_ATOMIC);
}

/* linux-2.6.38.8/include/linux/skbuff.h */
static inline struct sk_buff *__dev_alloc_skb(unsigned int length,
					      gfp_t gfp_mask)
{
	struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask);
	if (likely(skb))
		skb_reserve(skb, NET_SKB_PAD);
	return skb;
}

/* linux-2.6.38.8/include/linux/skbuff.h */
static inline struct sk_buff *alloc_skb(unsigned int size,
					gfp_t priority)
{
	return __alloc_skb(size, priority, 0, NUMA_NO_NODE);
}

    其中,NET_SKB_PAD的值在ARM體系架構上為32。

    接下來,在__alloc_skb函式中,首先通過kmem_cache_alloc_node函式(在未配置CONFIG_NUMA和CONFIG_SLOB的情況下,它的實現就是直接呼叫kmem_cache_alloc函式)從skbuff_head_cache快取記憶體中申請一個sk_buff結構體物件。建立skbuff_head_cache快取記憶體的原始碼如下: 

/* linux-2.6.38.8/net/socket.c */
static int __init sock_init(void)
{
	...

	/* Initialize skbuff SLAB cache */
	skb_init();

	...
}
core_initcall(sock_init);	/* early initcall */

/* linux-2.6.38.8/net/core/skbuff.c */
void __init skb_init(void)
{
	skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
					      sizeof(struct sk_buff),
					      0,
					      SLAB_HWCACHE_ALIGN|SLAB_PANIC,
					      NULL);
	skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache",
						(2*sizeof(struct sk_buff)) +
						sizeof(atomic_t),
						0,
						SLAB_HWCACHE_ALIGN|SLAB_PANIC,
						NULL);
}

    申請sk_buff結構體物件的程式碼如下: 

/* linux-2.6.38.8/net/core/skbuff.c */
	skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);
	if (!skb)
		goto out;
	prefetchw(skb);

    對於S3C2410,prefetchw函式的實現是使用GCC的內建函式__builtin_prefetch,定義如下: 

/* linux-2.6.38.8/include/linux/prefetch.h */
#ifndef ARCH_HAS_PREFETCHW
#define prefetchw(x) __builtin_prefetch(x,1)
#endif

    __builtin_prefetch的函式原型為void __builtin_prefetch (const void *addr, ...),常用於最小化資料的存取時間。引數addr的值為將要預取的記憶體地址,另外,它還有兩個可選的引數rw 和 locality,rw的值只能為常量0或者1,1用於寫的預取,預設值0用於讀的預取。關於它的詳細使用說明請參考網址http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/Other-Builtins.html#Other-Builtins

    對於ARMv5,prefetchw函式使用另一種實現,而S3C2410是無法支援的。另外,對於S3C2410,__LINUX_ARM_ARCH__的值為4,在linux-2.6.38.8/arch/arm/Makefile檔案中被宣告。

    __alloc_skb函式的另一個重要功能就是分配資料緩衝區,包括skb_shared_info結構體。先使用SKB_DATA_ALIGN巨集以SMP_CACHE_BYTES(對於ARM體系架構,它的值為32)位對齊資料緩衝區(這裡不包括skb_shared_info結構體)的大小,然後呼叫kmalloc_node_track_caller函式分配記憶體,程式碼如下: 

/* linux-2.6.38.8/net/core/skbuff.c */
	size = SKB_DATA_ALIGN(size);
	data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),
			gfp_mask, node);
	if (!data)
		goto nodata;
	prefetchw(data + size);

    其中兩個主要函式的實現如下: 

/* linux-2.6.38.8/include/linux/skbuff.h */
#define SKB_DATA_ALIGN(X)	(((X) + (SMP_CACHE_BYTES - 1)) & \
				 ~(SMP_CACHE_BYTES - 1))

/* linux-2.6.38.8/include/linux/slab.h */
#define kmalloc_node_track_caller(size, flags, node) \
	kmalloc_track_caller(size, flags)

#define kmalloc_track_caller(size, flags) \
	__kmalloc(size, flags)

/* linux-2.6.38.8/mm/slab.c */
void *__kmalloc(size_t size, gfp_t flags)
{
	return __do_kmalloc(size, flags, NULL);
}

    最後,__alloc_skb函式會完成對sk_buff和skb_shared_info兩個結構體變數部分成員的初始化。 

/* linux-2.6.38.8/net/core/skbuff.c */
	memset(skb, 0, offsetof(struct sk_buff, tail));
	skb->truesize = size + sizeof(struct sk_buff);
	atomic_set(&skb->users, 1);
	skb->head = data;
	skb->data = data;
	skb_reset_tail_pointer(skb);
	skb->end = skb->tail + size;
#ifdef NET_SKBUFF_DATA_USES_OFFSET
	skb->mac_header = ~0U;
#endif

    其中,當NET_SKBUFF_DATA_USES_OFFSET未定義時,skb_reset_tail_pointer函式的定義如下: 

/* linux-2.6.38.8/include/linux/skbuff.h */
static inline void skb_reset_tail_pointer(struct sk_buff *skb)
{
	skb->tail = skb->data;
}
/* linux-2.6.38.8/net/core/skbuff.c */
	shinfo = skb_shinfo(skb);
	memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));
	atomic_set(&shinfo->dataref, 1);
	kmemcheck_annotate_variable(shinfo->destructor_arg);

    其中,skb_shinfo函式的定義如下: 

/* linux-2.6.38.8/include/linux/skbuff.h */
	#define skb_shinfo(SKB)	((struct skb_shared_info *)(skb_end_pointer(SKB)))

    當NET_SKBUFF_DATA_USES_OFFSET未定義時,skb_end_pointer函式的定義如下: 

/* linux-2.6.38.8/include/linux/skbuff.h */
static inline unsigned char *skb_end_pointer(const struct sk_buff *skb)
{
	return skb->end;
}

     __alloc_skb函式完成的工作大致如下圖(圖片來自《Understanding Linux Network Internals》):

 

    另外,當NET_SKBUFF_DATA_USES_OFFSET未定義時,sk_buff_data_t的宣告如下: 

/* linux-2.6.38.8/include/linux/skbuff.h */
typedef unsigned char *sk_buff_data_t;

    (2)、skb_reserve

    skb_reserve函式用於在緩衝區的頭部預留一些空間,其定義如下: 

/* linux-2.6.38.8/include/linux/skbuff.h */
static inline void skb_reserve(struct sk_buff *skb, int len)
{
	skb->data += len;
	skb->tail += len;
}

    skb_reserve函式只是簡單地更新data和tail兩個指標而已,如下圖(圖片來自《Understanding LinuxNetwork Internals》):

 

    (3)、skb_put

    skb_put函式會把一個數據塊新增到緩衝區的尾端。 

/* linux-2.6.38.8/net/core/skbuff.c */
unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{
	unsigned char *tmp = skb_tail_pointer(skb);
	SKB_LINEAR_ASSERT(skb);
	skb->tail += len;
	skb->len  += len;
	if (unlikely(skb->tail > skb->end))
		skb_over_panic(skb, len, __builtin_return_address(0));
	return tmp;
}

    其中,skb_tail_pointer函式在NET_SKBUFF_DATA_USES_OFFSET未定義時,其定義如下: 

/* linux-2.6.38.8/include/linux/skbuff.h */
static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb)
{
	return skb->tail;
}

    對於ARM體系結構,在CONFIG_BUG和CONFIG_DEBUG_BUGVERBOSE都配置的情況下,SKB_LINEAR_ASSERT的定義如下: 

/* linux-2.6.38.8/include/linux/skbuff.h */
#define SKB_LINEAR_ASSERT(skb)  BUG_ON(skb_is_nonlinear(skb))

/* linux-2.6.38.8/include/asm-generic/bug.h */
#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0)
#endif

/* linux-2.6.38.8/arch/arm/include/asm/bug.h */
extern void __bug(const char *file, int line) __attribute__((noreturn));

#define BUG()		__bug(__FILE__, __LINE__) /* give file/line information */

/* linux-2.6.38.8/arch/arm/kernel/traps.c */
void __attribute__((noreturn)) __bug(const char *file, int line)
{
	printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line);
	*(int *)0 = 0;

	/* Avoid "noreturn function does return" */
	for (;;);
}

    當函式skb_is_nonlinear返回非零值(也就是skb->data_len的值不為0)時,SKB_LINEAR_ASSERT將產生一個oops訊息。skb_is_nonlinear的定義如下: 

/* linux-2.6.38.8/include/linux/skbuff.h */
static inline int skb_is_nonlinear(const struct sk_buff *skb)
{
	return skb->data_len;
}

    skb_put函式其實也沒有真的把資料新增到緩衝區中,而只是簡單地更新了skb->tail和skb->len的值,如下圖(圖片來自《Understanding Linux Network Internals》):

 

    (4)、skb_push

    skb_push函式會把一個數據塊新增到緩衝區的開端。 

/* linux-2.6.38.8/net/core/skbuff.c */
unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
	skb->data -= len;
	skb->len  += len;
	if (unlikely(skb->data<skb->head))
		skb_under_panic(skb, len, __builtin_return_address(0));
	return skb->data;
}

    skb_push函式其實也沒有真的把資料新增到緩衝區中,而只是簡單地更新了skb->data和skb->len的值,如下圖(圖片來自《Understanding Linux Network Internals》):

 

    (5)、skb_pull

    skb_pull函式會把一個數據塊從緩衝區中的頂端刪除。 

/* linux-2.6.38.8/net/core/skbuff.c */
unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)
{
	return skb_pull_inline(skb, len);
}

/* linux-2.6.38.8/include/linux/skbuff.h */
static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int len)
{
	return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len);
}

static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len)
{
	skb->len -= len;
	BUG_ON(skb->len < skb->data_len);
	return skb->data += len;
}

    skb_pull函式其實也沒有真的把資料從緩衝區中刪除,而只是簡單地更新了skb->data和skb->len的值,如下圖(圖片來自《Understanding Linux Network Internals》):

 

相關推薦

網路驅動移植sk_buff結構及其相關操作函式

    2、結構體相關操作函式     (1)、dev_alloc_skb     實際上,函式dev_alloc_skb最終是呼叫__alloc_skb函式來分配資料緩衝區和sk_buff結構體的,如下圖:       從dev_alloc_skb到__alloc_skb

net_device結構及其相關操作函式

在Linux系統中,網路裝置都被抽象為struct net_device結構體。它是網路裝置硬體與上層協議之間聯絡的介面,瞭解它對編寫網路驅動程式非常有益,所以本文將著手簡要介紹linux-2.6.38.8/include/linux/netdevice.h檔案中struc

cdev結構及其相關函式 【轉】

1、在Linux2.6核心中一個字元裝置用cdev結構來描述,其定義如下: struct cdev {         struct kobject kobj;         struct module *owner;   //所屬模組         cons

Go語言基礎—— Go語言結構、Go語言切片Slice、range遍歷、Go語言Map集合

Go語言結構體 Go 語言中陣列可以儲存同一型別的資料,但在結構體中我們可以為不同項定義不同的資料型別。 結構體是由一系列具有相同型別或不同型別的資料構成的資料集合。  結構體表示一項記錄,比如儲存圖書館的書籍記錄,每本書有以下屬性: Title :標題&nbs

c語言==兩個結構之間的相互呼叫17

指向結構體型別變數的使用 首先讓我們定義結構體: struct stu { char name[20]; long number; float score[4]; } ; 再定義指向結構體型別變數的指標變數: struct stu *p1, *

c語言結構計算罰時與成績acm

Judging a programming contest is hard work, with demanding contestants, tedious decisions,and monotonous work. Not to mention the nutritional problems of s

《Java從小白到大牛》第10章 面向對象基礎

ket 方法重載 imp isp 配套 一次 sub 類名 類的變量 《Java從小白到大牛》紙質版已經上架了!!! 封裝性與訪問控制 Java面向對象的封裝性是通過對成員變量和方法進行訪問控制實現的,訪問控制分為4個等級:私有、默認、保護和公有,具體規則如表10-1所示。

spring我見--Controller註冊到DispatchServlet請求處理

1 DispatcherServlet請求分發 1.1 DispatcherServlet的初始化 在web.xml檔案裡,跟ContextLoaderListener形影不離的應該就是DispatcherServlet了,它一般做如下定義: <servlet>

UVM暫存器篇二:暫存器模型概覽

本文轉自:http://www.eetop.cn/blog/html/28/1561828-6266219.html 暫存器模型構建 在構建UVM暫存器模型的過程中,讀者需要了解下面這些與模型構建相關的類和它們的功能:     簡化後的MCDF暫存器模

android影象處理系列六--給圖片新增邊框-圖片疊加

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

企業架構研究總結9——聯邦企業架構CIO委員會的企業架構實施指南

開發基線企業架構       在開發基線企業架構這一過程中,各個企業或組織需要根據已經確定的架構目標、範圍和所採用的架構框架對當前自身的狀態進行各種製品的開發,這既包括針對核心架構製品的開發,也包括對支援性架構製品的開發,同時還包括針對其他由於特定需求而單獨定義的架構製品(

sencha touch 擴充套件篇將sencha touch打包成安裝程式- 使用phonegap打包安裝程式

      這講我們來講解下如何使用phonegapa建立專案環境並通過她們將sencha touch打包成app,這裡我們只講解打包android的apk,打包ios的過程有點類似,但是需要在mac環境下,最後通過xcode匯出成ipa安裝程式;   一、phonega

學不可以已--我一年Java路的回顧,反思以及展望

說到上個暑假,還有不得不提的事就是沉迷於CSDN的論壇中。早在暑假回家之前,除了那些學習計劃,我還有一個目標就是一定要在CSDN的Java板塊變成一個紅星,因為那個時候我覺得紅星就是技術水平高的代名詞(一般初學者才有這個想法^_^),所以那個假期在CSDN回答問題可以用一個瘋狂來形容,當時基本上事每一個帖子都

View的事件體系三 android事件分發機制詳解

  接著上一篇來分析事件分發機制,在看了各位大牛的關於事件分發機制的分析後茅塞頓開,之前看過好幾遍郭霖,弘揚以及玉剛大神關於事件體系的講解,一直看不懂,比較模糊,最近複習時,看到一篇博文,寫的相當精彩,看完後,再回看各位大神的博文,收穫頗豐,記錄一下自己的理解和

資料結構-棧和佇列面試題

面試題四:元素出棧、入棧順序的合法性。如入棧的序列(1,2,3,4,5),出棧序列為(4,5,3,2,1)。 思路: ①首先判斷出棧入棧序列長度是否一致,不一致直接返回false; ②借用一個臨時的棧,依次遍歷入棧序列的每一個元素,每次

資料結構入門---初始二叉樹

這篇文章我們準備將二叉樹實現為具體的程式碼 首先我們要從二叉樹的遍歷說起。二叉樹的遍歷主要有四種形式 1. 前序遍歷 方法:如果二叉樹為空,則直接返回。如果二叉樹非空,則訪問根結點,再前序遍歷左子樹,然後前序遍歷右子樹 我們可以知道這樣的遍歷方式是以遞迴

資料結構基礎溫故-6.查詢:雜湊表

雜湊(雜湊)技術既是一種儲存方法,也是一種查詢方法。然而它與線性表、樹、圖等結構不同的是,前面幾種結構,資料元素之間都存在某種邏輯關係,可以用連線圖示表示出來,而雜湊技術的記錄之間不存在什麼邏輯關係,它只與關鍵字有關聯。因此,雜湊主要是面向查詢的儲存結構。雜湊技術最適合的求解問題是查詢與給定值相等的記錄。

資料結構基礎溫故-5.圖:最短路徑

圖的最重要的應用之一就是在交通運輸和通訊網路中尋找最短路徑。例如在交通網路中經常會遇到這樣的問題:兩地之間是否有公路可通;在有多條公路可通的情況下,哪一條路徑是最短的等等。這就是帶權圖中求最短路徑的問題,此時路徑的長度不再是路徑上邊的數目總和,而是路徑上的邊所帶權值的和。帶權圖分為無向帶權圖和有向帶權圖,但如

袁毓林 李強:怎樣用物性結構知識解決“網球問題”?

“網球問題”指怎樣把racquet(網球拍)、ball(網球)和net(球網)之類具有情境聯想關係的詞彙概念聯絡起來、發現它們之間的語義和推理關係。這是一個自然語言處理和相關的語言知識資源建設的世界性難題。該文以求解“網球問題”為目標,對目前比較主流的幾種語言詞彙和概

獻給初學iOS的小盆友們——微博app專案開發十八搭建傳送微博介面

今天應該是這個專案最後一次更新啦,能堅持下來的小盆友真是棒棒噠。這節課我們將會完成傳送有圖片的微博,實現微博最後一個核心功能。因為微博的功能實在太多太多了,我們不可能把微博的每個功能都一一實現。小夥伴能從中學到最精髓的東西才是最主要的,萬變不離其宗,得到處理問題