uCOS平台下的LwIP移植.doc_第1页
uCOS平台下的LwIP移植.doc_第2页
uCOS平台下的LwIP移植.doc_第3页
uCOS平台下的LwIP移植.doc_第4页
uCOS平台下的LwIP移植.doc_第5页
已阅读5页,还剩57页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

此文档收集于网络,如有侵权,请联系网站删除1下載LwIP. 22建立一個最基本的工程. 23把LwIP加入工程. 24編寫作業系統類比層相關代碼. 34.1作業系統類比層移植說明中文翻譯 . 34.2編寫作業系統類比層. 64.2.1準備工作建立文件、定義資料類型及其它 . 64.2.2信號量操作函數. 84.2.3郵箱操作函數. 134.2.4實現sys_thread_new()函數 . 204.2.5實現sys_arch_timeouts()函數 . 224.2.6實現臨界保護函數. 254.2.7掃尾結束作業系統類比層的編寫 . 265LwIP介面初始設置及網路驅動. 285.1準備工作建立LwIP入口函數檔 . 285.2 ilvInitLwIP(). 295.3 ilvSetLwIP(). 305.4ethernetif_init()初始化底層介面 . 355.4.1ethernetif_init()函數分析 . 355.4.2low_level_output()鏈路層發送函數 . 365.4.3low_level_init()網卡初始化函數 . 385.4.4EMACInit()網卡初始化工作的實際完成者 . 405.4.5ethernetif_input()實現接收線程 . 475.4.6low_level_input()得到一整幀資料 . 495.4.7GetInputPacketLen()獲得幀長 . 505.4.8EMACReadPacket()複製,從接收緩衝區到pbuf . 535.4.9EMACSendPacket()發送一幀資料 . 555.4.10編譯ethernetif.c及lib_emac.c . 566ping結束LwIP的移植. 576.1編譯、鏈結整個工程. 576.2ping測試. 59後記. 62此文档仅供学习与交流本文將指導讀者一步步完成 LwIP 在 ADS1.2 開發環境下的移植工作,包括底層驅動的編寫。本文使用的 硬體平臺是 AT91SAM7X256 + RTL8201BL(PHY),至於軟體平臺,讀者從本文標題即可看出。我們使用 uC/OS-II 作為底層作業系統,而 LwIP 的移植亦將主要圍繞 uC/OS-II 展開。好了,不再多說,開始吧1 下載 LwIP很簡單,到LwIP的官方網站即可:/projects/lwip/。如果你不想看看其他內容 (可能對 你會很重 要 ),就只是想得到 源碼,好 的,直接 到這個地 址下載 : /releases/lwip/。目前官方發佈的最新版本是 1.1.1,找到 lwip-1.1.1.zip,然後下載、解壓縮,第一項工作完成。2 建立一個最基本的工程要想完成移植工作,我們必須要有一個包含 uC/OS-II 的工程才行,這一步我們就是要建立這個工程。 工程建立完畢後,編譯鏈結沒有問題,那麼,第二項工作也完成了。關於如何建立一個包含 uC/OS-II 的ADS 工 程的 問題,不在 本文描述範 圍之內,這 裏不做講述 。隨本筆記 一同發佈的 源碼文檔中 LwIPPortingTest_2 檔夾下包含了這個最基本工程的源碼,讀者可以直接使用。我的基本工程建立的 路徑是 D:workLwIPPortingTest,下文將以相對路徑進行講述,不再提供絕對路徑。3 把 LwIP 加入工程首先,在src文件夾下,建立 LwIP 文件夾,即:srcLwIP;然後將下載的 LwIP 源碼文件中 api、core、include、netif 文件複製到srcLwIP文件夾下,如下圖所示:圖 3.1然後,用 ADS 打開工程檔,按照 LwIP 源碼檔的實際存放路徑建立 LwIP 的工程結構,如下圖所示:圖 3.2這裏需要特別說明的是,源碼中的 IP V6、SLIP 及 PPP 部分我們沒有添加進來,主要是考慮我及大多數 讀者的網路還是 V4,而 SLIP、PPP 暫時不在我的考慮範圍之內。另外,在移植層面 V6 也和 V4 相差不多, 這裏就不再講解這部分內容了。現在基礎工程結構建立完畢,可以把 LwIP 源碼添加進來了。這一步很容 易,按照檔存放路徑,將源碼檔添加到相應的工程結構下即可。源碼添加完成後的工程參見所附源 碼檔的 LwIPPortingTest_3 文件夾。4 編寫作業系統類比層相關代碼LwIP 的作者為作業系統類比層提供了較為詳細的說明,檔案名為 sys_arch.txt,在 LwIP 的 doc 文 件夾下。我們的編寫工作根據這個說明進行。4.1作業系統類比層移植說明中文翻譯事先聲明,之所以筆者要翻譯該文檔,主要是筆者在撰寫這篇筆記時亦沒有通讀該文檔。筆者先前 使用的模擬層源碼是楊曄大俠的。為了真正弄懂 LwIP,筆者決定自己重新實現 LwIP 的移植,本筆 記是跟隨移植同步進行的,因此,翻譯的文檔也放在了這篇筆記中,使讀者能夠真正瞭解筆者的移 植歷程。另外再說一句,這個文檔是為 LwIP 0.6+版編寫,筆者搜遍了整個 LwIP 官方網站,沒有 發現比這更新的,筆者只好認為作業系統類比層在 0.6+之後沒有任何改動,如果有誰發現了更新 的,一定通知筆者,先謝謝了。好的,言歸正傳,下面就是譯文:LwIP 0.6+ sys_arch 介面作者:Adam Dunkels作業系統類比層(sys_arch)存在的目的主要是為了方便 LwIP 的移植,它在底層作業系統和LwIP 之間提供了一個介面。這樣,我們在移植 LwIP 到一個新的目標系統時,只需修改這個介面即可。不過,不依賴底層作業系統的支援也可以實現這個介面。sys_arch 需要為 LwIP 提供信號量(semaphores)和郵箱(mailboxes)兩種進程間通訊方式(IPC)。如果想獲得 LwIP 的完整功能,sys_arch 還必須支持多線程。當然,對於僅需要基本功能的用戶來說,可以不去實現多線程。LwIP 以前的版本還要求 sys_arch 實現計時器調度,不過,從 LwIP0.5開始,這一需求在更高一層實現。除了上文所述的 sys_arch 原始檔案需要實現的功能外,LwIP 還要求用戶提供幾個頭檔,這幾個頭檔包含 LwIP 使用的巨集定義。下文將詳細講述 sys_arch 及頭文件的實現。信號量即可以是計數信號量,也可以是二值信號量LwIP 都可以正常工作。郵箱用於消息傳遞,用戶即可以將其實現為一個佇列,允許多條消息投遞到這個郵箱,也可以每次只允許投遞一個消息。這兩種方式 LwIP 都可以正常運作。不過,前者更加有效。需要用戶特別注意的是投遞到郵箱中的消息只能是一個指標。在 sys_arch.h 檔中,我們指定資料類型“sys_sem_t”表示信號量,“sys_mbox_t”表示郵箱。至於 sys_sem_t 和 sys_mbox_t 如何表示這兩種不同類型,LwIP 沒有任何限制。以下函數必須在 sys_arch 中實現:- void sys_init(void)初始化 sys_arch 層。- sys_sem_t sys_sem_new(u8_t count)建立並返回一個新的信號量。參數 count 指定信號量的初始狀態。- void sys_sem_free(sys_sem_t sem)釋放信號量。- void sys_sem_signal(sys_sem_t sem)發送一個信號。- u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)等待指定的信號並阻塞線程。timeout 參數為 0,線程會一直被阻塞至收到指定的信號;非 0,則線程僅被阻塞至指定的 timeout 時間(單位為毫秒)。在 timeout 參數值非 0 的情況下,返回值為等待指定 的信號所 消耗的毫 秒數。如 果在指定 的時間內 並沒有收 到信號, 返回 值 為SYS_ARCH_TIMEOUT。如果線程不必再等待這個信號(也就是說,已經收到信號),返回值也可以為 0。注意,LwIP 實現了一個名稱與之相似的函數來調用這個函數,sys_sem_wait(),注意區別。- sys_mbox_t sys_mbox_new(void)建立一個空的郵箱。- void sys_mbox_free(sys_mbox_t mbox)釋放一個郵箱。如果釋放時郵箱中還有消息,它表明 LwIP 中存在一個編程錯誤,應該通知開發者(原文如此,這句話很費解。個人理解的意思是:當執行 sys_mbox_free()這個函數時,按道理郵箱中不應該再存在任何消息,如果用戶使用 LwIP 時發現郵箱中還存在消息,說明 LwIP 的開發者存在一個編程錯誤,不能把郵箱中的消息全部取出並處理掉。遇到這種情況,用戶應該告訴 LwIP 的作者,糾正這個 bug,譯注)。- void sys_mbox_post(sys_mbox_t mbox, void *msg)投遞消息“msg”到指定的郵箱“mbox”。- u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void *msg, u32_t timeout)阻塞 線程直至 郵箱收到 至少一條 消息。最 長阻塞時 間由 timeout 參 數指 定(與sys_arch_sem_wait()函數類似)。msg 是一個結果參數,用來保存郵箱中的消息指標(即*msg = ptr),它的值由這個函數設置。“msg”參數有可能為空,這表明當前這條消息應該被丟棄。返回值與 sys_arch_sem_wait()函數相同:等待的毫秒數或者 SYS_ARCH_TIMEOUT如果時間溢出的話。LwIP 實現的函數中,有一個名稱與之相似的sys_mbox_fetch(),注意區分。- struct sys_timeouts *sys_arch_timeouts(void)返回一個指向當前線程使用的 sys_timeouts 結構的指標。LwIP 中,每一個線程都有一個timeouts 鏈表,這個鏈表由 sys_timeout 結構組成,sys_timeouts 結構則保存了指向這個鏈表的指針。這個函數由 LwIP 的超時調度程式調用,並且不能返回一個空(NULL)值。單線程 sys_arch 實現中,這個函數只需簡單返回一個指標即可。這個指標指向保存在 sys_arch 模塊中的 sys_timeouts 總體變數。如果底層作業系統支援多線程並且 LwIP 中需要這樣的功能,那麼,下面的函數必須實現:- sys_thread_t sys_thread_new(void(*thread)(void *arg), void *arg, int prio)啟動一個由函數指標 thread 指定的新線程,arg 將作為參數傳遞給 thread()函數,prio 指定這個新線程的優先順序。返回值為這個新線程的 ID,ID 和優先順序由底層作業系統決定。- sys_prot_t sys_arch_protect(void)這是一個可選函數,它負責完成臨界區域保護並返回先前的保護狀態。該函數只有在小的臨界區域需要保護時才會被調用。基於 ISR 驅動的嵌入式系統可以通過禁止中斷來實現這個函數。基於任務的系統可以通過互斥量或禁止任務來實現這個函數。該函數應該支援來自於同一個任務或中斷的遞迴調用。換句話說,當該區域已經被保護,sys_arch_protect()函數依然能被調用。這時,函數的返回值會通知調用者該區域已經被保護。如果你的移植正在支援一個作業系統,sys_arch_protect()函數僅僅是一個需要。- void sys_arch_unprotect(sys_prot_t pval)該函數同樣是一個可選函數。它的功能就是恢復受保護區域的先前保護狀態,先前是受到保護還是沒有受到保護由參數 pval 指定。它與 sys_arch_protect()函數配套使用,詳細資訊參看sys_arch_protect()函數。該函數的說明是按照譯者個人理解的意思翻譯,原文講述不是很清楚,如有錯誤,歡迎批評指正,譯注。-OS 支援的類比層需要添加的頭檔說明- cc.h與硬體平臺及編譯器相關的環境變數及資料類型聲明檔(一些或許應該移到 sys_arch.h文件)。LwIP 使用的資料類型定義u8_t, s8_t, u16_t,s16_t,u32_t,s32_t,mem_ptr_t。與編譯器相關的 LwIP 結構體封裝巨集:PACK_STRUCT_FIELD(x)PACK_STRUCT_STRUCTPACK_STRUCT_BEGINPACK_STRUCT_END與平臺相關的調試輸出:LWIP_PLATFORM_DIAG(X)- 非故障,輸出一條提示資訊。LWIP_PLATFORM_ASSERT(x) - 故障,輸出一條故障資訊並放棄執行。“輕便的(lightweight)”同步機制:SYS_ARCH_DECL_PROTECT(x)- 聲明一個保護狀態變數。SYS_ARCH_PROTECT(x)- 進入保護模式。SYS_ARCH_UNPROTECT(x)- 脫離保護模式。如果編譯器不提供 memset()函數,這個檔必須包含它的定義,或者包含(include)一個定義它的文件。這個檔要麼包含一個本地系統(system-local)提供的頭檔這個檔定義了標準的*nix 錯誤編碼,要麼增加一條巨集定義語句:#defineLWIP_PROVIDE_ERRNO,這將使得lwip/arch.h 頭檔來定義這些編碼。這些編碼被用於 LwIP 的各個部分。- perf.h定義了性能測量使用的巨集,由 LwIP 調用,可以將其定義為一個空的宏。PERF_START- 開始測量。PERF_STOP(x)- 結束測量並記錄結果。- sys_arch.hsys_arch.c 的頭文件。定義 Arch(即整個移植所依賴的作業系統平臺,譯注)需要的資料類型:sys_sem_t,sys_mbox_t,sys_thread_t,以及可選類型:sys_prot_t。sys_mbox_t 和 sys_sem_t 變數的 NULL 值定義:SYS_MBOX_NULLNULLSYS_SEM_NULLNULL4.2編寫作業系統類比層4.1 節已經明白的講述了如何實現 sys_arch 介面,我們按照這個說明完成即可。4.2.1 準備工作建立文件、定義資料類型及其它在 ADS 工程 LwIP 組中添加一個新組 arch 並在這個組下面建立原始檔案 sys_arch.c,實際存 放路徑亦如此組織,如下圖所示:圖 4.2.1然後在 LwIP/include 組同樣建立一個新組 arch,在 arch 組建立新檔 sys_arch.h 及 cc.h, 如下圖示:圖 4.2.2 檔建立完成,我們先實現資料類型定義,這個實現完全按照移植說明一文進行。首先在cc.h 檔中定義常用的資料類型。這些常用資料類型不僅類比層介面函數使用,底層協定棧實 現亦要使用。在 cc.h 文件中添加如下語句(參見 LwIPPortingTest_4 文件夾下的 cc.h 文件): typedef unsigned charu8_t;typedef chars8_t; typedef unsigned short u16_t; typedef shorts16_t; typedef unsigned intu32_t; typedef ints32_t; typedef u32_tmem_ptr_t;在上面的資料類型定義中,除了 mem_ptr_t 之外其他類型均很直觀,不需解釋。至於 mem_ptr_t為什麼指定為 u32_t,而不是像它的名稱所表現的一樣將其指定為指針呢?其實原因很簡單, 筆者在定義它時首先找到了使用它的相關語句,從這些語句中才確定這樣聲明。讀者可找到 mem.h 文件看看裏面有關 mem_ptr_t 的使用語句就能明白怎麼回事。好了,不再多說,讓我們 的準備工作接著進行。在 sys_arch.h 文件中添加如下語句:typedef HANDLER sys_sem_t;其中 HANDLER 是筆者本人自定義的一個宏,它是為了方便 uC/OS-II 的使用定義的。讀者可以在 所附源碼檔srcuCOS_IIAPIos_api.h 中找到相關定義:typedef OS_EVENT*HANDLER;它實際上就是指向 uCOS 中 OS_EVENT 結構的指標。 聲明相關資料類型的語句添加完成後,我們再把這兩個檔 sys_arch.h 和 cc.h 添加到sys_arch.c 檔中,以使該檔裏的相關函數能夠使用這些新定義的資料類型:#include/LwIP/include/arch/cc.h#include/LwIP/include/arch/sys_arch.h這裏一定要注意順序,先包含 cc.h 檔,再包含 sys_arch.h 檔,因為 sys_arch.h 檔中有 些語句需要用到 cc.h 檔中的類型聲明。好了,準備工作已經完成,現在開始編寫介面函數。4.2.2 信號量操作函數相關函數實現讀者也可直接參看 sys_arch.c 文件。- sys_new_sem()/*-/* 函數名稱 : sys_sem_new/* 功能描述 : 建立並返回一個新的信號量/* 入口參數 : in 指定信號量的初始狀態/* 出口參數 : 返回新的信號量/*-sys_sem_t sys_sem_new(u8_t count)return OSAPISemNew(count);這個函數的實現其實很簡單,因為 uC/OS-II 提供了信號量,我們只需直接調用建立信號量 的相關函數就行了。上面的源碼中 OSAPISemNew 是筆者本人為了統一對 OS 底層函數的調用重新 定義的一個介面函數,這個介面函數負責調用 OS 底層函數完成相應功能。在後面的類比層介面 函數實現中,筆者使用了很多這樣的 API。這些介面函數都以 OSAPI 作為函數名首碼,其實現 細節請參看srcuCOS_IIAPIos_api.c 檔,本文不再贅述。- sys_sem_signal()/*-/* 函數名稱 : sys_sem_signal/* 功能描述 : 發送信號/* 入口參數 : in sem 指定要發送的信號/* 出口參數 : 無/*-void sys_sem_signal(sys_sem_t sem)OSAPISemSend(sem);這個函數就不再多說了,與 sys_sem_new()函數的實現機制相同。- sys_sem_free()/*-/* 函數名稱 : sys_sem_free/* 功能描述 : 釋放信號量/* 入口參數 : in 指定要釋放的信號量/* 出口參數 : 無/*-void sys_sem_free(sys_sem_t sem)OSAPISemFreeExt(sem);與前兩個函數相似,不再贅述。- sys_arch_sem_wait()/*-/* 函數名稱 : sys_arch_sem_wait/* 功能描述 : 等待由參數 sem 指定的信號並阻塞線程/* 入口參數 :in sem 指定要發送的信號/*: in 指定等待的最長時間(單位為毫秒)。為 0,線程會一直/*:被阻塞直至收到指定的信號;非 0,指定線程最長等待時/*:間/* 出口參數 : -0: 在指定時間內等到指定信號/*: - SYS_ARCH_TIMEOUT: 在指定時間內沒有等到指定信號/*-u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)if(OSAPISemWait(sem, timeout) = OS_NO_ERR)return 0;elsereturn SYS_ARCH_TIMEOUT;實現信號量等待的 OS 底層函數是 OSSemPend()。打開源碼文件srcuCOS_IIOs_sem.c, 找到這個函數,我們會發現它與我們要實現的函數還是有些差別的:其一、OSSemPend()函數沒 有返回值,它使用了一個結果參數 err 來代替返回值;其二、OSSemPend()函數的執行結果與 sys_arch_sem_wait()函數的執行結果(返回值)不相同,OSSemPend()函數有 5 種執行結果,而 sys_arch_sem_wait()函數只有兩種結果;其三、timeout 參數的時間單位不同,一個是時鐘 節拍數(OSSemPend()),另一個則是毫秒數(sys_arch_sem_wait())。這些差別的存在導致我 們在調用 OSSemPend()函數時必須根據需求作出相應的調整。讀者可以看到,在上面的代碼實現中,筆者使用了自定義的 OS 介面函數來代替對 OSSemPend()函數的直接調用。這個介面函數與 sys_arch_sem_wait()函數相比不存在一、三這兩種差別。唯一的差別就是返回值,OSAPISemWait()函數仍然保留了 OSSemPend()函數的全部 5種執行 結果 ,而 LwIP 只關心 兩種 結果: 等到 指定信 號或 者超時 。筆 者為了 省事 , sys_arch_sem_wait()函 數隻判斷 是否等到 指定信號 ( OSAPISemWait(sem, timeout) = OS_NO_ERR),其他情況一律視為超時(實際上除了超時外,另外三種屬於程式的 BUG,一個運 行穩定的系統不應該存在),返回 SYS_ARCH_TIMEOUT。另外,按照筆者個人的理解(移植說明一文講述不是很清楚),在指定時間內等到指定信號 時,函數即可以返回實際等待的毫秒數也可以返回 0,為了編程方便,筆者選擇返回 0。既然移植說明一文告訴我們等待超時要返回 SYS_ARCH_TIMEOUT,那麼在 LwIP 中一定會存 在這個巨集定義。使用 SYS_ARCH_TIMEOUT 作為關鍵字搜索整個 ADS 工程(CTRL+SHIFT+M)發現 sys.h 定義了這個宏,因此,sys_arch.c 檔還需要包含這個檔:#include/LwIP/include/lwip/sys.h 把上面的語句添加到 sys_arch.c 中,如下圖示:圖 4.2.3好了,編寫到這裏,讓我們先把這個檔編譯一下,看看有什麼問題。讀者可以使用 ADS 直接 打開所附文檔LwIPPortingTest_4LwIPPortingTest_4_1(它是直接反映本文當前移植進度的 階段性文檔),找到 sys_arch.c 檔進行編譯。編譯完成,呵,錯誤真多,28 個,見下圖:圖 4.2.4 不怕,老話說的好:兵來將擋,水來土掩。跟著錯誤說明,讓我們一個個地消滅。第一個錯誤是說無法打開 lwipopts.h 檔,出錯的地方在 opt.h 文件。我們看看 opt.h 檔中關於lwipopts.h 文件的描述,如下圖示:圖 4.2.5上圖圈其來的注釋語句就是對 lwipopts.h 文件的說明。很簡單,它是用戶自定義的配置檔, 我們不需要這個,因此沒有建立這個檔,編譯器找不到它,只好報錯了。解決方法很簡單, 我們把這條語句注釋掉即可,如下圖示:圖 4.2.6再編譯看看,還有多少個錯誤,還有 27 個,少了一個,呵呵,進步啊。好,讓我們繼續進步 下面這個錯誤是說“sys_mbox_t”缺少相關類型聲明。移植說明一文已經提到過 sys_mbox_t, 其用來表示一個郵箱,暫時還用不到,所以在這裏先將其隨便聲明為一種資料類型,先編譯通 過,以後再說。根據移植說明,我們在 sys_arch.h 頭文件裏定義它,打開 sys_arch.h,添加 如下語句:typedef HANDLER sys_mbox_t; 見圖示:圖 4.2.7 編譯,出乎意料,還剩三個錯誤。太好了,已經看到勝利的彼岸了。剩下的這幾個錯誤,核心問題與上一個問題一樣,缺少聲明,這次是“sys_thread_t”。它是新線程的 ID,在 uCOS 中,ID 號實際就是優先順序,也就是 0-63 之間的數字,所以在 sys_arch.h 文件中添加如下語句:typedef u8_t sys_thread_t; 如下圖所示:圖 4.2.8 再編譯,沒有任何錯誤,通過。好了,信號量相關的操作函數已經完成,下面的工作就是實現郵箱操作函數。4.2.3 郵箱操作函數在動手實現之前,讓我們再回顧一下移植說明一文關於郵箱的描述:“郵箱用於消息傳遞, 用戶即可以將其實現為一個佇列,允許多條消息投遞到這個郵箱,也可以每次只允許投遞一個 消息,這兩種方式 LwIP 都可以正常運作。不過,前者更加有效。需要用戶特別注意的是投 遞到郵箱中的消息只能是一個指標。“從這個描述中,我們得到兩條有價值的資訊:其一、郵箱 一次能夠接收多條消息比僅接收一條消息更加有效;其二、郵箱中的消息是一個指標。先說說第一條。很顯然,使用消息佇列帶來的性能提升是影響我們選擇的關鍵因素,我們 並不希望使用一個打了折扣的 LwIP。其實,在 uCOS 中實現消息佇列很簡單,它提供了非常豐 富的消息佇列管理函數,我們只需在此基礎上實現即可。這也是影響我們如此選擇的一個重要 因素。對於第二條,其帶給我們的資訊很直白,不需贅述,唯一需要交待的是uCOS 中投遞 到郵箱中的消息也是一個指標,這對我們來說實在是一個大好消息。好了,言歸正傳,下面談談筆者本人的設計思路,為讀者抛磚引玉。 我的想法是系統可以同時建立多個郵箱,這些郵箱通過一個單向鏈錶鏈接在一起。每個郵箱一次可以接收多條消息,接收消息的最大數量由消息陣列的大小決定。如下圖所示:圖 4.2.9我們在系統初始化階段完成建立郵箱和鏈表的工作,並把鏈表的首位址保存到總體變數pstCurFreeMbox 指針中。申請建立一個新郵箱時,我們先看看 pstCurFreeMBox 是否為空值。為空,表明鏈表已經沒 有空閒節點,無法滿足申請。不為空,則立即將 pstCurFreeMBox 指向的當前節點從鏈表中取出 交給申請者使用,pstCurFreeMBox 指向下一個空閒節點(見圖 4.2.10)。在系統第一次申請的 時候,pstCurFreeMBox 指向節點 1,申請完畢,節點 1 與鏈表斷開鏈結,整個鏈表減少了一個 節點。這時,pstCurFreeMBox 指標被賦值為節點 1 的 pstNext 值,其向後移動了一個節點,指 向了節點 2。以此類推,假設系統只能申請建立郵箱,不能撤銷申請將郵箱歸還給鏈表,這將 使得鏈表逐漸變短。當 pstCurFreeMBox 指針被賦值為節點 N 的 pstNext 值時,這意味著 pstCurFreeMBox 指標已經越過了鏈表的最後一個節點,鏈表的長度縮減為 0,鏈表不再可用。 這時,如果系統再有新的申請產生,該申請將被拋棄。如果我們建立的鏈表長度過短,當網路 系統繁忙導致佔用的郵箱不能及時歸還時,很有可能出現鏈表不可用的情況。這將直接導致網 絡系統的回應時間變長甚至不再回應。這對於一個設計可靠的系統來說,是一個很嚴重的問題。 要想避免這個問題,就必須對網路系統的負荷能力作出正確的評估,從而為我們選擇一個合理 的鏈表長度提供判斷依據。筆者對這個問題的解決方法是:在申請郵箱的函數中增加記錄鏈表 不可用次數的代碼,然後類比各種情況對目標系統進行盡可能多的併發訪問,不斷調整鏈表長 度,直至不可用次數正好為 0。最後,把當前鏈表長度再增加一定數量以應付意外情況的發生(一般再增加 25%-50%的長度即可),就得到了我們想要的值。 郵箱使用完畢,歸還給鏈表的過程參見圖 4.2.10 歸還節點部分。歸還節點實際上就是重建鏈表,鏈表會從無到有,從短到長,逐漸恢復原來的樣子。而 pstCurFreeMBox 指標的移動方向 正好與申請時相反,它是從鏈表的尾端向前移動。不過,這裏需要特別注意的是,鏈表的重建 將會打亂鏈表最初的節點順序,正如圖 4.2.10 所示的一樣。為什麼會出現這種情況呢?原因很 簡單,雖然申請節點時我們是從鏈表的首部開始順序申請,但是申請者佔用各個節點的時間是 不相同的,有的長有的短,與申請順序無關。最先申請的節點有可能最後被歸還,而最後申請 的節點則有可能最先被歸還。因為 pstCurFreeMBox 指標始終指向最後被歸還的節點,也就是鏈 表的首位址,因此,最後申請的節點成為了鏈表首部的節點,而原先首部的節點則成了重建後 鏈表的尾部節點。圖 4.2.10 很直觀的描述了這一過程。善於思考的讀者可能還會發現這種處理 機制的一個特點,假設系統只申請了一個郵箱,然後歸還,然後再申請,那麼再申請的郵箱還 是最先申請的那個郵箱。也就是說,如果系統最多只需要三個郵箱,而我們的鏈表共有五個郵 箱,那麼鏈表中的後兩個郵箱將一直不被使用,而前三個郵箱則使用頻繁。鏈表中的前三個節 點順序將不斷的重新排列(123、321、231),而節點 4 和節點 5 則會一直保持這個順序。 不過不用擔心,這種情況除了造成一定的記憶體浪費之外(當然,如果我們的鏈表長度合適,這 個問題也可避免),不會對系統的穩定產生任何問題。另外,鏈表建立在 RAM 而不是 FLASH 中, 我們更不用考慮頻繁擦寫固定區域對記憶體造成的傷害。圖 4.2.10 不知讀者是否還記得在講解信號量操作函數時,我們曾經提到過用於表示郵箱的自定義數據類型 sys_mbox_t。當時我們只是簡單的為其作了類型定義,並沒有考慮是否能夠滿足實際應 用。春去春回、鬥轉星移,而現在我們對於如何實現郵箱已經了然於胸。前文已經說過,鏈表 中的每一個節點就是一個郵箱,用 C 語言來描述郵箱實際上就是一個結構體。sys_mbox_t 是用來表示郵箱的,而郵箱就是結構體,顯然 sys_mbox_t 應該聲明為結構體。為了操作簡便, 我們亦可以將其聲明為指向這個結構體的指標。現在我們要做的就是實現這個結構體,然後再將 sys_mbox_t 聲明為指向這個結構體的指標,完成郵箱操作的基本單元構建工作。實現這個結 構體的工作很簡單,聰明的讀者一定會發現筆者已經在前文實現了這個結構體,只是沒有明說 而已。沒錯,圖 4.2.9 已經很直觀的給出了結構體成員列表,我們用代碼實現即可。打開 sys_arch.h 檔,在這個檔裏定義這個結構體並聲明 sys_mbox_t,相關代碼如下:#define MBOX_SIZE16/* 指定郵箱能夠接收的消息數量#define MBOX_NB8/* 指定郵箱個數,也就是鏈表長度/* LwIP 郵箱結構 */typedef struct stLwIPMBoxstruct stLwIPMBox*pstNext;HANDLERhMBox;void*pvaMsgsMBOX_SIZE; ST_LWIP_MBOX, *PST_LWIP_MBOX;typedef PST_LWIP_MBOX sys_mbox_t;/* LwIP 郵箱有了 sys_mbox_t,我們就可以建立一個 sys_mbox_t 類型的陣列,為鏈表中的各個節點分 配最基本的存儲空間。打開 sys_arch.c 檔,建立一個具有全局屬性的 ST_LWIP_MBOX 陣列, 陣列大小由 MBO

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论