PS:由于百度文庫(kù)里的《LwIP協(xié)議棧源碼詳解——TCP/IP協(xié)議的實(shí)現(xiàn)》竟然坑爹的被屏蔽,為了偉大的科研事業(yè)的繼續(xù),只好做重復(fù)工。一如既往的歡迎廣大讀者使用與轉(zhuǎn)載,但請(qǐng)注明轉(zhuǎn)載出處,謝謝配合,3Q。
——老衲五木
如果你認(rèn)為所謂的毅力是每分每秒的“艱苦忍耐”式的奮斗,那這是一種很不足的心理狀態(tài)。毅力是一種習(xí)慣,毅力是一種狀態(tài),毅力是一種生活。看了這么久的代碼覺得是不是該寫點(diǎn)東西了,不然怎么對(duì)得起某人口中所說的科研人員這個(gè)光榮稱號(hào)。初見這十幾二十萬行的代碼,著實(shí)看出了一身冷汗?,F(xiàn)在想想其實(shí)也不是那么難,那么多革命先輩經(jīng)過N長(zhǎng)時(shí)間才搞出來的東東怎么可能讓你個(gè)毛小子幾周之內(nèi)搞懂。我見到的只是冰川的一小角,萬里長(zhǎng)征的一小步,九頭牛身上的一小毛…再用某人的話說,寫吧,昏寫,瞎寫,胡寫,亂寫,寫寫就懂了。
我想我很適合當(dāng)一個(gè)歌頌者,青春在風(fēng)中飄著。你知道,就算大雨讓這座城市顛倒,我會(huì)給你懷抱;受不了,看見你背影來到,寫下我度秒如年難捱的離騷;就算整個(gè)世界被寂寞綁票,我也不會(huì)奔跑;逃不了,最后誰(shuí)也都蒼老,寫下我,時(shí)間和琴聲交錯(cuò)的城堡。我正在聽的歌。扯遠(yuǎn)了…
正題,嵌入式產(chǎn)品連入Internet網(wǎng),這個(gè)MS是個(gè)愈演愈烈的趨勢(shì)。想想,你可以足不出戶對(duì)你的產(chǎn)品進(jìn)行配置,并獲取你關(guān)心的數(shù)據(jù)信息,多好。這也許也是物聯(lián)網(wǎng)世界最基本的雛形。當(dāng)然,你的產(chǎn)品要有如此功能,那可不容易,至少它得有個(gè)目前很Fashion的TCP/IP協(xié)議棧。LWIP是一套用于嵌入式系統(tǒng)的開放源代碼TCP/IP協(xié)議棧。在你的嵌入式處理器不是很NB,內(nèi)部Flash和Ram不是很強(qiáng)大的情況下,用它還是很合適滴。
LWIP的設(shè)計(jì)者為像我這樣的懶惰者提供了詳細(xì)的移植說明文檔,當(dāng)然這還不夠,他們還盡可能的包攬了大部分工作,懶人們只要做很少的工作就功德圓滿了??v觀整個(gè)移植過程,使用者需要完成以下幾個(gè)方面的東西:
首先是LWIP協(xié)議內(nèi)部使用的數(shù)據(jù)類型的定義,如u8_t,s8_t,u16_t,u32_t等等等等。由于所移植平臺(tái)處理器的不同和使用的編譯器的不同,這些數(shù)據(jù)類型必須重新定義。想想,一個(gè)int型數(shù)據(jù)在64位處理器上其長(zhǎng)度為8個(gè)字節(jié),在32位處理器上為4個(gè)字節(jié),而在16位處理器上就只有兩個(gè)字節(jié)了。因此這部分需要使用者根據(jù)處理器位數(shù)和和使用的編譯器的特點(diǎn)來編寫。所以在ARM7處理器上使用的typedefunsigned int u32_t移植語(yǔ)句用在64位處理器的移植過程中那肯定是行不通的了。
其次是實(shí)現(xiàn)與信號(hào)量和郵箱操作相關(guān)的函數(shù),比如建立、刪除、等待、釋放等。如果在裸機(jī)上直接跑LWIP,這點(diǎn)實(shí)現(xiàn)起來比較麻煩,使用者必須自己去建立一套信號(hào)量和郵箱相關(guān)的機(jī)制。一般情況下,在使用LWIP的嵌入式系統(tǒng)中都會(huì)有操作系統(tǒng)的支持,而在操作系統(tǒng)中信號(hào)量和郵箱往往是最基本的進(jìn)程通信機(jī)制了。UC/OSII應(yīng)該算是最簡(jiǎn)單的嵌入式操作系統(tǒng)了吧,它也無例外的能夠提供信號(hào)量和郵箱機(jī)制,只要我們將UC/OSII中的相關(guān)函數(shù)做相應(yīng)的封裝,就可滿足LWIP的需求。LWIP使用郵箱和信號(hào)量來實(shí)現(xiàn)上層應(yīng)用與協(xié)議棧間、下層硬件驅(qū)動(dòng)與協(xié)議棧間的信息交互。LWIP協(xié)議模擬了TCP/IP協(xié)議的分層思想,表面上看LWIP也是有分層思想的,但從實(shí)現(xiàn)上看,LWIP只在一個(gè)進(jìn)程內(nèi)實(shí)現(xiàn)了各個(gè)層次的所有工作。具體如下:LWIP完成相關(guān)初始化后,會(huì)阻塞在一個(gè)郵箱上,等待接收數(shù)據(jù)進(jìn)行處理。這個(gè)郵箱內(nèi)的數(shù)據(jù)可能來自底層硬件驅(qū)動(dòng)接收到的數(shù)據(jù)包,也可能來自應(yīng)用程序。當(dāng)在該郵箱內(nèi)取得數(shù)據(jù)后,LWIP會(huì)對(duì)數(shù)據(jù)進(jìn)行解析,然后再依次調(diào)用協(xié)議棧內(nèi)部上層相關(guān)處理函數(shù)處理數(shù)據(jù)。處理結(jié)束后,LWIP繼續(xù)阻塞在郵箱上等待下一批數(shù)據(jù)。當(dāng)然LWIP還有一大串的內(nèi)存管理機(jī)制用以避免在各層間交互數(shù)據(jù)時(shí)大量的時(shí)間和內(nèi)存開銷,這將在后續(xù)講解中慢慢道來。當(dāng)然,但這樣的設(shè)計(jì)使得代碼理解難度加大,這一點(diǎn)讓人頭大。信號(hào)量也可以用在應(yīng)用程序與協(xié)議棧的互相通信中。比如,應(yīng)用程序要發(fā)送數(shù)據(jù)了,它先把數(shù)據(jù)發(fā)到LWIP阻塞的郵箱上,然后它掛起在一個(gè)信號(hào)量上;LWIP從郵箱上取得數(shù)據(jù)處理后,釋放一個(gè)信號(hào)量,告訴應(yīng)用程序,你要發(fā)的數(shù)據(jù)我已經(jīng)搞定了;此后,應(yīng)用程序得到信號(hào)量繼續(xù)運(yùn)行,而LWIP繼續(xù)阻塞在郵箱上等待下一批處理數(shù)據(jù)。
其其次,就是與等待超時(shí)相關(guān)的函數(shù)。上面說到LWIP協(xié)議棧會(huì)阻塞在郵箱上等待接收數(shù)據(jù)的到來。這種等待在外部看起來是一直進(jìn)行的,但其實(shí)不然。一般在初始化LWIP進(jìn)程的時(shí)候,都會(huì)同時(shí)的初始化一些超時(shí)事件,即當(dāng)某些事件等待超時(shí)后,它們會(huì)自動(dòng)調(diào)用一些超時(shí)處理函數(shù)做相關(guān)處理,以滿足TCP/IP協(xié)議棧的需求。這樣看來,當(dāng)LWIP協(xié)議棧阻塞等待郵箱之前,它會(huì)精明的計(jì)算到底應(yīng)該等待多久,如果LWIP進(jìn)程中沒有初始化任何超時(shí)事件,那好,這種情況最簡(jiǎn)單了,永遠(yuǎn)的掛起進(jìn)程就可以了,這時(shí)的等待就可以看做是天長(zhǎng)地久的….有點(diǎn)曖昧了。如果LWIP進(jìn)程中有初始化的超時(shí)事件,這時(shí)就不能一直等了,因?yàn)檫@樣超時(shí)事件沒有任何被執(zhí)行的機(jī)會(huì)。LWIP是這樣做的,等待郵箱的時(shí)間設(shè)置為第一個(gè)超時(shí)事件的時(shí)間長(zhǎng)度,如果時(shí)間到了,還沒等到數(shù)據(jù),那好,直接跳出郵箱等待轉(zhuǎn)而執(zhí)行超時(shí)事件,當(dāng)執(zhí)行完成超時(shí)事件后,再按照上述的方法繼續(xù)阻塞郵箱。可以看出,對(duì)一個(gè)LWIP進(jìn)程,需要用一個(gè)鏈表來管理這些超時(shí)事件。這個(gè)鏈表的大部分工作已經(jīng)被LWIP的設(shè)計(jì)者完成了,使用者只需要實(shí)現(xiàn)的僅有一個(gè)函數(shù):該函數(shù)能夠返回當(dāng)前進(jìn)程個(gè)超時(shí)事件鏈表的首地址。LWIP內(nèi)部協(xié)議要利用該首地址來查找完成相關(guān)超時(shí)事件。

