2009年4月6日 星期一

endian & alignment --轉貼


To be TCP/IP or not to be ?

by http://playstation2.idv.tw/garyhu.jpg

不幸的是,這個問題應該是在你的產品中,你需要多複雜的TCP/IP功能?

TCP/IP是一個廣義的通訊協定,他事實上是一個網際網路通訊協定家族的總稱,其中包含了許多的通訊協定,例如IPTCPUDP…等,我們可以參考一下圖一大概的列出整個TCP/IP協定家族之間的關聯性。例如TCPUDP是建構在IP之上的協定,而FTP又是透過TCP來達到傳輸檔案的功能。

圖一、TCP/IP家族

嵌入式系統(Embedded System)在最近這一股資訊家電的潮流中,突然地受到大家的重視,特別是PDA、股票機廠商的業績比起正統電腦設備廠來得亮麗的時候,大家都很好奇這麼樣的小東西為什麼會這麼受歡迎?於是專家們宣告後PC時代來臨。

筆者不在這邊討論這種後PC時代的問題,今天的主題是在嵌入式系統中加入TCP/IP功能的必要性,除了當紅的PDA之外,其實許多藏在生活周遭的裝置裡也存在著許多嵌入式系統,而在嵌入式系統中,往往需要基本的通訊功能,從簡單的RS-232通訊、銀行提款機的專線連接、到複雜許多的手機通話,很明顯的嵌入式系統擺脫不了通訊的功能,不過如果使用者打算的是透過您的產品連接到廣大的Internet,那除了TCP/IP之外,別無選擇,特別是在現今這個Internet橫行的時代裡,如果您不連上Internet就落伍的情況。所以這個問題您應該換另外一個角度來看,真正的問題應該是在你的產品中,你需要多複雜的TCP/IP功能?。於是我們可以看到國內幾家以電子字典打下江山的廠商,從去年開始,強打上網功能的廣告,大都是透過PC將所需要的資料下載,再透過同步功能傳到電子字典裡,這樣的功能表示了無限擴充,筆者手邊有一台哈x族,正是這樣功能的典範之作,不過我們注意到一件事情,電子字典本身並沒有直接上網,是以一種間接的方式取的網路上的資料,很快的,這樣的方式就會因為無線寬頻時代的來臨,而轉變成電子字典直接上網。舉另外一個例子來說,創下國內PDA模範生的產品︰股票機,目前是透過BBCall的方式來取得單方傳遞訊息,若是將來使用者打算直接透過股票機來交易呢?股票機廠商難道沒有看到這樣廣大的商機嗎?事實上是有的,以神乎x機來說,WAP就是這樣的解決方案,目前正如火如荼的進行移植測試的工作。

看看全世界引為龍頭的PalmWinCE,上網功能早已經內含,就算是歐洲遠征出軍的EPOC(手機上普遍所見到的嵌入式作業系統)也是將TCP/IP列為標準的功能,如果您是打算發展嵌入式系統產品的廠商,是否應該將TCP/IP加到您的產品中?我想答案已經很明顯了。不過嵌入式系統是個非常特殊的系統,他事實上是一種『量身訂做』的系統,也許甲廠商跟乙廠商用的是同一個嵌入式作業系統,不過產品可能長得非常不一樣,例如現在許多的PDA是用摩托羅拉(Motolora)所生產的Dragon Ball EZ或是VZ系列的CPU,所以可能順便就使用摩托羅拉所提供的PPSM(Personal Portable System Manager)作業系統,可是一個做出PDA,一個作成電子字典,使用者就可能覺得這是不一樣的系統,因此嵌入式系統必須具有很大的修改空間,特別是使用者視窗部份,廠商必須針對用途、針對客戶群去對整個使用界面做調整,而不是說任何一個寫好的視窗就直接套過來用。

其實PPSM其實功能也算蠻完整的,可是目前並沒有殺手級的產品,因此知名度不如PalmOS,也沒有Microsoft會行銷包裝,所以知名度也不如WinCE,這是題外話。

