新聞中心
1 概述
Linux下的程序大多充當(dāng)服務(wù)器的角色,在這種情況下,隨著負(fù)載量和功能的增加,服務(wù)器所使用內(nèi)存必然也隨之增加,然而32位系統(tǒng)固有的4GB虛擬地址空間限制,在如今已是非常突出的問(wèn)題了;另一個(gè)需要改進(jìn)的地方是日期,在Linux中,日期是使用32位整數(shù)來(lái)表示的,該值所表示的是從1970年1月1日至今所經(jīng)過(guò)的秒數(shù),這在2038年就會(huì)失效,但是在64位系統(tǒng)中,日期是使用64位整數(shù)表示的,基本上不用擔(dān)心其會(huì)失效。在這種情況下,將服務(wù)器移植到64位系統(tǒng)下,幾乎成了必然的選擇。要獲得能在64位系統(tǒng)下運(yùn)行的程序,特別是達(dá)到只維護(hù)同一套代碼就能獲得在32位及64位系統(tǒng)下都能運(yùn)行的程序,編碼時(shí)需遵循一定的原則,是一個(gè)較為繁瑣的過(guò)程。雖然有一些高級(jí)語(yǔ)言不會(huì)受這些數(shù)據(jù)類別變化的影響,但是C/C++的確會(huì)受到影響。下面,我們先來(lái)了解一下64位數(shù)據(jù)模型,為后面的介紹打下鋪墊。

