SylixOS中時間結構體探究
1.1 什麽是timeval結構體
timeval結構體在SylixOS系統中的定義如程序清單1.1所示:
程序清單1.1 timeval結構體定義
struct timeval {time_t tv_sec; /* seconds */LONG tv_usec; /* microseconds */};
tv_sec儲存從UTC 1970年1月1日00時00分00秒到創建結構體時的秒數,tv_usec為微秒數。在SylixOS下,time_t的實際類型為long long型,大小為8個字節,長度64位。
2. 使用timeval結構體存在的問題
2.1 Linux下的timeval結構體
Linux下的timeval結構體定義為程序清單2.1所示:
程序清單2.1 timeval結構體定義
struct timeval {__time_t tv_sec; /* seconds */__suseconds_t tv_usec; /* microseconds */};
其中tv_sec和tv_usec的儲存內容與SylixOS下一致,但是在32位的Linux系統中,__time_t實際類型為long int,大小為4個字節,長度32位。
2.2 結構體不一致造成的問題
2.2.1 計時長度問題
由於32位的Linux系統使用timeval結構體中的tv_sec長度為32位,則其最大值為2^32-1,意味著到UTC時間2038年1月19號03點14分07秒過後,32位Linux系統存儲的時間變量將會溢出,造成系統將時間混亂。這很可能會引起軟件故障,甚至是系統癱瘓。
而使用SylixOS系統,無論是32位系統或是64位系統,tv_sec的長度均為64位,時間最多可以使用到UTC時間292,277,026,596年12月04日15時30分08秒則基本不會遇到這類溢出問題。
2.2.2 參數傳入問題
在完善glib移植的過程中,遇到這樣一個問題:依賴glib庫的lcm庫,需要將GTimeVal結構體作為參數傳入select函數。
其中GTimeVal結構體的定義為程序清單2.2 所示:
程序清單2.2 GTimeVal結構體定義
typedef long glong;typedef struct _GTimeVal GTimeVal;struct _GTimeVal { glong tv_sec; glong tv_usec; };
傳入方式為程序清單2.3 所示:
程序清單2.3 GTimeVal傳入select函數
GTimeVal selectto;status=select (recvfd + 1,&readfds,0,0, (struct timeval*) &selectto);
由於lcm庫是基於32位Linux系統的,select函數接受的參數為timeval結構體類型的指針。雖然對於傳入的指針做了強制類型轉換,在編譯時不會報錯,但實際運行中,SylixOS系統尋找的地址為8字節+4字節,而實際只儲存了4字節+4字節的內容,導致tv_usec變量獲取值異常,出現不可預知的錯誤。
所以需要手動添加timeval結構體,賦值後將timeval結構體傳入select函數,如程序清單2.4 所示:
程序清單2.4 傳參修改方法
#ifdef SYLIXOS struct timeval selectt; selectt.tv_sec = selectto.tv_sec; selectt.tv_usec = selectto.tv_usec; status=select (recvfd + 1,&readfds,0,0, (struct timeval*) &selectt);#else status=select (recvfd + 1,&readfds,0,0, (struct timeval*) &selectto);#endif
2.2.3 結構體大小問題
使用sizeof運算符求SylixOS下timeval結構體的大小時,獲得的值為12;32位Linux下獲得值為8;在32位Windows平臺仿造SylixOS下定義timeval結構體,獲得其大小為16。
這涉及了結構體字節對齊的問題,在用sizeof運算符求算某結構體所占空間時,並不是簡單地將結構體中所有元素各自占的空間相加。從理論上講,對於任何變量的訪問都可以從任何地址開始訪問,但是事實上不是如此,實際上訪問特定類型的變量只能在特定的地址訪問,需要各個變量在空間上按一定的規則排列,而不是簡單地順序排列,這就是內存對齊。
內存對齊的原則為:
1.結構體每個成員相對結構體首地址的偏移量是對齊參數的整數倍,如有需要會在成員之間填充字節。編譯器在為結構體成員開辟空間時,首先檢查預開辟空間的地址相對於結構體首地址的偏移量是否為對齊參數的整數倍,若是,則存放該成員;若不是,則填充若幹字節,以達到整數倍的要求。
2.結構體變量所占空間的大小是對齊參數大小的整數倍。如有需要會在最後一個成員末尾填充若幹字節使得所占空間大小是對齊參數大小的整數倍。
不同類型變量的長度與自身對齊參數如表2.5 所示:
表2.5 變量的長度與自身對齊參數
char | short | Int | float | double | 指針 | ||
Windows(32位) VisualStudio2010 | 長度 | 1 | 2 | 4 | 4 | 8 | 4 |
自身對齊參數 | 1 | 2 | 4 | 4 | 8 | 4 | |
Linux(32位) GCC | 長度 | 1 | 2 | 4 | 4 | 8 | 4 |
自身對齊參數 | 1 | 2 | 4 | 4 | 4 | 4 | |
SylixOS(32位) GCC | 長度 | 1 | 2 | 4 | 4 | 8 | 4 |
自身對齊參數 | 1 | 2 | 4 | 4 | 4 | 4 |
從上面可以發現,在Windows(32)/ VisualStudio2010下各種類型的變量的自身對齊參數就是該類型變量所占字節數的大小,而在Linux(32)/GCC下double類型的變量自身對齊參數是4,是因為Linux(32)/GCC下如果該類型變量的長度沒有超過CPU的字長,則以該類型變量的長度作為自身對齊參數,如果該類型變量的長度超過CPU字長,則自身對齊參數為CPU字長,而32位系統其CPU字長是4,所以Linux(32)/GCC下double類型的變量自身對齊參數是4,如果是在Linux(64)下,則double類型的自身對齊參數是8。SylixOS系統也與Linux系統類似。
所以構造相同的結構體timeval在Windows下與32位SylixOS、32位Linux在內存中所占大小存在差異,需要在移植中註意。
3. 總結
SylixOS與Linux、Windows存在差異,即使系統接口名稱一樣,參數類型看似一致,仍可能存在不兼容的情況。有些時候甚至編譯運行都不會出錯,但確實存在問題,移植時需要謹慎。
4. 參考資料
《SylixOS應用程序開發手冊》
《RealEvo-IDE使用手冊》
SylixOS中時間結構體探究