Linux 是可以使用 64 位處理器的跨平臺操作系統之一,現在 64 位的系統在服務器和桌面端都已經非常常見了。很多開發人員現在都面臨著需要將自己的應用程序從 32 位環境移植到 64 位環境中。隨著 Intel? Itanium? 和其他 64 位處理器的引入,使軟件針對 64 位環境做好準備變得日益重要了。
與 UNIX? 和其他類 UNIX 操作系統一樣,Linux 使用了 LP64 標準,其中指針和長整數都是 64 位的,而普通的整數則依然是 32 位的。盡管有些高級語言并不會受到這種類型大小不同的影響,但是另外一些語言(例如 C 語言)卻的確會受到這種影響。
將應用程序從 32 位系統移植到 64 位系統上的工作可能會非常簡單,也可能會非常困難,這取決于這些應用程序是如何編寫和維護的。很多瑣碎的問題都可能導致產生問題,即使在一個編寫得非常好的高度可移植的應用程序中也是如此,因此本文將對這些問題進行歸納總結,并給出解決這些問題的一些方法建議。
64 位的優點
32 位平臺有很多限制,這些限制正在阻礙大型應用程序(例如數據庫)開發人員的工作進展,尤其對那些希望充分利用計算機硬件優點的開發人員來說更是如此??茖W計算通常要依賴于浮點計算,而有些應用程序(例如金融計算)則需要一個比較狹窄的數字范圍,但是卻要求更高的精度,其精度高于浮點數所提供的精度。64 位數學運算提供了這種更高精度的定點數學計算,同時還提供了足夠的數字范圍?,F在在計算機業界中有很多關于 32 位地址空間所表示的地址空間的討論。32 位指針只能尋址 4GB 的虛擬地址空間。我們可以克服這種限制,但是應用程序開發就變得非常復雜了,其性能也會顯著降低。
在語言實現方面,目前的 C 語言標準要求 “long long” 數據類型至少是 64 位的。然而,其實現可能會將其定義為更大。
另外一個需要改進的地方是日期。在 Linux 中,日期是使用 32 位整數來表示的,該值所表示的是從 1970 年 1 月 1 日至今所經過的秒數。這在 2038 年就會失效。但是在 64 位的系統中,日期是使用有符號的 64 位整數表示的,這可以極大地擴充其可用范圍。
總之,64 位具有以下優點:
1. 64 位的應用程序可以直接訪問 4EB 的虛擬內存,Intel Itanium 處理器提供了連續的線性地址空間。
2. 64 位的 Linux 允許文件大小最大達到 4 EB(2 的 63 次冪),其重要的優點之一就是可以處理對大型數據庫的訪問。
Linux 64 位體系結構
不幸的是,C 編程語言并沒有提供一種機制來添加新的基本數據類型。因此,提供 64 位的尋址和整數運算能力必須要修改現有數據類型的綁定或映射,或者向 C 語言中添加新的數據類型。
表 1. 32 位和 64 位數據模型
這 3 個 64 位模型(LP64、LLP64 和 ILP64)之間的區別在于非浮點數據類型。當一個或多個 C 數據類型的寬度從一種模型變換成另外一種模型時,應用程序可能會受到很多方面的影響。這些影響主要可以分為兩類:
ILP32 LP64 LLP64 ILP64
char
8
8
8
8
short
16
16
16
16
int
32
32
32
64
long
32
64
32
64
long long
64
64
64
64
指針
32
64
64
64
數據對象的大小。編譯器按照自然邊界對數據類型進行對齊;換而言之,32 位的數據類型在 64 位系統上要按照 32 位邊界進行對齊,而 64 位的數據類型在 64 位系統上則要按照 64 位邊界進行對齊。這意味著諸如結構或聯合之類的數據對象的大小在 32 位和 64 位系統上是不同的。
基本數據類型的大小。通常關于基本數據類型之間關系的假設在 64 位數據模型上都已經無效了。依賴于這些關系的應用程序在 64 位平臺上編譯也會失敗。例如,sizeof (int) = sizeof (long) = sizeof (pointer) 的假設對于 ILP32 數據模型有效,但是對于其他數據模型就無效了。
總之,編譯器要按照自然邊界對數據類型進行對齊,這意味著編譯器會進行 “填充”,從而強制進行這種方式的對齊,就像是在 C 結構和聯合中所做的一樣。結構或聯合的成員是根據最寬的成員進行對齊的。清單 1 對這個結構進行了解釋。
清單 1. C 結構
struct test {
int i1;
double d;
int i2;
long l;
}
表 2 給出了這個結構中每個成員的大小,以及這個結構在 32 位系統和 64 位系統上的大小。
表 2. 結構和結構成員的大小
結構成員 在 32 位系統上的大小 在 64 位系統上的大小
struct test {
int i1;
32 位
32 位
32 位填充
double d;
64 位
64 位
int i2;
32 位
32 位
32 位填充
long l;
32 位
64 位
};
結構大小為 20 字節
結構大小為 32 字節
注意,在一個 32 位的系統上,編譯器可能并沒有對變量 d 進行對齊,盡管它是一個 64 位的對象,這是因為硬件會將其當作兩個 32 位的對象進行處理。然而,64 位的系統會對 d 和 l 都進行對齊,這樣會添加兩個 4 字節的填充。
從 32 位系統移植到 64 位系統
本節介紹如何解決一些常見的問題:
聲明表達式賦值數字常數Endianism類型定義位移字符串格式化函數參數
聲明
要想讓您的代碼在 32 位和 64 位系統上都可以工作,請注意以下有關聲明的用法:
根據需要適當地使用 “L” 或 “U” 來聲明整型常量。
確保使用無符號整數來防止符號擴展的問題。
如果有些變量在這兩個平臺上都需要是 32 位的,請將其類型定義為 int.如果有些變量在 32 位系統上是 32 位的,在 64 位系統上是 64 位的,請將其類型定義為 long.為了對齊和性能的需要,請將數字變量聲明為 int 或 long 類型。不要試圖使用 char 或 short 類型來保存字節。
將字符指針和字符字節聲明為無符號類型的,這樣可以防止 8 位字符的符號擴展問題。
表達式
在 C/C++ 中,表達式是基于結合律、操作符的優先級和一組數學計算規則的。要想讓表達式在 32 位和 64 位系統上都可以正確工作,請注意以下規則:
兩個有符號整數相加的結果是一個有符號整數。
int 和 long 類型的兩個數相加,結果是一個 long 類型的數。
如果一個操作數是無符號整數,另外一個操作數是有符號整數,那么表達式的結果就是無符號整數。
int 和 doubule 類型的兩個數相加,結果是一個 double 類型的數。此處 int 類型的數在執行加法運算之前轉換成 double 類型。
賦值
由于指針、int 和 long 在 64 位系統上大小不再相同了,因此根據這些變量是如何賦值和在應用程序中使用的,可能會出現問題。下面是有關賦值的一些技巧:
不要交換使用 int 和 long 類型,因為這可能會導致高位數字被截斷。例如,不要做下面的事情:
int i;
long l;
i = l;
不要使用 int 類型來存儲指針。下面這個例子在 32 位系統上可以很好地工作,但是在 64 位系統上會失敗,這是因為 32 位整數無法存放 64 位的指針。例如,不要做下面的事情:
unsigned int i, *ptr;
i = (unsigned) ptr;
不要使用指針來存放 int 類型的值。例如,不要做下面的事情;
int *ptr;
int i;
ptr = (int *) i;
如果在表達式中混合使用無符號和有符號的 32 位整數,并將其賦值給一個有符號的 long 類型,那么將其中一個操作數轉換成 64 位的類型。這會導致其他操作數也被轉換成 64 位的類型,這樣在對表達式進行賦值時就不需要再進行轉換了。另外一種解決方案是對整個表達式進行轉換,這樣就可以在賦值時進行符號擴展。例如,考慮下面這種用法可能會出現的問題:
|
|
原文轉自:http://www.anti-gravitydesign.com