其其其次,如果LWIP是建立在多線程操作系統(tǒng)之上的話,則要實(shí)現(xiàn)創(chuàng)建一個(gè)新線程的函數(shù)。不支持多線程的操作系統(tǒng),汗…表示還沒聽過。不過UC/OSII顯然是支持多線程的,地球人都知道。這樣一個(gè)典型的LWIP應(yīng)用系統(tǒng)包括這樣的三個(gè)進(jìn)程:首先啟動(dòng)的是上層應(yīng)用程序進(jìn)程,然后是LWIP協(xié)議棧進(jìn)程,最后是底層硬件數(shù)據(jù)包接收發(fā)送進(jìn)程。通常LWIP協(xié)議棧進(jìn)程是在應(yīng)用程序中調(diào)用LWIP協(xié)議棧初始化函數(shù)來創(chuàng)建的。注意LWIP協(xié)議棧進(jìn)程一般具有最高的優(yōu)先級(jí),以便實(shí)時(shí)正確的對(duì)數(shù)據(jù)進(jìn)行響應(yīng)。
其其其其次,其他一些細(xì)節(jié)之處。比如臨界區(qū)保護(hù)函數(shù),用于LWIP協(xié)議棧處理某些臨界區(qū)時(shí)使用,一般通過進(jìn)臨界區(qū)關(guān)中斷、出臨界區(qū)開中斷的方式來實(shí)現(xiàn);又如結(jié)構(gòu)體定義時(shí)用到的結(jié)構(gòu)體封裝宏,LWIP的實(shí)現(xiàn)基于這樣一種機(jī)制,即上層協(xié)議已經(jīng)明確知道了下層所傳上來的數(shù)據(jù)的結(jié)構(gòu)特點(diǎn),上層直接使用相關(guān)取地址計(jì)算得到想要的數(shù)據(jù),而避免了數(shù)據(jù)遞交時(shí)的復(fù)制與緩沖,所以定義結(jié)構(gòu)體封裝宏,禁止編譯器的地址自動(dòng)對(duì)齊是必須的;還有諸如調(diào)試輸出、測(cè)量記錄方面的宏不做講解。
最后,也是比較重要的地方。底層網(wǎng)絡(luò)驅(qū)動(dòng)函數(shù)的實(shí)現(xiàn)。這取決于你嵌入式硬件系統(tǒng)所使用的網(wǎng)絡(luò)接口芯片,也就是網(wǎng)卡芯片,常見的有RTL8201BL、ENC28J60等等。不同的接口芯片廠商都會(huì)提供豐富的驅(qū)動(dòng)函數(shù)。我們只要將這些發(fā)送接收接口函數(shù)做相應(yīng)的封裝,將接收到得數(shù)據(jù)包封裝為L(zhǎng)WIP協(xié)議棧熟悉的數(shù)據(jù)結(jié)構(gòu)、將發(fā)送的數(shù)據(jù)包分解為芯片熟悉的數(shù)據(jù)結(jié)構(gòu)就基本搞定了。最起碼的,發(fā)送一個(gè)數(shù)據(jù)包函數(shù)和接收一個(gè)數(shù)據(jù)包函數(shù)需要被實(shí)現(xiàn)。
那就這樣了吧,雖然寫得草草,但終于在撤退之前搞定。好的開始是成功的一半,那這暫且先算四分之一吧。不曉得一個(gè)月、兩個(gè)月或者更多時(shí)間能寫完否。預(yù)知后事如何,請(qǐng)見下回分解。
愛華網(wǎng)


