本文的內(nèi)容是 Win32 API(特別是進(jìn)程、線程和共享內(nèi)存服務(wù))到 POWER 上 Linux 的映射。本文可以幫助您確定哪種映射服務(wù)最適合您的需要。作者向您詳細(xì)介紹了他在移植 Win32 C/C++ 應(yīng)用程序時(shí)遇到的 API 映射。
概述
有很多方式可以將 Win32 C/C++ 應(yīng)用程序移植和遷移到 pSeries 平臺(tái)。您可以使用免費(fèi)軟件或者第三方工具來(lái)將 Win32 應(yīng)用程序代碼移到 Linux。在我們的方案中,我們決定使用一個(gè)可移植層來(lái)抽象系統(tǒng) API 調(diào)用。可移植層將使我們的應(yīng)用程序具有以下優(yōu)勢(shì):
- 與硬件無(wú)關(guān)。
- 與操作系統(tǒng)無(wú)關(guān)。
- 與操作系統(tǒng)上版本與版本間的變化無(wú)關(guān)。
- 與操作系統(tǒng) API 風(fēng)格及錯(cuò)誤代碼無(wú)關(guān)。
- 能夠統(tǒng)一地在對(duì) OS 的調(diào)用中置入性能和 RAS 鉤子(hook)。
由于 Windows 環(huán)境與 pSeries Linux 環(huán)境有很大區(qū)別,所以進(jìn)行跨 UNIX 平臺(tái)的移植比進(jìn)行從 Win32 平臺(tái)到 UNIX 平臺(tái)的移植要容易得多。這是可以想到的,因?yàn)楹芏?UNIX 系統(tǒng)都使用共同的設(shè)計(jì)理念,在應(yīng)用程序?qū)佑蟹浅6嗟念愃浦帯2贿^(guò),Win32 API 在移植到 Linux 時(shí)是受限的。本文剖析了由于 Linux 和 Win32 之間設(shè)計(jì)的不同而引發(fā)的問(wèn)題。
初始化和終止
在 Win2K/NT 上,DLL 的初始化和終止入口點(diǎn)是 _DLL_InitTerm 函數(shù)。當(dāng)每個(gè)新的進(jìn)程獲得對(duì) DLL 的訪問(wèn)時(shí),這個(gè)函數(shù)初始化 DLL 所必需的環(huán)境。當(dāng)每個(gè)新的進(jìn)程釋放其對(duì) DLL 的訪問(wèn)時(shí),這個(gè)函數(shù)為那個(gè)環(huán)境終止 DLL。當(dāng)您鏈接到那個(gè) DLL 時(shí),這個(gè)函數(shù)會(huì)自動(dòng)地被調(diào)用。對(duì)應(yīng)用程序而言,_DLL_InitTerm 函數(shù)中包含了另外一個(gè)初始化和終止例程。
在 Linux 上,GCC 有一個(gè)擴(kuò)展,允許指定當(dāng)可執(zhí)行文件或者包含它的共享對(duì)象啟動(dòng)或停止時(shí)應(yīng)該調(diào)用某個(gè)函數(shù)。語(yǔ)法是 __attribute__((constructor))
或 __attribute__((destructor))
。這些基本上與構(gòu)造函數(shù)及析構(gòu)函數(shù)相同,可以替代 glibc 庫(kù)中的 _init 和 _fini 函數(shù)。
這些函數(shù)的 C 原型是:
void __attribute__ ((constructor)) app_init(void);
void __attribute__ ((destructor)) app_fini(void);
|
Win32 sample
_DLL_InitTerm(HMODULE modhandle, DWORD fdwReason, LPVOID lpvReserved)
{
WSADATA Data;
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
if (_CRT_init() == -1)
return 0L;
/* start with initialization code */
app_init();
break;
case DLL_PROCESS_DETACH:
/* Start with termination code*/
app_fini();
_CRT_term();
break;
…..
default:
/* unexpected flag value - return error indication */
return 0UL;
} return 1UL; /* success */
}
|
進(jìn)程服務(wù)
Win32 進(jìn)程模型沒(méi)有與 fork()
和 exec()
直接相當(dāng)?shù)暮瘮?shù)。在 Linux 中使用 fork()
調(diào)用總是會(huì)繼承所有內(nèi)容,與此不同, CreateProcess()
接收用于控制進(jìn)程創(chuàng)建方面的顯式參數(shù),比如文件句柄繼承。
CreateProcess API 創(chuàng)建一個(gè)包含有一個(gè)或多個(gè)在此進(jìn)程的上下文中運(yùn)行的線程的新進(jìn)程,子進(jìn)程與父進(jìn)程之間沒(méi)有關(guān)系。在 Windows NT/2000/XP 上,返回的進(jìn)程 ID 是 Win32 進(jìn)程 ID。在 Windows ME 上,返回的進(jìn)程 ID 是除去了高位(high-order bit)的 Win32 進(jìn)程 ID。當(dāng)創(chuàng)建的進(jìn)程終止時(shí),所有與此進(jìn)程相關(guān)的數(shù)據(jù)都從內(nèi)存中刪除。
為了在 Linux 中創(chuàng)建一個(gè)新的進(jìn)程, fork()
系統(tǒng)調(diào)用會(huì)復(fù)制那個(gè)進(jìn)程。新進(jìn)程創(chuàng)建后,父進(jìn)程和子進(jìn)程的關(guān)系就會(huì)自動(dòng)建立,子進(jìn)程默認(rèn)繼承父進(jìn)程的所有屬性。Linux 使用一個(gè)不帶任何參數(shù)的調(diào)用創(chuàng)建新的進(jìn)程。 fork()
將子進(jìn)程的進(jìn)程 ID 返回給父進(jìn)程,而不返回給子進(jìn)程任何內(nèi)容。
Win32 進(jìn)程同時(shí)使用句柄和進(jìn)程 ID 來(lái)標(biāo)識(shí),而 Linux 沒(méi)有進(jìn)程句柄。
進(jìn)程映射表
Win32 |
Linux |
CreateProcess |
fork() execv() |
TerminateProcess |
kill |
ExitProcess() |
exit() |
GetCommandLine |
argv[] |
GetCurrentProcessId |
getpid |
KillTimer |
alarm(0) |
SetEnvironmentVariable |
putenv |
GetEnvironmentVariable |
getenv |
GetExitCodeProcess |
waitpid |
創(chuàng)建進(jìn)程服務(wù)
在 Win32 中, CreateProcess()
的第一個(gè)參數(shù)指定要運(yùn)行的程序,第二個(gè)參數(shù)給出命令行參數(shù)。CreateProcess 將其他進(jìn)程參數(shù)作為參數(shù)。倒數(shù)第二個(gè)參數(shù)是一個(gè)指向某個(gè) STARTUPINFORMATION 結(jié)構(gòu)體的指針,它為進(jìn)程指定了標(biāo)準(zhǔn)的設(shè)備以及其他關(guān)于進(jìn)程環(huán)境的啟動(dòng)信息。在將 STARTUPINFORMATION 結(jié)構(gòu)體的地址傳給 CreateProcess 以重定向進(jìn)程的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤之前,您需要設(shè)置這個(gè)結(jié)構(gòu)體的 hStdin、hStdout 和 hStderr 成員。最后一個(gè)參數(shù)是一個(gè)指向某個(gè) PROCESSINFORMATION 結(jié)構(gòu)體的指針,由被創(chuàng)建的進(jìn)程為其添加內(nèi)容。進(jìn)程一旦啟動(dòng),它將包含創(chuàng)建它的進(jìn)程的句柄以及其他內(nèi)容。
Win32 example
PROCESS_INFORMATION procInfo;
STARTUPINFO startupInfo;
typedef DWORD processId;
char *exec_path_name
char *_cmd_line;
GetStartupInfo( &startupInfo ); // You must fill in this structure
if( CreateProcess( exec_path_name, // specify the executable program
_cmd_line, // the command line arguments
NULL, // ignored in Linux
NULL, // ignored in Linux
TRUE, // ignored in Linux
DETACHED_PROCESS | HIGH_PRIORITY_CLASS,
NULL, // ignored in Linux
NULL, // ignored in Linux
&startupInfo,
&procInfo))
*processId = procInfo.dwProcessId;
else
{
*processId = 0;
return RC_PROCESS_NOT_CREATED;
}
|
在 Linux 中,進(jìn)程 ID 是一個(gè)整數(shù)。Linux 中的搜索目錄由 PATH 環(huán)境變量(exec_path_name)決定。 fork()
函數(shù)建立父進(jìn)程的一個(gè)副本,包括父進(jìn)程的數(shù)據(jù)空間、堆和棧。 execv()
子例程使用 exec_path_name 將調(diào)用進(jìn)程當(dāng)前環(huán)境傳遞給新的進(jìn)程。
這個(gè)函數(shù)用一個(gè)由 exec_path_name 指定的新的進(jìn)程映像替換當(dāng)前的進(jìn)程映像。新的映像構(gòu)造自一個(gè)由 exec_path_name 指定的正規(guī)的、可執(zhí)行的文件。由于調(diào)用的進(jìn)程映像被新的進(jìn)程映像所替換,所以沒(méi)有任何返回。
Equivalent Linux code
#include <stdlib.h>
#include <stdio.h>
int processId;
char *exec_path_name;
char * cmd_line ;
cmd_line = (char *) malloc(strlen(_cmd_line ) + 1 );
if(cmd_line == NULL)
return RC_NOT_ENOUGH_MEMORY;
strcpy(cmd_line, _cmd_line);
if( ( *processId =
fork() ) == 0 ) // Create child
{
char *pArg, *pPtr;
char *argv[WR_MAX_ARG + 1];
int argc;
if( ( pArg = strrchr( exec_path_name, '/' ) ) != NULL )
pArg++;
else
pArg = exec_path_name;
argv[0] = pArg;
argc = 1;
if( cmd_line != NULL && *cmd_line != '\0' )
{
pArg = strtok_r(cmd_line, " ", &pPtr);
while( pArg != NULL )
{
argv[argc] = pArg;
argc++;
if( argc >= WR_MAX_ARG )
break;
pArg = strtok_r(NULL, " ", &pPtr);
}
}
argv[argc] = NULL;
execv(exec_path_name, argv);
free(cmd_line);
exit( -1 );
}
else if( *processId == -1 )
{
*processId = 0;
free(cmd_line);
return RC_PROCESS_NOT_CREATED;
}
|
終止進(jìn)程服務(wù)
在 Win32 進(jìn)程中,父進(jìn)程和子進(jìn)程可能需要單獨(dú)訪問(wèn)子進(jìn)程所繼承的由某個(gè)句柄標(biāo)識(shí)的對(duì)象。父進(jìn)程可以創(chuàng)建一個(gè)可訪問(wèn)而且可繼承的副本句柄。Win32 示例代碼使用下面的方法終止進(jìn)程:
- 使用 OpenProcess 來(lái)獲得指定進(jìn)程的句柄。
- 使用 GetCurrentProcess 獲得其自己的句柄。
- 使用 DuplicateHandle 來(lái)獲得一個(gè)來(lái)自同一對(duì)象的句柄作為原始句柄。
如果函數(shù)成功,則使用 TerminateThread 函數(shù)來(lái)釋放同一進(jìn)程上的主線程。然后使用 TerminateThread 函數(shù)來(lái)無(wú)條件地使一個(gè)進(jìn)程退出。它啟動(dòng)終止并立即返回。
Win32 sample code
if( thread != (HANDLE) NULL )
{
HANDLE thread_dup;
if( DuplicateHandle( OpenProcess(PROCESS_ALL_ACCESS, TRUE, processId),
thread,
GetCurrentProcess(),
&thread_dup, //Output
0,
FALSE,
DUPLICATE_SAME_ACCESS ))
{
TerminateThread( thread_dup, 0);
}
}
TerminateProcess(
OpenProcess(PROCESS_ALL_ACCESS, TRUE,
processId),
(UINT)0 );
|
在 Linux 中,使用 kill 子例程發(fā)送 SIGTERM 信號(hào)來(lái)終止特定進(jìn)程(processId)。然后調(diào)用設(shè)置 WNOHANG 位的 waitpid 子例程。這將檢查特定的進(jìn)程并終止。
Equivalent Linux code
pid_t nRet;
int status;
kill( processId, SIGTERM );
nRet = waitpid( processId, &status, WNOHANG); //Check specified
process is terminated
|
進(jìn)程依然存在服務(wù)
Win32 OpenProcess 返回特定進(jìn)程(processId)的句柄。如果函數(shù)成功,則 GetExitCodeProcess 將獲得特定進(jìn)程的狀態(tài),并檢查進(jìn)程的狀態(tài)是否是 STILL_ACTIVE。
Win 32 sample
HANDLE nProc;
DWORD dwExitCode;
nProc = OpenProcess(PROCESS_ALL_ACCESS, TRUE, processId);
if ( nProc != NULL)
{
GetExitCodeProcess( nProc, &dwExitCode );
if (dwExitCode == STILL_ACTIVE )
return RC_PROCESS_EXIST;
else
return RC_PROCESS_NOT_EXIST;
}
else
return RC_PROCESS_NOT_EXIST;
|
在 Linux 中,使用 kill 子例程發(fā)送通過(guò) Signal
參數(shù)指定的信號(hào)給由 Process
參數(shù)(processId)指定的特定進(jìn)程。Signal 參數(shù)是一個(gè) null 值,會(huì)執(zhí)行錯(cuò)誤檢查,但不發(fā)送信號(hào)。
Equivalent Linux code
if ( kill ( processId, 0 ) == -1 && errno == ESRCH ) // No process can
be found
// corresponding to processId
return RC_PROCESS_NOT_EXIST;
else
return RC_PROCESS_EXIST;
|
線程模型
線程 是系統(tǒng)分配 CPU 時(shí)間的基本單位;當(dāng)?shù)却{(diào)度時(shí),每個(gè)線程保持信息來(lái)保存它的“上下文”。每個(gè)線程都可以執(zhí)行程序代碼的任何部分,并共享進(jìn)程的全局變量。
構(gòu)建于 clone()
系統(tǒng)調(diào)用之上的 LinuxThreads 是一個(gè) pthreads 兼容線程系統(tǒng)。因?yàn)榫€程由內(nèi)核來(lái)調(diào)度,所以 LinuxThreads 支持阻塞的 I/O 操作和多處理器。不過(guò),每個(gè)線程實(shí)際上是一個(gè) Linux 進(jìn)程,所以一個(gè)程序可以擁有的線程數(shù)目受內(nèi)核所允許的進(jìn)程總數(shù)的限制。Linux 內(nèi)核沒(méi)有為線程同步提供系統(tǒng)調(diào)用。Linux Threads 庫(kù)提供了另外的代碼來(lái)支持對(duì)互斥和條件變量的操作(使用管道來(lái)阻塞線程)。
對(duì)有外加 LinuxThreads 的信號(hào)處理來(lái)說(shuō),每個(gè)線程都會(huì)繼承信號(hào)處理器(如果派生這個(gè)線程的父進(jìn)程注冊(cè)了一個(gè)信號(hào)處理器的話。只有在 Linux Kernel 2.6 和更高版本中支持的新特性才會(huì)包含 POSIX 線程支持,比如 用于 Linux 的 Native POSIX Thread Library(NPTL)。
線程同步、等待函數(shù)、線程本地存儲(chǔ)以及初始化和終止抽象是線程模型的重要部分。在這些之下,線程服務(wù)只負(fù)責(zé):
- 新線程被創(chuàng)建,threadId 被返回。
- 通過(guò)調(diào)用 pthread_exit 函數(shù)可以終止當(dāng)前的新線程。
線程映射表
Win32 |
Linux |
_beginthread |
pthread_attr_init pthread_attr_setstacksize pthread_create |
_endthread |
pthread_exit |
TerminateThread |
pthread_cancel |
GetCurrentThreadId |
pthread_self |
線程創(chuàng)建
Win32 應(yīng)用程序使用 C 運(yùn)行期庫(kù),而不使用 Create_Thread API。使用了 _beginthread 和 _endthread 例程。這些例程會(huì)考慮任何可重入性(reentrancy)和內(nèi)存不足問(wèn)題、線程本地存儲(chǔ)、初始化和終止抽象。
Linux 使用 pthread 庫(kù)調(diào)用 pthread_create()
來(lái)派生一個(gè)線程。
threadId 作為一個(gè)輸出參數(shù)返回。為創(chuàng)建一個(gè)新線程,要傳遞一組參數(shù)。當(dāng)新線程被創(chuàng)建時(shí),這些參數(shù)會(huì)執(zhí)行一個(gè)函數(shù)。stacksize 用作新線程的棧的大小(以字節(jié)為單位),當(dāng)新線程開始執(zhí)行時(shí),實(shí)際的參數(shù)被傳遞給函數(shù)。
指定線程程序(函數(shù))
進(jìn)行創(chuàng)建的線程必須指定要執(zhí)行的新線程的啟動(dòng)函數(shù)的代碼。啟動(dòng)地址是 threadproc 函數(shù)(帶有一個(gè)單獨(dú)的參數(shù),即 threadparam)的名稱。如果調(diào)用成功地創(chuàng)建了一個(gè)新線程,則返回 threadId。Win32 threadId 的類型定義是 HANDLE。Linux threadId 的類型定義是 pthread_t。
- threadproc
- 要執(zhí)行的線程程序(函數(shù))。它接收一個(gè)單獨(dú)的 void 參數(shù)。
- threadparam
- 線程開始執(zhí)行時(shí)傳遞給它的參數(shù)。
設(shè)置棧大小
在 Win32 中,線程的棧由進(jìn)程的內(nèi)存空間自動(dòng)分配。系統(tǒng)根據(jù)需要增加棧的大小,并在線程終止時(shí)釋放它。在 Linux 中,棧的大小在 pthread 屬性對(duì)象中設(shè)置;pthread_attr_t 傳遞給庫(kù)調(diào)用 pthread_create()
。
Win32 sample
int hThrd;
DWORD dwIDThread;
unsigned stacksize;
void *thrdparam; //parameter to be passed to the thread when it
//begins execution
HANDLE *threadId;
if( stacksize < 8192 )
stacksize = 8192;
else
stacksize = (stacksize/4096+1)*4096;
hThrd = _beginthread( thrdproc, // Definition of a thread entry
//point
NULL,
stacksize,
thrdparam);
if (hThrd == -1)
return RC_THREAD_NOT_CREATED);
*threadId = (HANDLE) hThrd;
__________________________________________________________________
Equivalent Linux code
#include <pthread.h>
pthread_t *threadId;
void thrdproc (void *data); //the thread procedure (function) to
//be executed.
//It receives a single void parameter
void *thrdparam; //parameter to be passed to the thread when it
//begins execution
pthread_attr_t attr;
int rc = 0;
if (thrdproc == NULL || threadId == NULL)
return RC_INVALID_PARAM);
if (rc = pthread_attr_init(&attr))
return RC_THREAD_NOT_CREATED); // EINVAL, ENOMEM
if (rc = pthread_attr_setstacksize(&attr, stacksize))
return RC_THREAD_NOT_CREATED); // EINVAL, ENOSYS
if (rc = pthread_create(threadId, &attr, (void*(*)(void*))thrdproc,
thrdparam))
return RC_THREAD_NOT_CREATED); // EINVAL, EAGAIN
|
終止線程服務(wù)
在 Win32 中,一個(gè)線程可以使用 TerminateThread 函數(shù)終止另一個(gè)線程。不過(guò),線程的棧和其他資源將不會(huì)被收回。如果線程終止自己,則這樣是可取的。在 Linux 中,pthread_cancel 可以終止由具體的 threadId 所標(biāo)識(shí)的線程的執(zhí)行。
Win32 |
Linux |
TerminateThread((HANDLE *) threadId, 0); |
pthread_cancel(threadId); |
線程狀態(tài)
在 Linux 中,線程默認(rèn)創(chuàng)建為可合并(joinable)狀態(tài)。另一個(gè)線程可以使用 pthread_join()
同步線程的終止并重新獲得終止代碼。可合并線程的線程資源只有在其被合并后才被釋放。
Win32 使用 WaitForSingleObject()
來(lái)等待線程終止。
Linux 使用 pthread_join 完成同樣的事情。
Win32 |
Linux |
unsigned long rc;
rc = (unsigned long) WaitForSingleObject (threadId, INIFITE); |
unsigned long rc=0;
rc = pthread_join(threadId, void **status); |
結(jié)束當(dāng)前線程服務(wù)的執(zhí)行
在 Win32 中,使用 _endthread()
來(lái)結(jié)束當(dāng)前線程的執(zhí)行。在 Linux 中,推薦使用 pthread_exit()
來(lái)退出一個(gè)線程,以避免顯式地調(diào)用 exit 例程。在 Linux 中,線程的返回值是 retval,可以由另一個(gè)線程調(diào)用 pthread_join()
來(lái)獲得它。
Win32 |
Linux |
_endthread(); |
pthread_exit(0); |
獲得當(dāng)前線程 ID 服務(wù)
在 Win32 進(jìn)程中,GetCurrentThreadId 函數(shù)獲得進(jìn)行調(diào)用的線程的線程標(biāo)識(shí)符。Linux 使用 pthread_self()
函數(shù)來(lái)返回進(jìn)行調(diào)用的線程的 ID。
Win32 |
Linux |
GetCurrentThreadId() |
pthread_self() |
sleep 服務(wù)
Win32 |
Equivalent Linux code |
Sleep (50) |
struct timespec timeOut,remains;
timeOut.tv_sec = 0; timeOut.tv_nsec = 500000000; /* 50 milliseconds */
nanosleep(&timeOut, &remains); |
用于 Win32 Sleep 函數(shù)的時(shí)間段的單位是毫秒,可以是 INFINITE,在這種情況下線程將永遠(yuǎn)不會(huì)再重新開始。 Linux sleep 函數(shù)類似于 Sleep,但是時(shí)間段以秒來(lái)計(jì)。要獲得毫秒級(jí)的精度,則使用 nanosleep 函數(shù)來(lái)提供同樣的服務(wù)。
Win32 SleepEx 函數(shù)掛起 當(dāng)前線程,直到下面事件之一發(fā)生:
- 一個(gè) I/O 完成回調(diào)函數(shù)被調(diào)用。
- 一個(gè)異步過(guò)程調(diào)用(asynchronous procedure call,APC)排隊(duì)到此線程。
- 最小超時(shí)時(shí)間間隔已經(jīng)過(guò)去。
Linux 使用 sched_yield 完成同樣的事情。
Win32 |
Linux |
SleepEx (0,0) |
sched_yield() |
共享內(nèi)存服務(wù)
共享內(nèi)存允許多個(gè)進(jìn)程將它們的部分虛地址映射到一個(gè)公用的內(nèi)存區(qū)域。任何進(jìn)程都可以向共享內(nèi)存區(qū)域?qū)懭霐?shù)據(jù),并且數(shù)據(jù)可以由其他進(jìn)程讀取或修改。共享內(nèi)存用于實(shí)現(xiàn)進(jìn)程間通信媒介。不過(guò),共享內(nèi)存不為使用它的進(jìn)程提供任何訪問(wèn)控制。使用共享內(nèi)存時(shí)通常會(huì)同時(shí)使用“鎖”。
一個(gè)典型的使用情形是:
- 某個(gè)服務(wù)器創(chuàng)建了一個(gè)共享內(nèi)存區(qū)域,并建立了一個(gè)共享的鎖對(duì)象。
- 某個(gè)客戶機(jī)連接到服務(wù)器所創(chuàng)建的共享內(nèi)存區(qū)域。
- 客戶機(jī)和服務(wù)器雙方都可以使用共享的鎖對(duì)象來(lái)獲得對(duì)共享內(nèi)存區(qū)域的訪問(wèn)。
- 客戶機(jī)和服務(wù)器可以查詢共享內(nèi)存區(qū)域的位置。
共享內(nèi)存映射表
Win32 |
Linux |
CreateFileMaping, OpenFileMapping |
mmap shmget |
UnmapViewOfFile |
munmap shmdt |
MapViewOfFile |
mmap shmat |
創(chuàng)建共享內(nèi)存資源
Win32 通過(guò)共享的內(nèi)存映射文件來(lái)創(chuàng)建共享內(nèi)存資源。Linux 使用 shmget/mmap 函數(shù)通過(guò)直接將文件數(shù)據(jù)合并入內(nèi)存來(lái)訪問(wèn)文件。內(nèi)存區(qū)域是已知的作為共享內(nèi)存的段。
文件和數(shù)據(jù)也可以在多個(gè)進(jìn)程和線程之間共享。不過(guò),這需要進(jìn)程或線程之間同步,由應(yīng)用程序來(lái)處理。
如果資源已經(jīng)存在,則 CreateFileMapping()
重新初始化共享資源對(duì)于進(jìn)程的約定。如果沒(méi)有足夠的空閑內(nèi)存來(lái)處理錯(cuò)誤的共享資源,此調(diào)用可能會(huì)失敗。 OpenFileMapping()
需要共享資源必須已經(jīng)存在;這個(gè)調(diào)用只是請(qǐng)求對(duì)它的訪問(wèn)。
在 Win32 中,CreateFileMapping 不允許您增加文件大小,但是在 Linux 中不是這樣。在 Linux 中,如果資源已經(jīng)存在,它將被重新初始化。它可能被銷毀并重新創(chuàng)建。Linux 創(chuàng)建可以通過(guò)名稱訪問(wèn)的共享內(nèi)存。 open()
系統(tǒng)調(diào)用確定映射是否可讀或可寫。傳遞給 mmap()
的參數(shù)必須不能與 open()
時(shí)請(qǐng)求的訪問(wèn)相沖突。 mmap()
需要為映射提供文件的大小(字節(jié)數(shù))。
對(duì) 32-位內(nèi)核而言,有 4GB 虛地址空間。最前的 1 GB 用于設(shè)備驅(qū)動(dòng)程序。最后 1 GB 用于內(nèi)核數(shù)據(jù)結(jié)構(gòu)。中間的 2GB 可以用于共享內(nèi)存。當(dāng)前,POWER 上的 Linux 允許內(nèi)核使用 4GB 虛地址空間,允許用戶應(yīng)用程序使用最多 4GB 虛地址空間。
映射內(nèi)存訪問(wèn)保護(hù)位
Win32 |
Linux |
PAGE_READONLY |
PROT_READ |
PAGE_READWRITE |
(PROT_READ | PROT_WRITE) |
PAGE_NOACCESS |
PROT_NONE |
PAGE_EXECUTE |
PROT_EXEC |
PAGE_EXECUTE_READ |
(PROT_EXEC |PROT_READ) |
PAGE_EXECUTE_READWRITE |
(PROT_EXEC | PROT_READ | PROT_WRITE) |
要獲得 Linux 共享內(nèi)存的分配,您可以查看 /proc/sys/kernel 目錄下的 shmmax、shmmin 和 shmall。
在 Linux 上增加共享內(nèi)存的一個(gè)示例:
echo 524288000 > /proc/sys/kernel/shmmax
|
最大共享內(nèi)存增加到 500 MB。
下面是創(chuàng)建共享內(nèi)存資源的 Win32 示例代碼,以及相對(duì)應(yīng)的 Linux nmap 實(shí)現(xiàn)。
Win32 sample code
typedef struct
{
// This receives a pointer within the current process at which the
// shared memory is located.
// The same shared memory may reside at different addresses in other
// processes which share it.
void * location;
HANDLE hFileMapping;
}mem_shared_struct, *mem_shared, *token;
mem_shared_struct *token;
if ((*token = (mem_shared) malloc(sizeof(mem_shared_struct))) == NULL)
return RC_NOT_ENOUGH_MEMORY;
if (newmode == new_shared_create)
(*token)->hFileMapping = CreateFileMapping((HANDLE) 0xFFFFFFFF, NULL,
PAGE_READWRITE,
0,
(DWORD) size,
(LPSTR) name);
else
(*token)->hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS,
FALSE,
(LPSTR) name);
if ((*token)->hFileMapping == NULL)
{
free( *token );
return RC_SHM_NOT_CREATED );
}
(*token)->location = MapViewOfFile((*token)->hFileMapping,
FILE_MAP_READ | FILE_MAP_WRITE,
0, 0, 0);
if ((*token)->location == NULL)
{
CloseHandle((*token)->hFileMapping);
free(*token);
return RC_OBJECT_NOT_CREATED;
}
____________________________________________________________________
Equivalent Linux code
typedef struct
{
void *location;
int nFileDes;
cs_size nSize;
char *pFileName;
}mem_shared_struct, *mem_shared, token;
mode_t mode=0;
int flag=0;
int i, ch='\0';
char name_buff[128];
if (newmode == new_shared_create)
flag = O_CREAT;
else if (newmode != new_shared_attach)
return RC_INVALID_PARAM;
if ((*token = (mem_shared) malloc(sizeof(mem_shared_struct))) == NULL)
return RC_NOT_ENOUGH_MEMORY;
strcpy(name_buff, "/tmp/" );
strcat(name_buff, name );
if(((*token)->pFileName = malloc(strlen(name_buff)+1)) == NULL )
{
free(*token);
return RC_NOT_ENOUGH_MEMORY;
}
mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
flag |= O_RDWR;
if(newmode == new_shared_create)
remove(name_buff);
if(((*token)->nFileDes = open(name_buff, flag, mode)) < 0)
{
free((*token)->pFileName);
free(*token);
return RC_OBJECT_NOT_CREATED;
}
if(newmode == new_shared_create)
{
lseek((*token)->nFileDes, size - 1, SEEK_SET);
write((*token)->nFileDes, &ch, 1);
}
if(lseek((*token)->nFileDes, 0, SEEK_END) < size)
{
free((*token)->pFileName);
free(*token);
return RC_MEMSIZE_ERROR;
}
(*token)->location = mmap( 0, size,
PROT_READ | PROT_WRITE,
MAP_VARIABLE | MAP_SHARED,
(*token)->nFileDes,
0);
if((int)((*token)->location) == -1)
{
free((*token)->pFileName);
free(*token);
return RC_OBJECT_NOT_CREATED;
}
(*token)->nSize = size;strcpy((*token)->pFileName, name_buff);
|
刪除共享內(nèi)存資源
為銷毀共享內(nèi)存資源,munmap 子例程要取消被映射文件區(qū)域的映射。munmap 子例程只是取消對(duì) mmap 子例程的調(diào)用而創(chuàng)建的區(qū)域的映射。如果某個(gè)區(qū)域內(nèi)的一個(gè)地址被 mmap 子例程取消映射,并且那個(gè)區(qū)域后來(lái)未被再次映射,那么任何對(duì)那個(gè)地址的引用將導(dǎo)致給進(jìn)程發(fā)出一個(gè) SIGSEGV 信號(hào)。
Win32 |
等價(jià)的 Linux 代碼 |
UnmapViewOfFile(token->location);
CloseHandle(token->hFileMapping); |
munmap(token->location, token->nSize);
close(token->nFileDes);
remove(token->pFileName);
free(token->pFileName); |
結(jié)束語(yǔ)
本文介紹了關(guān)于初始化和終止、進(jìn)程、線程及共享內(nèi)存服務(wù)從 Win32 API 到 POWER 上 Linux 的映射。這絕對(duì)沒(méi)有涵蓋所有的 API 映射,而且讀者只能將此信息用作將 Win32 C/C++ 應(yīng)用程序遷移到 POWER Linux 的一個(gè)參考。
特別聲明
IBM、eServer 和 pSeries 是 IBM Corporation 在美國(guó)和/或其它國(guó)家或地區(qū)的商標(biāo)。
UNIX 是 The Open Group 在美國(guó)和其它國(guó)家或地區(qū)的注冊(cè)商標(biāo)。
Microsoft 和 Windows 是 Microsoft Corporation 在美國(guó)和/或其它國(guó)家或地區(qū)的商標(biāo)或注冊(cè)商標(biāo)。
所有其他商標(biāo)和注冊(cè)商標(biāo)是它們相應(yīng)公司的財(cái)產(chǎn)。
此出版物/說(shuō)明是在美國(guó)完成的。IBM 可能不在其他國(guó)家或地區(qū)提供在此討論的產(chǎn)品、程序、服務(wù)或特性,而且信息可能會(huì)不加聲明地加以修改。有關(guān)您當(dāng)前所在區(qū)域的產(chǎn)品、程序、服務(wù)和特性的信息,請(qǐng)向您當(dāng)?shù)氐?IBM 代表咨詢。任何對(duì) IBM 產(chǎn)品、程序、服務(wù)或者特性的引用并非意在明示或暗示只能使用 IBM 的產(chǎn)品、程序、服務(wù)或者特性。只要不侵犯 IBM 的知識(shí)產(chǎn)權(quán),任何同等功能的產(chǎn)品、程序、服務(wù)或特性,都可以代替 IBM 產(chǎn)品、程序、服務(wù)或特性。
涉及非 IBM 產(chǎn)品的信息可從這些產(chǎn)品的供應(yīng)商、其出版說(shuō)明或其他可公開獲得的資料中獲取,并不構(gòu)成 IBM 對(duì)此產(chǎn)品的認(rèn)可。非 IBM 價(jià)目及性能數(shù)字資源取自可公開獲得的信息,包括供應(yīng)商的聲明和供應(yīng)商的全球主頁(yè)。 IBM 沒(méi)有對(duì)這些產(chǎn)品進(jìn)行測(cè)試,也無(wú)法確認(rèn)其性能的精確性、兼容性或任何其他關(guān)于非 IBM 產(chǎn)品的聲明。有關(guān)非 IBM 產(chǎn)品性能的問(wèn)題應(yīng)當(dāng)向這些產(chǎn)品的供應(yīng)商提出。
有關(guān)非 IBM 產(chǎn)品性能的問(wèn)題應(yīng)當(dāng)向這些產(chǎn)品的供應(yīng)商提出。IBM 公司可能已擁有或正在申請(qǐng)與本說(shuō)明中描述的內(nèi)容有關(guān)的各項(xiàng)專利。提供本說(shuō)明并未授予用戶使用這些專利的任何許可。您可以用書面方式將許可查詢寄往: IBM Director of Licensing IBM Corporation North Castle Drive Armonk, NY 10504-1785 U.S.A。所有關(guān)于 IBM 未來(lái)方向或意向的聲明都可隨時(shí)更改或收回,而不另行通知,它們僅僅表示了目標(biāo)和意愿而已。聯(lián)系您本地的 IBM 辦公人員或者 IBM 授權(quán)的轉(zhuǎn)銷商,以獲得特定的 Statement of General Direction 的全文。
本說(shuō)明中所包含的信息沒(méi)有提交給任何正式的 IBM 測(cè)試,而只是“按原樣”發(fā)布。雖然 IBM 可能為了其在特定條件下的精確性而已經(jīng)對(duì)每個(gè)條目進(jìn)行了檢查,但不保證在其他地方可以獲得相同的或者類似的結(jié)果。使用此信息或者實(shí)現(xiàn)這里所描述的任何技術(shù)是客戶的責(zé)任,取決于客戶評(píng)價(jià)并集成它們到客戶的操作環(huán)境的能力。嘗試為他們自己的環(huán)境而修改這些技術(shù)的客戶,這樣做所帶來(lái)的風(fēng)險(xiǎn)由他們自行承擔(dān)。
參考資料
- 您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文。
作者簡(jiǎn)介 Nam Keung 是一名高級(jí)程序員,他曾致力于 AIX 通信開發(fā)、AIX 多媒體、SOM/DSOM 開發(fā)和 Java 性能方面的工作。他目前的工作包括幫助 ISV 進(jìn)行應(yīng)用程序設(shè)計(jì)、部署應(yīng)用程序、性能調(diào)優(yōu)和關(guān)于 pSeries 平臺(tái)的教育。他從 1987 年起就是 IBM 的程序員了。您可以通過(guò) namkeung@us.ibm.com 與 Nam 聯(lián)系。 |
Chakarat Skawratananond 是 IBM eServer Solutions Enablement 組織的一名技術(shù)顧問(wèn),在那里,他幫助獨(dú)立軟件開發(fā)商在 IBM pSeries 平臺(tái)上使用他們的用于 AIX 5L 和 Linux 的應(yīng)用程序。您可以通過(guò) chakarat@us.ibm.com 與他聯(lián)系。
|