圖二、Palm的標誌

圖三、EPOC的標誌

圖四、PPSM的標誌

另外就是嵌入式系統都是燒在ROM裡的,ROM的空間可不像PC的硬碟空間那樣的大,記憶體也相對的少了許多,就像筆者的哈x族,每次想裝個新東西進來,就得要把另一個軟體刪除掉,人家的記憶體空間,不管是RAM或是ROM可都是寸土寸金,因此所以就算是TCP/IP,我們需要整包的協定都包進來嗎?

以如果要發展一個WAP瀏覽器為例,事實上目前通用的版本只需要UDP協定就夠了,雖然說新版的WAP會連TCP都用上,不過等到真的出來再說,我們可以很明顯的看到事實上我們不需要一個很複雜的TCP/IP家族,只要兩三個協定,就夠我們享用不盡了。談到這邊,筆者描述一下筆者過去的發展經驗,也許可以提供當作讀者一個發展的經驗借鏡。

當初筆者所用的發展系統TICKERS,原本也是不具有TCP/IP功能的作業系統,不過這是一種單一定址空間、多執行緒的作業系統,挺適合發展嵌入式系統的,簡單而不失完整是他最大的好處,沒聽說過這套作業系統?沒關係,嵌入式作業系統多的是像這種聽都沒聽過的OS,就算是最有名的如pSOS(現在已經被WinRriver併購並且和VxWORKS合為一體了),我想聽過的讀者也應該不多。因為某些因素,筆者必須在這個作業系統上發展出專用的TCP/IP通訊協定,一開始當然找了許多的資料,例如大學時代修課的課本,心裡想想,反正這種東西發展了幾十年了,應該可以參考的原始程式多得數不清,最不濟也有Linux或是BSD TCP/IP stack的原始碼來參考,反正都是free的,總不會慘到自己重頭開始寫吧?

當 然,開發一個算是蠻複雜的系統時,一定要先想好怎麼去進行,尤其是嵌入式系統要求精簡、確實,不可以隨便多或是少,我們會仔細想好界面的需求,並考量到以 後移植到其他的硬體時所需要作的修改,就像下面這張圖一樣,我們會將所有該分層的分開,避免每次換到新的產品上就要花很大的心力去作修改。

圖五、開發網路界面架構圖

準備規劃的動作做完了、稍微看了一下課本,重新喚起當年的記憶之後,就開始準備著手移植BSD TCP/IP stack,這下子不得了了,這是一個非常巨大的工程,非常不容易了解還是其次,事實上是很不容易縮小成我們想要的系統,筆者只不過是要移植WAP瀏覽器所需要的UDP協定,真的有必要去跟這巨大的怪物搏鬥嗎?筆者的朋友很同情我,介紹我去找一下xlinu系統上的TCP/IP協定來研究,於是乎筆者很用心的研究了整個xlinu上的TCP/IP運作,順便後悔當初上課的時候根本有聽沒有懂,xlinu系統上的TCP/IP協定是一個需要開6個執行緒去跑的TCP/IP系統,這樣的東西適合放在嵌入式系統裡嗎?其實不管他照放,也是沒關係,因為整個系統的表現程度還算可以接受,但是從原本是工作站的系統,直接放到一個小小類似PDA的系統上,似乎有那麼一點點不對勁。(Linux何嘗不是?)

談到這邊,筆者先來描述一下所用到的發展環境,所用的作業系統,前面已經提到就是TICKERS,所用的CPUMotorola DragonBall MC68EZ328,這是目前相當普遍的68K系列的CPU,知名的Palm就是採用這個種類的CPU,雖然他是一種32位元的CPU,但是運算執行速度並不快,若是系統設計得好的話,省電是他的一大特色,另外筆者的發展平台還包含了4MB RAM2MB flashROM,因此很明顯可以看出來,這是不太適合一口氣開一大堆執行緒在上面執行的一個環境。

