《LwIP協(xié)議棧源碼詳解——TCP/IP協(xié)議的實現(xiàn)》以太網(wǎng)數(shù)據(jù)接收

姓名:朱小鵬 ? ?學(xué)號:16010130023

轉(zhuǎn)載:

http://blog.sina.com.cn/s/blog_62a85b950101am9n.html

【嵌牛導(dǎo)讀】:low_level_init函數(shù)是與我們使用的與硬件密切相關(guān)初始化函數(shù)

【嵌牛鼻子】:以太網(wǎng)數(shù)據(jù)接收

【嵌牛提問】:LWIP是怎樣來處理以太網(wǎng)數(shù)據(jù)接收?

【嵌牛正文】:

昨天說到low_level_init函數(shù)是與我們使用的與硬件密切相關(guān)初始化函數(shù)辩尊,看看:

static void low_level_init(struct netif *netif)

{

netif->hwaddr_len = ETHARP_HWADDR_LEN; //設(shè)置變量enc28j60的hwaddr_len字段

netif->hwaddr[0] = 'F';//初始化變量enc28j60的MAC地址

netif->hwaddr[1] = 'O';//設(shè)什么地址用戶自由發(fā)揮吧,但是不要與其他

netif->hwaddr[2] = 'R';//網(wǎng)絡(luò)設(shè)備的MAC地址重復(fù)。

netif->hwaddr[3] = 'E';

netif->hwaddr[4] = 'S';

netif->hwaddr[5] = 'T';

netif->mtu = 1500;//最大允許傳輸單元

//允許該網(wǎng)卡廣播和ARP功能,并且該網(wǎng)卡允許有硬件鏈路連接

netif->flags= NETIF_FLAG_BROADCAST |\

NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

enc28j60_init(netif->hwaddr);//與底層驅(qū)動硬件驅(qū)動程序密切相關(guān)的硬件初始化函數(shù)

}

至此芦缰,終于變量enc28j60被初始化好了幔翰,而且它描述的網(wǎng)卡芯片enc28j60也被初始化好了,而且變量enc28j60也被鏈入鏈表netif_list饭耳。

接著上上上上面的語句(8)調(diào)用netif_set_default函數(shù)初始化缺省網(wǎng)絡(luò)接口。協(xié)議棧除了有個netif_list全局變量指向netif網(wǎng)絡(luò)接口結(jié)構(gòu)的鏈表执解,還有個全局變量netif_default全局變量指向缺省的網(wǎng)絡(luò)接口結(jié)構(gòu)寞肖。當(dāng)IP層有數(shù)據(jù)發(fā)送時,它首先會以netif_list為索引選擇滿足某個條件的網(wǎng)絡(luò)接口發(fā)送數(shù)據(jù)包,但是新蟆,當(dāng)找不到這樣的接口時觅赊,協(xié)議棧就會調(diào)用缺省的網(wǎng)絡(luò)接口直接發(fā)送數(shù)據(jù)包,所以(8)中的意思是把變量enc28j60描述的網(wǎng)絡(luò)接口設(shè)置為缺省的網(wǎng)絡(luò)接口琼稻。

(9)調(diào)用函數(shù)netif_set_up使能網(wǎng)絡(luò)接口吮螺,這通過一個簡單語句來實現(xiàn):

netif->flags |= NETIF_FLAG_UP;

至此,網(wǎng)卡初始化完成帕翻,能正常接收和發(fā)送數(shù)據(jù)包了鸠补。下面我們來討論討論關(guān)于網(wǎng)卡數(shù)據(jù)包的接收和發(fā)送。

LWIP中實現(xiàn)了接收一個數(shù)據(jù)包和發(fā)送一個數(shù)據(jù)包函數(shù)的框架嘀掸,這兩個函數(shù)分別是low_level_input和low_level_output紫岩,用戶需要使用實際網(wǎng)卡驅(qū)動程序完成這兩個函數(shù)。在第一篇中講過横殴,一個典型的LWIP應(yīng)用系統(tǒng)包括這樣的三個進(jìn)程:首先是上層應(yīng)用程序進(jìn)程被因,然后是LWIP協(xié)議棧進(jìn)程,最后是底層硬件數(shù)據(jù)包接收進(jìn)程衫仑。這里我們就來講講第三個進(jìn)程梨与,看看數(shù)據(jù)包是怎樣被接收并往上層傳遞的。但在這之前文狱,有必要說說以太網(wǎng)網(wǎng)卡所收到的數(shù)據(jù)包的格式粥鞋。如下圖,

LWIP使用了一個eth_hdr的數(shù)據(jù)結(jié)構(gòu)來描述以太網(wǎng)數(shù)據(jù)包包頭的14個字節(jié)瞄崇。如下呻粹,

PACK_STRUCT_BEGIN