創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站設(shè)計(jì)制作、網(wǎng)站設(shè)計(jì)、十堰鄖陽(yáng)網(wǎng)絡(luò)推廣、成都微信小程序、十堰鄖陽(yáng)網(wǎng)絡(luò)營(yíng)銷、十堰鄖陽(yáng)企業(yè)策劃、十堰鄖陽(yáng)品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供十堰鄖陽(yáng)建站搭建服務(wù),24小時(shí)服務(wù)熱線:18982081108,官方網(wǎng)址:www.cdcxhl.com
2 64位系統(tǒng)數(shù)據(jù)模型
2.1 LP64/ILP64/LLP64
下面的表格說(shuō)明了32位和64位數(shù)據(jù)模型在各個(gè)數(shù)據(jù)類別上的區(qū)別,這里的I是指int,L是指long,P是指pointer:
| Datatype | LP64 | ILP64 | LLP64 | ILP32 | LP32 |
|---|---|---|---|---|---|
| char | 8 | 8 | 8 | 8 | 8 |
| short | 16 | 16 | 16 | 16 | 16 |
| int | 32 | 64 | 32 | 32 | 16 |
| long | 64 | 64 | 32 | 32 | 32 |
| long long | 64 | 64 | 64 | 64 | 64 |
| pointer | 64 | 64 | 64 | 32 | 32 |
顯示詳細(xì)信息
表2.1
這3個(gè)64位模型(LP64、LLP64和ILP64)之間的區(qū)別在于非浮點(diǎn)數(shù)據(jù)類型。當(dāng)一個(gè)或多個(gè)C數(shù)據(jù)類型的寬度從一種模型變換成另外一種模型時(shí),應(yīng)用程序可能會(huì)受到很多方面的影響。這些影響主要可以分為兩類:
-
數(shù)據(jù)對(duì)象的大小。編譯器按照自然邊界對(duì)數(shù)據(jù)類型進(jìn)行對(duì)齊;換而言之,32位的數(shù)據(jù)類型在64位系統(tǒng)上要按照32位邊界進(jìn)行對(duì)齊,而64位的數(shù)據(jù)類型在64位系統(tǒng)上則要按照64位邊界進(jìn)行對(duì)齊。這意味著諸如結(jié)構(gòu)或聯(lián)合之類的數(shù)據(jù)對(duì)象的大小在32位和64位系統(tǒng)上是不同的。
-
基本數(shù)據(jù)類型的大小。通常關(guān)于基本數(shù)據(jù)類型之間關(guān)系的假設(shè)在64位數(shù)據(jù)模型上都已經(jīng)無(wú)效了。依賴于這些關(guān)系的應(yīng)用程序在64位平臺(tái)上編譯也會(huì)失敗。例如,sizeof (int) = sizeof (long) = sizeof (pointer) 的假設(shè)對(duì)于ILP32數(shù)據(jù)模型有效,但是對(duì)于其他數(shù)據(jù)模型就無(wú)效了。
總之,編譯器要按照自然邊界對(duì)數(shù)據(jù)類型進(jìn)行對(duì)齊,這意味著編譯器會(huì)進(jìn)行“填充”,從而強(qiáng)制進(jìn)行這種方式的對(duì)齊,就像是在C結(jié)構(gòu)和聯(lián)合中所做的一樣。結(jié)構(gòu)或聯(lián)合的成員是根據(jù)最寬的成員進(jìn)行對(duì)齊的。Windows 64位系統(tǒng)采用LLP64的數(shù)據(jù)模型,從Win32到Win64就只有指針長(zhǎng)度不同,因此移植較為簡(jiǎn)單。而Linux 64位系統(tǒng)采用LP64數(shù)據(jù)模型,因此在long和pointer上,都有著和32位系統(tǒng)不同的長(zhǎng)度。
2.2 數(shù)據(jù)對(duì)齊
默認(rèn)情況下,編譯器按照自然邊界對(duì)數(shù)據(jù)類型進(jìn)行對(duì)齊;換而言之,32位的數(shù)據(jù)類型在64位系統(tǒng)上要按照32位邊界進(jìn)行對(duì)齊,而64位的數(shù)據(jù)類型在64位系統(tǒng)上則要按照64位邊界進(jìn)行對(duì)齊。
2.2.1 #pragma pack
上面談到,默認(rèn)情況下,編譯器按照自然邊界對(duì)數(shù)據(jù)類型進(jìn)行對(duì)齊,但使用編譯器指令#pragma pack可以修改對(duì)齊方式。
2.2.2 結(jié)構(gòu)體對(duì)齊舉例
struct test
{
int i1;
double d;
int i2;
long l;
}
| 結(jié)構(gòu)成員 | 在 32 位系統(tǒng)上的大小 | 在 64 位系統(tǒng)上的大小 |
|---|---|---|
| struct test { | ||
| int i1; | 32位 | 32位 |
| 32位填充 | ||
| double d; | 64位 | 64位 |
| int i2; | 32位 | 32位 |
| 32位填充 | ||
| long l; | 32位 | 64位 |
| }; | 結(jié)構(gòu)大小為20字節(jié) | 結(jié)構(gòu)大小為32字節(jié) |
顯示詳細(xì)信息
表2.2
注意,在我自己所測(cè)試的32位系統(tǒng)上,編譯器并沒有對(duì)double型數(shù)據(jù)進(jìn)行對(duì)齊,盡管它是一個(gè)64位的對(duì)象,這是因?yàn)橛布?huì)將其當(dāng)成兩個(gè)32位的對(duì)象進(jìn)行處理。
3 從32位系統(tǒng)移植到64位系統(tǒng)
3.1 基本原則
3.1.1 類型定義
不要使用C/C++中那些在64位系統(tǒng)上會(huì)改變大小的數(shù)據(jù)類型來(lái)編寫應(yīng)用程序,而是使用一些類型定義或宏來(lái)顯式地說(shuō)明變量中所包含的數(shù)據(jù)的大小和類型。有些定義可以使代碼的可移植性更好。
l ptrdiff_t:
這個(gè)值在32位系統(tǒng)下是int,在64位系統(tǒng)下是long,表示兩個(gè)指針相減后的結(jié)果。
l size_t:
這個(gè)值在32位系統(tǒng)下是unsigned int,在64位系統(tǒng)下是unsigned long,用來(lái)表示非負(fù)的大小,一般用來(lái)表示sizeof的結(jié)果或表示數(shù)組的大小。
-
int32_t、uint32_t 等:
定義具有預(yù)定義寬度的整型。
-
intptr_t 和 uintptr_t:
這2個(gè)值在32位系統(tǒng)下是int和unsigned int,在64位系統(tǒng)下是long和unsigned long,任何有效指針都可以轉(zhuǎn)換成這個(gè)類型。
3.1.2 表達(dá)式
在C/C++中,表達(dá)式是基于結(jié)合律、操作符的優(yōu)先級(jí)和一組數(shù)學(xué)計(jì)算規(guī)則的。要想讓表達(dá)式在32位和64位系統(tǒng)上都可以正確工作,請(qǐng)注意以下規(guī)則:
-
兩個(gè)有符號(hào)整數(shù)相加的結(jié)果是一個(gè)有符號(hào)整數(shù)。
-
int和long類型的兩個(gè)數(shù)相加,結(jié)果是一個(gè)long類型的數(shù)。
-
如果一個(gè)操作數(shù)是無(wú)符號(hào)整數(shù),另外一個(gè)操作數(shù)是有符號(hào)整數(shù),那么表達(dá)式的結(jié)果就是無(wú)符號(hào)整數(shù)。
-
int和double類型的兩個(gè)數(shù)相加,結(jié)果是一個(gè)double類型的數(shù)。此處int類型的數(shù)在執(zhí)行加法運(yùn)算之前轉(zhuǎn)換成double類型。
3.1.3 賦值
-
sizeof和數(shù)組大?。?br />
vector intArray;
……
int arraysz = (int)intArray.size();
不要int類型來(lái)接收STL數(shù)據(jù)類型的大小,而應(yīng)該使用size_t:
size_t arraysz = intArray.size();
上面這種是比較明顯的錯(cuò)誤,不明顯的錯(cuò)誤有:
for (int i = 0; i
{
……
}
這樣有可能導(dǎo)致數(shù)據(jù)截?cái)唷?/p>
-
time_t:
不要使用int類型參與時(shí)間的運(yùn)算,因?yàn)閠ime_t是long類型,在64位機(jī)器上會(huì)導(dǎo)致數(shù)據(jù)截?cái)?,原則是與時(shí)間相關(guān)的運(yùn)算都采用time_t類型。
例如在32位程序中可能有如下代碼:
long m_lastHeartBeatTime; //最后心跳時(shí)間
int GetLastHeartBeatTime()
{
return m_lastHeartBeatTime;
}
time_t currtime = GetCurrentTime();
if(currtime >= GetLastHeartBeatTime())
{
SetLastHeartBeatTime(currtime);
}
這些代碼在32位系統(tǒng)下沒有問(wèn)題,但在64位系統(tǒng)下可能會(huì)導(dǎo)致嚴(yán)重的問(wèn)題。
-
格式化打印
vector intArray;
……
size_t arraysz = intArray.size();
32位系統(tǒng)下代碼應(yīng)為:
printf(“array size = %u”, arraysz);
64位系統(tǒng)下代碼應(yīng)為:
printf(“array size = %lu”, arraysz);
3.2 移植經(jīng)驗(yàn)
3.2.1 如何判斷一個(gè)可執(zhí)行文件是32位編譯的版本還是64位編譯的版本
-
使用file可執(zhí)行文件名
顯示ELF 64-bit LSB executable 則是64位可執(zhí)行文件版本
顯示ELF 32-bit LSB 則是32位可執(zhí)行文件版本
-
使用readelf -h可執(zhí)行文件名,看其中的Class
顯示ELF64是64位可執(zhí)行文件
顯示ELF32是32位可執(zhí)行文件
3.2.2 如何判斷環(huán)境是32位還是64位
代碼中:
#if __WORDSIZE == 64
#endif
腳本中:
if [ getconf LONG_BIT -eq 64 ];then
64位處理邏輯
else
32位處理邏輯
fi
3.2.3 數(shù)據(jù)定義
修改所有l(wèi)ong定義的變量為int類型,由于long類型在32位和64位下的長(zhǎng)度是不一樣的,為了避免兼容性問(wèn)題,盡量檢查和修改掉類型定義為非固定長(zhǎng)度的整數(shù)類型。
指針類型的,如果做加減等運(yùn)算處理,不能轉(zhuǎn)換為int類型,而統(tǒng)一改為intptr_t類型,比如:
intptr_toffset = (intptr_t)pCurr – (intptr_t)pBase;
3.2.4 格式化字符串的時(shí)候
#if __WORDSIZE == 64
#define FMT_SIZET “%u”
#define FMT_UINT64 “%llu”
#define FMT_INT64 “%lld”
#else
#define FMT_SIZET “%lu”
#define FMT_UINT64 “%lu”
#define FMT_INT64 “%lld”
#endif
例如:
sprintf(errorDesc,”Insufficient memory buffer size,”FMT_SIZET” needed,but only “FMT_SIZET” bytes”,unit_size,m_capacity);
當(dāng)然也可以使用系統(tǒng)定義的宏P(guān)RIu64和PRId64等來(lái)作一些文章。
3.2.5 基本數(shù)據(jù)定義
long, time_t, size_t 類型在32位和64位下的長(zhǎng)度是不一樣的,要檢查代碼中是否有time_t *,size_t 類型的指針參數(shù),由于調(diào)用傳入的變量大部分是int類型,所以將這些函數(shù)定義統(tǒng)一修改為int,同時(shí)仔細(xì)檢查所有調(diào)用的地方,傳入的指針變量長(zhǎng)度是否匹配。
比如下面的范例:
int Func1(size_t *pSize1,size_t size2); 需要修改為
int Func1(int *pSize1,size_t size2); 其中size2是非指針類型,可以不需要修改。
然后檢查調(diào)用的地方,如果傳入?yún)?shù)是非int類型,則需要修改為int類型變量傳入,比如
short shParam = 0;
Func1(&shParam,100);
要修改為
int iParam = 0;
Func1(&iParam,100);
如果是一些已經(jīng)定義好的結(jié)構(gòu)體成員,則可通過(guò)臨時(shí)變量來(lái)修改
Func(&stPlayer.shParam,100)
修改為
int iTmpParam = stPlayer.shParam;
Func(&iTmpParam,100);
stPlayer.shParam = iTmpParam;
3.2.6 time_t的加減要注意
比如下面這段代碼,在32位系統(tǒng)上運(yùn)行沒有問(wèn)題,但64位下運(yùn)行異常:
if((leftTime + xxz::framework::GetCurrentTimeVal(NULL)) > 0 && (leftTime >= 0))
{
n->expireTime = leftTime + xxz::framework::GetCurrentTimeVal(NULL);
}
else
{
n->expireTime = 0x7FFFFFFF;
}
這里在64位下,如果lefttime等于0x7FFFFFFF,則lefttime + xxz::framework::GetCurrentTimeVal(NULL)的結(jié)果為long(因?yàn)閤xz::framework::GetCurrentTimeVal(NULL)返回time_t,為long類型),因此不會(huì)溢出,這時(shí)相加的結(jié)果賦給一個(gè)整形(n->expireTime),則這個(gè)整形溢出,稱為負(fù)值,從而發(fā)生錯(cuò)誤。
改為:
if ((int64)leftTime + (int64)xxz::framework::GetCurrentTimeVal(NULL) >= (int64)0x7FFFFFFF)
{
dstTime = 0x7FFFFFFF;
}
else
{
dstTime = leftTime + xxz::framework::GetCurrentTimeVal(NULL);
}
3.3 移植步驟
1 修改代碼,主要注意以下事項(xiàng)
去除所有的long,替換為固定大小的類型,如int32_t, int64_t等。
時(shí)間相關(guān)類型的全部用使用time_t來(lái)進(jìn)行處理。
pointer之間的加減法使用intptr_t來(lái)存儲(chǔ)結(jié)果,不要在pointer和int之間相互轉(zhuǎn)換。
如果必須用size_t,比如STL,則傳值賦值都用size_t,不要在int和size_t之間相互轉(zhuǎn)換,以免結(jié)果被截?cái)唷?/p>
格式化字符串使用如下的兼容性定義來(lái)處理,避免告警:
#if __WORDSIZE == 64
#define FMT_SIZET “%u”
#else
#define FMT_SIZET “%lu”
2 替換外部庫(kù)
這一步比較難,因?yàn)橛行┩獠繋?kù)沒有64位版本,這就有可能需要推動(dòng)外部庫(kù)的64位化工作,或者將這部分功能挪到其它進(jìn)程。
3 運(yùn)營(yíng)環(huán)境
修改腳本支持64位環(huán)境
一些數(shù)據(jù)需要用64位程序重新生成,供程序使用
4 總結(jié)
主流的硬件供應(yīng)商最近都在擴(kuò)充自己的64位產(chǎn)品,這是因?yàn)?4位平臺(tái)可以提供更好的性能和可伸縮性。32位系統(tǒng)的限制,特別是4GB的虛擬內(nèi)存上限,已經(jīng)極大地刺激很多公司開始考慮遷移到64位平臺(tái)上。了解如何將應(yīng)用程序移植到64位體系結(jié)構(gòu)上可以幫助我們編寫可移植性更好且效率更高的代碼。
當(dāng)前標(biāo)題:講解一下Linux64位程序移植
本文網(wǎng)址:http://www.dlmjj.cn/article/cdgdcpe.html


咨詢
建站咨詢