當然進行移植的時候,碰到一大堆的問題,例如DragonBall CPU是一種big endianCPU,普通的x86 CPU是屬於little endianCPU,所以我們在處理多位元的數值,必須要特別做數值的轉換處理。

什麼是big endianCPU?那什麼是little endianCPU?其實這只是取值的方法不同的系統,像big endianCPU,如果你是想要取得多位元的數值,例如取得一個short的值,我們知道short是兩個byte所組成的值,當一個記憶體中顯示為『0x01 0x02』的值,在big endianCPU會視為12,但是在little endianCPU會視為21。當然像是long的話,也是剛好相反﹔不過在字元處理上,我們知道一個字元就是一個byte,處理方式兩者是一樣的,還好一樣,不然做不下去了。我用下面的例子再為各位讀者說明一下,這是以x86 CPU(little endianCPU)為解釋的範例,若是程式長得像這樣︰

char c1 = 1;

char c2 = 2;

short s = 255; // 0x00FF

long l = 0x44332211;

在記憶體直接顯示出來就像這樣︰

offset

Memory dump

0x0000

01

02

FF

00

0x0004

11

22

33

44

看出來了嗎?剛好是顛倒過來的表示方式。

另外可能會碰到的問題就是byte alignment的問題,像是DragonBall CPU2bytes alignmentCPU,所以一旦存取到奇數位元的值,就會發生CPU例外錯誤的發生,簡單一點的講法就是當機了,下次再仔細為讀者介紹這一部份的發生狀況。所以移植的時候,會需要針對不同CPU的特性,特別作調整,仔細一點的程式設計師就會特別對需要這些處理的情況做出定義或是巨集來解決這樣的問題,例如我在處理htons()htonl()ntohs() ntohl()這四個TCP/IP常用的函式裡就定義如下︰

#if defined(BIG_ENDIAN)

#define htons(A) (A)

#define htonl(A) (A)

#define ntohs(A) (A)

#define ntohl(A) (A)

#elif defined(LITTLE_ENDIAN)

#define htons(A) ((((A) & 0xff00) >> 8) | \

((A) & 0x00ff) <<>

#define htonl(A) ((((A) & 0xff000000) >> 24) | \

(((A) & 0x00ff0000) >> 8) | \