struct eth_hdr {

PACK_STRUCT_FIELD(struct eth_addr dest);//目標(biāo)MAC地址

PACK_STRUCT_FIELD(struct eth_addr src);//源MAC地址

PACK_STRUCT_FIELD(u16_t type);//類型

} PACK_STRUCT_STRUCT;

PACK_STRUCT_END

其中PACK_STRUCT_xxx都是與編譯器字對齊相關(guān)的宏定義,這里不作詳細(xì)介紹了苏研。上面的dest等浊、src和type三個字段分別和上圖中的目的MAC地址、源MAC地址和類型域字段對應(yīng)摹蘑。

在上面討論的基礎(chǔ)上筹燕,我們來看看這個數(shù)據(jù)包接收進(jìn)程,源代碼如下:

voidethernetif_input(void *arg)//創(chuàng)建該進(jìn)程時衅鹿,要將某個網(wǎng)絡(luò)接口結(jié)構(gòu)的netif結(jié)構(gòu)指

{//針作為參數(shù)傳入

struct eth_hdr *ethhdr;

struct pbuf *p;

struct netif *netif = (struct netif *)arg;

while (1)

{

p = low_level_input (netif);//接收一個數(shù)據(jù)包

if (p == NULL)//如果數(shù)據(jù)包為空撒踪,

continue;//則循環(huán)結(jié)束,啟動下次接收過程

ethhdr = p->payload;//取得數(shù)據(jù)包內(nèi)數(shù)據(jù)

switch (htons(ethhdr->type))//判斷數(shù)據(jù)包類型

{//只對IP數(shù)據(jù)包和ARP數(shù)據(jù)包進(jìn)行處理

case ETHTYPE_IP://IP數(shù)據(jù)包

case ETHTYPE_ARP://ARP數(shù)據(jù)包

if (netif->input(p, netif)!=ERR_OK)//將數(shù)據(jù)包發(fā)送到上層應(yīng)用函數(shù)

{

pbuf_free(p);

p = NULL;

}

break;

default:

pbuf_free(p);

p = NULL;

break;

}//switch

}//while

}//main函數(shù)

要創(chuàng)建上面的這個進(jìn)程大渤,需要把個網(wǎng)絡(luò)接口結(jié)構(gòu)的netif結(jié)構(gòu)指針作為參數(shù)傳入制妄,在UC/OSII中要用到下面的語句實現(xiàn),

OSTaskCreate(ethernetif_input,(void *)&enc28j60,

&T_ETHERNETIF_INPUT_STK[T_ETHERNETIF_INPUT_STKSIZE-1]

ETH_IF_TASK_PRIO);

在數(shù)據(jù)包接收進(jìn)程中泵三,有三個需要注意的地方耕捞。一是數(shù)據(jù)包接收的方法是查詢方式衔掸,即處理器不斷向網(wǎng)卡芯片中讀取數(shù)據(jù),如果讀不到數(shù)據(jù)砸脊,則控制器會重新啟動一個讀取時序具篇;如果能夠成功讀取到數(shù)據(jù)纬霞,則將數(shù)據(jù)通過網(wǎng)卡注冊的input函數(shù)交往上層進(jìn)行處理凌埂。使用查詢方式實現(xiàn)的數(shù)據(jù)包接收進(jìn)程其優(yōu)先級必須低于系統(tǒng)中其他進(jìn)程的優(yōu)先級,否則它會阻塞比它優(yōu)先級低的進(jìn)程的運行诗芜。上面的程序有個可以改進(jìn)的地方瞳抓,即在讀取到的數(shù)據(jù)包為空時,接收進(jìn)程調(diào)用系統(tǒng)函數(shù)將自己延時一段時間再啟動下一個讀取過程伏恐,這樣可以使其不能阻止優(yōu)先級更低的進(jìn)程的運行孩哑,缺點是數(shù)據(jù)包的接收得不到及時的響應(yīng)。其實數(shù)據(jù)包的接收可以采用中斷的方式來實現(xiàn)翠桦,這種方式是一種比較好的方式横蜒。一般的網(wǎng)卡芯片都有中斷功能,即當(dāng)網(wǎng)卡接收到一個數(shù)據(jù)包后销凑,它可以產(chǎn)生中斷信號告訴控制器自己接收到一個數(shù)據(jù)包丛晌。控制器此時啟動一個讀取數(shù)據(jù)包時序斗幼,就能有效的讀取到非空數(shù)據(jù)包澎蛛。所以可以這樣來實現(xiàn)一個接收數(shù)據(jù)包進(jìn)程:在無數(shù)據(jù)包收到時,數(shù)據(jù)包接收進(jìn)程阻塞在一個信號量下蜕窿,當(dāng)有數(shù)據(jù)包到來時谋逻,網(wǎng)卡芯片產(chǎn)生一個中斷信號,處理器進(jìn)入中斷處理桐经,并釋放一個信號量毁兆。中斷退出后,數(shù)據(jù)包接收進(jìn)程得到信號量阴挣,并從網(wǎng)卡芯片中讀取數(shù)據(jù)包气堕,并將數(shù)據(jù)包遞交給上層進(jìn)行處理。