(((A) & 0x0000ff00) <<>

(((A) & 0x000000ff) <<>

#define ntohs htons

#define ntohl htohl

#else

#error "One of BIG_ENDIAN or LITTLE_ENDIAN must be #define'd."

#endif

看得出來這四個函式在做些什麼呢?原來就是把多位元數值(例如short或是long)的位元作前後掉換的動作。因為一個標準的網路封包內的格式是固定的,不會因為你的工作環境CPU的不同而有所變動,否則很難達到所有電腦都連上網路的效果。因此他全都是以類似big endian的方式存在,如果在x86電腦這種little endian的系統上,就必須作轉換的動作,不然x86電腦會誤判這個數值。

這是處理這樣問題常用的技巧,如果讀者拿到一個號稱可以跨平台的程式,一定會發現到處都是定義(#define),因為每個平台用的表達方法全都不一樣,如果用的編譯器又很笨,例如筆者現在用的SDS C Compiler,實在是不太聰明,什麼動作都要手動自己調,程式寫錯了也檢查不出來,這時候就是要看個人經驗了,也就是您在這樣的環境下吃過多少苦頭,以後就累積成您的經驗了。例如筆者的一個朋友黑狗兄就曾經對2500AD C Compiler(發展8051系統常用的C Compiler)做出這樣的感慨︰

『如果我有一個願望可以實現的話,我希望2500AD這家公司不曾在地球上出現過,出那鍋什麼爛compiler, sh.......t

講了許多的移植經驗,讀者一定很好奇,可憐的筆者到底最後怎麼解決所需要的答案,很不幸各位讀者一定猜到了,筆者自己重新寫一個出來,真正應驗了莫非定律,最壞的情況一定會發生。還好UDP協定只需要處理網路封包的編解動作,而且是屬於遺失就不理他的協定,並不需要有重新傳送的機制,筆者只需要自己開一個計時器,決定是否要等待這個封包的回應就好了,那書上寫了一大堆的Socket的機制呢?天呀!就不要再要求了吧?Socket本身是一個複雜的資料結構,還真的叫我把這些全部實作出來?好吧!我承認我偷懶。

圖六、TCP/IP架構和OSI七層之間的對應關係

當然,只寫出一個UDP協定是沒有用的,還要其他的相關程式配合,例如撥接上網的話,我需要一個PPP (Point-to-Point Protocol)的機制來幫助我連上ISP,當然還要自己寫一個UART(DragonBall EZ CPU本身就包含了一個UART的控制晶片)的驅動程式來控制數據機的撥號,我需要在作業系統裡開啟一個監控的執行緒,來監視每一個從RS-232傳進來的位元,並且將資料解出來放到應用程式已經規劃好的記憶體空間裡,這時候我們會用到資料結構的教科書裡常常提的環狀buffer的機制,以免記憶體存取失當造成破壞,所以我們做出來的東西就大概類似上圖所示,筆者順便列出整個TCP/IP架構和OSI七層之間的對應關係。

最後特別提醒千萬要注意處理記憶體要非常小心,因為許多嵌入式作業系統為了達到核心很小的目的,並沒有做太多的記憶體保護,有的更扯,連記憶體管理都沒有,也就是說您不可以使用任何動態要求記憶體空間的行為,這種作業系統為什麼還有人用呢?便宜就是最大的原因,例如SuperTask這種作業系統就是一個例子。

拉里拉雜的講了這麼多,無非是想讓有興趣一窺究竟的讀者有個初步的認識跟印象,如果真的對嵌入式系統上TCP/IP協定開發有興趣,也許您可以參考筆者後面所列的幾本書,會有一些基礎的認識,有幾本TCP/IP聖經有中譯版,可惜中譯版翻譯的很難看懂,還是要中英對照之後才懂翻譯的意思,希望從事翻譯工作的大爺們能夠多多用心,因為筆者大學的時候也是靠中譯版撐過每一學期的課程。

最後讀者可能還有疑問就是為什麼筆者只用哈x族當例子,而不是用Palm呢?其實筆者每天都可以接觸到許多的PDA或是類似產品,可是唯一只有這台哈x族是筆者自己買的,其他接觸的東西都是公家的,不能隨便污走,真希望能夠賺點外快,買台屬於自己的PDA,也希望以後還有機會多和讀者分享這方面相關的資訊和經驗。

附註參考資料

Comer, Douglas E. Internetworking with TCP/IP-Volume 1: Principles, Protocols, and Architecture, 4th ed. Englewood Cliffs, NJ: Prentice Hall, 2000.

Comer, Douglas E. and David L. Stevens. Internetworking with TCP/IP-Volume 2, ANSI C Version: Design, Implementation, and Internals, 3rd ed. Englewood Cliffs, NJ: Prentice Hall, 1998.

Comer, Douglas E. and David L. Stevens. Internetworking with TCP/IP-Volume 3, Windows Socket Version: Client-Server Programming and Applications, 1st ed. Englewood Cliffs, NJ: Prentice Hall, 1997.

Comer, Douglas E. and David L. Stevens. Internetworking with TCP/IP-Volume 3, BSD Socket Version: Client-Server Programming and Applications, 2nd ed. Englewood Cliffs, NJ: Prentice Hall, 1996.

Stevens, W. Richard. TCP/IP Illustrated-Volume 1: The Protocols. Reading, MA: Addison-Wesley, 1994.

Wright, Gary R. and W. Richard Stevens. TCP/IP Illustrated-Volume 2: The Implementation. Reading, MA: Addison-Wesley, 1995.

Stevens, W. Richard. TCP/IP Illustrated-Volume 3: TCP for Transactions, HTTP, NNTP, and the Unix Domain Protocols. Reading, MA: Addison-Wesley, 1996.

RFC 參考資料︰http://www.faqs.org/rfcs/.