第二個需要注意的地方是htons(ethhdr->type)函數(shù)的使用屯吊,htons函數(shù)的功能是將一個半字長的數(shù)據(jù)從網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換到我們的處理器支持的字節(jié)順序送巡。解釋一下,在計算機體系結(jié)構(gòu)和計算機通信領(lǐng)域中盒卸,對于半字骗爆、字等的存儲機制有可能不同。目前通常采用的存儲機制主要有兩種:big-endian和little-endian蔽介,即大端和小端摘投。對于大端模式煮寡,某個半字或字?jǐn)?shù)據(jù)的高位字節(jié)被在內(nèi)存的低地址端,低位字節(jié)排放在內(nèi)存的高地址端犀呼。對于小端模式幸撕,則恰好相反。由于我們使用的ARM處理器使用的是小端模式外臂,而接收到的網(wǎng)絡(luò)字節(jié)數(shù)據(jù)用的是大端模式坐儿,所以這里調(diào)用函數(shù)htons實現(xiàn)大端與小端的轉(zhuǎn)換,實際就是將兩個字節(jié)交換順序即可宋光。這樣調(diào)用htons(ethhdr->type)后貌矿,ethhdr->type的值就為0x0800或0x0806等。

最后需要注意的地方罪佳,netif->input在結(jié)構(gòu)enc28j60初始化時已經(jīng)被設(shè)置為指向tcpip_input函數(shù)逛漫,所以實際上上面是調(diào)用tcpip_input函數(shù)往上層遞交數(shù)據(jù)包。tcpip_input屬于IP層函數(shù)赘艳,從這里我們可以看出LWIP的一個很大的特點酌毡,即各層之間沒有明顯的界限劃分。像前面所講的那樣蕾管,LWIP協(xié)議棧進(jìn)程完成初始化相關(guān)工作后枷踏,會阻塞在一個郵箱上等待數(shù)據(jù)包的輸入,這就對了娇掏,tcpip_input函數(shù)就是向這個郵箱發(fā)送一條消息呕寝,且該消息中包含了收到的數(shù)據(jù)包存儲的地址。LWIP協(xié)議棧進(jìn)程從郵箱中取到該地址后就可以對數(shù)據(jù)包進(jìn)行處理了婴梧。

至此下梢,數(shù)據(jù)包的接收可算大功告成,關(guān)于數(shù)據(jù)包的發(fā)送塞蹭,這點很簡單孽江,因為它不必像數(shù)據(jù)包接收那樣要使用一個專門的進(jìn)程來實現(xiàn),而是這樣的:當(dāng)上層有數(shù)據(jù)包要發(fā)送時番电,直接調(diào)用netif->linkoutput發(fā)送數(shù)據(jù)包就可以了岗屏。netif->linkoutput在結(jié)構(gòu)enc28j60初始化時已經(jīng)被設(shè)置為指向low_level_output函數(shù),該函數(shù)和底層硬件驅(qū)動密切相關(guān)漱办,用于實現(xiàn)發(fā)送一個數(shù)據(jù)包的功能这刷。用戶應(yīng)該結(jié)合具體網(wǎng)卡驅(qū)動實現(xiàn)該函數(shù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娩井,一起剝皮案震驚了整個濱河市暇屋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌洞辣,老刑警劉巖咐刨,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昙衅,死亡現(xiàn)場離奇詭異,居然都是意外死亡定鸟,警方通過查閱死者的電腦和手機而涉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來联予,“玉大人啼县,你說我怎么就攤上這事∏” “怎么了谭羔?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長麦向。 經(jīng)常有香客問我,道長客叉,這世上最難降的妖魔是什么诵竭? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮兼搏,結(jié)果婚禮上卵慰,老公的妹妹穿的比我還像新娘。我一直安慰自己佛呻,他們只是感情好裳朋,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吓著,像睡著了一般鲤嫡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绑莺,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天暖眼,我揣著相機與錄音,去河邊找鬼纺裁。 笑死诫肠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的欺缘。 我是一名探鬼主播栋豫,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谚殊!你這毒婦竟也來了丧鸯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤络凿,失蹤者是張志新(化名)和其女友劉穎骡送,沒想到半個月后昂羡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡摔踱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年虐先,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片派敷。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛹批,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出篮愉,到底是詐尸還是另有隱情腐芍,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布试躏,位于F島的核電站猪勇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏颠蕴。R本人自食惡果不足惜泣刹,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望犀被。 院中可真熱鬧椅您,春花似錦、人聲如沸寡键。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽西轩。三九已至员舵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間遭商,已是汗流浹背固灵。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留劫流,地道東北人巫玻。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像祠汇,于是被迫代替她去往敵國和親仍秤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容