將 Windows IPC 應用移植到 Linux,第 1 部分: 進程和線程關於複雜的多線程和多進程應用程序的映射指南 |
|
級別: 初級
Srinivasan S. Muthuswamy ([email protected]), 軟件工程師, IBM Global Services Group
Kavitha Varadarajan ([email protected]), 軟件工程師, IBM India Software Lab
2005 年 5 月 08 日
隨着開發者將原本普遍的 Windows® 應用遷移到 Linux™ 平臺,正在進行的向開源遷移的浪潮有可能引發極大的移植問題。這個由三部分構成的系列文章提供一個映射指南,並附有例子,能夠簡化從 Windows 到 Linux 的轉變。第 1 部分介紹了進程和線程。
當前,很多全球商務和服務都正在趨於開源 —— 業界的所有主要參與者都在爭取實現此目標。這一趨勢催生了一個重要的遷移模式:爲不同平臺(Windows、OS2、Solaris 等)維持的現有產品將被移植到開放源代碼的 Linux 平臺。
很多應用程序在設計時並未考慮到需要將它們移植到 Linux。這有可能使移植成爲一件痛苦的事情,但並非絕對如此。本系列文章的目的是,幫助您將涉及到 IPC 和線程原語的複雜應用程序從 Windows 遷移到 Linux。我們與您分享遷移這些關鍵應用程序的經驗,包括要求線程同步的多線程應用程序以及要求進程間同步的多進程應用程序。
簡言之,可以將此係列文章看作是一個映射文檔 —— 它提供了與線程、進程和進程間通信元素(互斥體、信號量等等)相關的各種 Windows 調用到 Linux 調用的映射。我們將那些映射分爲三個部分:
- 第 1 部分涉及的是進程和線程。
- 第 2 部分處理的是信號量與事件。
- 第 3 部分涵蓋了信號量、關鍵區域和等待函數。
Windows 中和 Linux 中的基本執行單位是不同的。在 Windows 中,線程是基本執行單位,進程是一個容納線程的容器。
在 Linux 中,基本執行單位是進程。Windows API 所提供的功能可以直接映射到 Linux 系統調用:
Windows | Linux | 類別 |
CreateProcess() CreateProcessAsUser() |
fork() setuid() exec() |
可映射 |
TerminateProcess() |
kill() |
可映射 |
SetThreadpriority() GetThreadPriority() |
Setpriority() getPriority() |
可映射 |
GetCurrentProcessID() |
getpid() |
可映射 |
Exitprocess() |
exit() |
可映射 |
Waitforsingleobject() Waitformultipleobject() GetExitCodeProcess() |
waitpid() Using Sys V semaphores, Waitforsingleobject/multipleobject 不能實現 |
與上下文相關 |
GetEnvironmentVariable SetEnvironmentVariable |
getenv() setenv() |
可映射 |
“類別”一列(解釋了本文中所使用的分類結構)表明了 Windows 結構是否 可映射 或者 與上下文相關:
- 如果可映射,則 Windows 結構可以映射到特定的 Linux 結構(需要仔細檢查類型、參數、返回代碼等)。Windows 和 Linux 結構都提供了類似的功能。
- 如果是與上下文相關,則 Linux 中可能有(也可能沒有)相應於給定的 Windows 結構的結構,或者 Linux 可能有不只一個提供類似功能的結構。無論是哪種情況,都要根據應用程序上下文才能確定要使用哪個特定的 Linux 結構。
在 Windows 中,您可以使用 CreateProcess()
來創建一個新的進程。 CreateProcess()
函數創建一個新的進程及其主線程,如下:
BOOL CreateProcess( LPCTSTR lpApplicationName, // name of executable module LPTSTR lpCommandLine, // command line string LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD BOOL bInheritHandles, // handle inheritance option DWORD dwCreationFlags, // creation flags LPVOID lpEnvironment, // new environment block LPCTSTR lpCurrentDirectory, // current directory name LPSTARTUPINFO lpStartupInfo, // startup information LPPROCESS_INFORMATION lpProcessInformation // process information ) |
bInheritHandles
確定了子進程是否要繼承父進程的句柄。lpApplicationName
和 lpCommandLine
給出了將要被啓動的進程的名稱與路徑。lpEnvironment
定義了進程可使用的環境變量。
在 Linux 中,exec
* 家族函數使用一個新的進程映像取代當前進程映像(如下所示):
int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg , ..., char * const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); |
exec
* 的這些版本只是內核函數 execve()
(int execve(const char *filename, char *const argv [], char *const envp[])
)的各種調用接口。在這裏,argv
是包含有參數 list
的指針,envp
是包含有環境變量列表(主要是 key=value 對)的指針。
它必須與 fork()
命令一起使用,所以父進程和子進程都在運行: pid_t fork(void)
。fork()
會創建一個子進程,與父進程相比只是 PID 和 PPID 不同;實際上,資源利用設爲 0。
默認情況下,exec()
繼承父進程的組和用戶 ID,這就使得它會依賴於父進程。可以使用以下方法來改變:
- 設置指定程序文件的
set-uid
和set-gid
位 - 使用
setpgid()
和setuid()
系統調用
CreateProcessAsUser()
函數與 CreateProcess()
類似,只是新進程是在用戶通過 hToken
參數描述的安全上下文中運行。在 Linux 中,沒有與此函數惟一對應的函數,但是可以使用下面的邏輯來實現對它的複製:
- 使用
fork()
創建一個具有新的 PID 的子進程 - 使用
setuid()
切換到那個新的 PID - 使用
exec()
將現有進程改變爲將要執行的進程
在 Windows 中,您可以使用 TerminateProcess()
強制終止一個運行中的進程。
BOOL TerminateProcess( HANDLE hProcess, // handle to the process UINT uExitCode // exit code for the process ); |
這個函數終止運行中的進程及其相關線程。只是在非常極端的場合纔會使用這個函數。
在 Linux 中,您可以使用 kill()
來強行殺死一個進程: int kill(pid_t pid, int sig)
。這個系統調用會終止 id 爲 PID 的進程。您也可以使用它向任何組或者進程發出信號。
在子進程依賴於父進程的情況下,您可以在父進程中使用等待函數來等待子進程的終止。在 Windows 中,您可以使用 WaitForSingleObject()
函數調用來實現此功能。
您可以使用 WaitForMultipleObject()
函數來等待多個對象。
DWORD WaitForMultipleObjects( DWORD nCount, // number of handles in array CONST HANDLE *lpHandles, // object-handle array BOOL bWaitAll, // wait option DWORD dwMilliseconds // time-out interval ); |
您可以向對象句柄數組(object-handle array)中填充很多需要等待的對象。根據 bWaitALL
選項,您既可以等待所有對象被信號通知,也可以等待其中任意一個被信號通知。
在這兩個函數中,如果您想等待有限的一段時間,則可以在第二個參數中指定時間間隔。如果您想無限制等待,那麼使用 INFINITE
作爲 dwMilliseconds
的值。將 dwMilliseconds
設置爲 0 則只是檢測對象的狀態並返回。
在 Linux 中,如果您希望無限期等待進程被殺死,則可以使用 waitpid()
。在 Linux 中,使用 waitpid()
調用無法等待限定的時間。
在這段代碼中:pid_t waitpid(pid_t pid, int *status, int options)
,waitpid()
會無限期等待子進程的終止。在 Windows 和 Linux 中,等待函數會掛起當前進程的執行,直到它完成等待,不過,在 Windows 中可以選擇在指定的時間後退出。使用 System V 信號量,您可以實現類似於 WaitForSingleObject()
和 WaitForMultipleObject()
的限時等待或者 NO WAIT 功能,在本系列的第 2 部分中將討論此內容。本系列的第 3 部分將深入討論等待函數。
退出進程指的是優雅(graceful)地退出進程,並完成適當的清除工作。在 Windows 中,您可以使用 ExitProcess()
來執行此操作。
VOID ExitProcess( UINT uExitCode // exit code for all threads ); |
ExitProcess()
是在進程結束處執行的方法。這個函數能夠乾淨地停止進程。包括調用所有鏈接到的動態鏈接庫(DLL)的入口點函數,給出一個值,指出這個進程正在解除那個 DLL 的鏈接。
Linux 中與 ExitProcess()
相對應的是 exit()
:void exit(int status);
。
exit()
函數會令程序正常終止,並將 &0377 狀態值返回給父進程。 C 語言標準規定了兩個定義(EXIT_SUCCESS
和 EXIT_FAILURE
),可以被傳遞到狀態參數,以說明終止成功或者不成功。
每個進程都擁有關聯到它的一組環境,其中主要是 name=value 對,指明進程可以訪問的各種環境變量。儘管我們可以在創建進程時指定環境,不過也有特定函數可以在進程創建後設置和獲得環境變量。
在 Windows 中,您可以使用 GetEnvironmentVariable()
和 SetEnvironmentVariable()
來獲得和設置環境變量。
DWORD GetEnvironmentVariable( LPCTSTR lpName, // environment variable name LPTSTR lpBuffer, // buffer for variable value DWORD nSize // size of buffer ); |
如果成功,則此函數返回值緩存的大小,如果指定的名稱並不是一個合法的環境變量名,則返回 0。 SetEnvironmentVariable()
函數爲當前進程設置指定的環境變量的內容。
BOOL SetEnvironmentVariable( LPCTSTR lpName, // environment variable name LPCTSTR lpValue // new value for variable ); |
如果函數成功,則返回值非零。如果函數失敗,則返回值爲零。
在 Linux 中,getenv()
和 setenv()
系統調用提供了相應的功能。
char *getenv(const char *name); int setenv(const char *name, const char *value, int overwrite); |
getenv()
函數會在環境列表中搜索與名稱字符串相匹配的字符串。這個函數會返回一個指向環境中的值的指針,或者如果不匹配則返回 NULL。setenv()
函數將變量名和值添加到環境中,如果那個名稱並不存在。如果環境中已經存在那個名稱,而且如果 overwrite 非零,則它的值會被修改爲 value
。如果 overwrite 爲零,則 name
的值不會被改變。如果成功,則 setenv()
會返回零,如果環境中空間不足,則返回 -1。
下面的例子解釋了我們在本節中討論的內容。
清單 1. Windows 進程代碼
//Sample Application that explain process concepts //Parameters Declaration/Definition int TimetoWait; STARTUPINFO si; PROCESS_INFORMATION pi; LPTSTR lpszCurrValue,LPTSTR lpszVariable; TCHAR tchBuf[BUFSIZE]; BOOL fSuccess; if(argc > 2) { printf("InvalidArgument"); ExitProcess(1); //Failure } //Get and display an environment variable PATH lpszCurrValue = ((GetEnvironmentVariable("PATH",tchBuf, BUFSIZE) > 0) ? tchBuf : NULL); lpszVariable = lpszCurrValue; //Display the environment variable while (*lpszVariable) putchar(*lpszVariable++); putchar('/n'); //Initialise si and pi ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); //Create a childProcess if( !CreateProcess( NULL, // No module name (use command line). "SomeProcess", // Command line. NULL, // Process handle not inheritable. NULL, // Thread handle not inheritable. FALSE, // Set handle inheritance to FALSE. 0, // No creation flags. NULL, // Use parent's environment block. NULL, // Use parent's starting directory. &si, // Pointer to STARTUPINFO structure. &pi ) // Pointer to PROCESS_INFORMATION structure. ) { printf( "CreateProcess failed." ); } // Wait until child process exits. if(argc == 2) { TIMEOUT = atoi(argv[1]); ret = WaitForSingleObject( pi.hProcess, TIMEOUT ); if(ret == WAIT_TIMEOUT) { TerminateProcess(pi.hProcess); } } else { WaitForSingleObject( pi.hProcess, INFINITE ); ... } ExitProcess(0); //Success |
#include <stdlib.h> int main(int argc,char *argv[]) { //Parameters Declaration/Definition char PathName[255]; char *Argptr[20]; int rc; char *EnvValue,*lpszVariable; if(argc > 1) { printf(" Wrong parameters !!"); exit(EXIT_FAILURE); } //Get and display an environment variable PATH EnvValue = getenv("PATH"); if(EnvValue == NULL) { printf("Invalid environment variable passed as param !!"); }else { lpszVariable = EnvValue; while (*lpszVariable) putchar(*lpszVariable++); putchar('/n'); } rc = fork(); //variable rc's value on success would be process ID in the parent //process, and 0 in the child's thread of execution. switch(rc) { case -1: printf("Fork() function failed !!"); ret = -1; break; case 0: printf("Child process..."); setpgid(0,0); //Change the parent grp ID to 0 ret = execv(PathName,Argptr); // there are other flavours of exec available, // u can use any of them based on the arguments. if(ret == -1) { kill(getpid(),0); } break; default: // infinitely waits for child process to die Waitpid(rc,&status,WNOHANG); //Note RC will have PID returned since this is parent process. break; } exit(EXIT_SUCCESS); } |
|
在 Windows 中,線程是基本的執行單位。在進程的上下文中會有一個或多個線程在運行。調度代碼在內核中實現。沒有單獨的“調度器(scheduler)”模塊或例程。
Linux 內核使用的是進程模型,而不是線程模型。Linux 內核提供了一個輕量級進程框架來創建線程;實際的線程在用戶空間中實現。在 Linux 中有多種可用的線程庫(LinuxThreads、NGPT、NPTL 等等)。本文中的資料基於 LinuxThreads 庫,不過這裏的資料也適用於 Red Hat 的 Native POSIX Threading Library(NPTL)。
本節描述 Windows 和 Linux 中的線程。內容涵蓋了創建線程、設置其屬性以及修改其優先級。
Windows | Linux | 類別 |
CreateThread |
pthread_create pthread_attr_init pthread_attr_setstacksize pthread_attr_destroy |
可映射 |
ThreadExit |
pthread_exit |
可映射 |
WaitForSingleObject |
pthread_join pthread_attr_setdetachstate pthread_detach |
可映射 |
SetPriorityClass SetThreadPriority |
setpriority sched_setscheduler sched_setparam pthread_setschedparam pthread_setschedpolicy pthread_attr_setschedparam pthread_attr_setschedpolicy |
與上下文相關 |
在 Windows 中,您可以使用 CreateThread()
來創建線程,創建的線程在調用進程的虛擬地址空間中運行。
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD SIZE_T dwStackSize, // initial stack size LPTHREAD_START_ROUTINE lpStartAddress, // thread function LPVOID lpParameter, // thread argument DWORD dwCreationFlags, // creation option LPDWORD lpThreadId // thread identifier ); |
lpThreadAttributes
是指向線程屬性的指針,決定了線程句柄是否能由子進程繼承。
Linux 使用 pthread 庫調用 pthread_create()
來派生線程:
int pthread_create (pthread_t *thread_id, pthread_attr_t *threadAttr, void * (*start_address)(void *), void * arg); |
注意:在 Windows 中,受可用虛擬內存的限制,一個進程可以創建的線程數目是有限的。默認情況下,每個線程有一兆棧空間。因此,您最多可以創建 2,028 個線程。如果您減小默認棧大小,那麼可以創建更多線程。在 Linux 中,使用 ULIMIT -a
(limits for all users)可以獲得每個用戶可以創建的線程的最大數目,可以使用 ULIMIT -u
來修改它,不過只有在登錄時纔可以這樣做。 /usr/Include/limit.h 和 ulimit.h 下的頭文件定義了這些內容。您可以修改它們並重新編譯內核,以使其永久生效。對於 POSIX 線程限制而言,local_lim.h 中定義的 THREAD_THREADS_MAX
宏定義了數目的上限。
CreateThread()
中的 lpStartAddress
參數是剛創建的線程要執行的函數的地址。
pthread_create()
庫調用的 start_address
參數是剛創建的線程要執行的函數的地址。
在 Windows 中,系統調用 CreateThread()
的參數 lpParameter
指定了要傳遞給剛創建的線程的參數。它指明瞭將要傳遞給新線程的數據條目的地址。
在 Linux 中,庫調用 pthread_create()
的參數 arg
指定了將要傳遞給新線程的參數。
在 Windows 中,CreateThread()
的參數 dwStackSize
是將要分配給新線程的以字節爲單位的棧大小。棧大小應該是 4 KB 的非零整數倍,最小爲 8 KB。
在 Linux 中,棧大小在線程屬性對象中設置;也就是說,將類型爲 pthread_attr_t
的參數 threadAttr
傳遞給庫調用 pthread_create()
。在設置任何屬性之前,需要通過調用 pthread_attr_init()
來初始化這個對象。使用調用 pthread_attr_destroy()
來銷燬屬性對象:
int pthread_attr_init(pthread_attr_t *threadAttr); int pthread_attr_destroy(pthread_attr_t *threadAttr); |
注意,所有 pthread_attr_setxxxx
調用都有與 pthread_xxxx
調用(如果有)類似的功能,只是您只能在線程創建之前使用 pthread_attr_xxxx
,來更新將要作爲參數傳遞給 pthread_create
的屬性對象。同時,您在創建線程之後的任意時候都可以使用 pthread_xxxx
。
使用調用 pthread_attr_setstacksize()
來設置棧大小: int pthread_attr_setstacksize(pthread_attr_t *threadAttr, int stack_size);
。
在 Windows 中,系統調用 ExitThread()
會終止線程。 dwExitCode
是線程的返回值,另一個線程通過調用 GetExitCodeThread()
就可以得到它。
VOID ExitThread( DWORD dwExitCode // exit code for this thread ); |
Linux 中與此相對應的是庫調用 pthread_exit()
。 retval
是線程的返回值,可以在另一個線程中通過調用 pthread_join()
來獲得它: int pthread_exit(void* retval);
。
在 Windows 中,沒有保持關於線程終止的顯式線程狀態。不過,WaitForSingleObject()
讓線程能夠顯式地等待進程中某個指定的或者非指定的線程終止。
在 Linux 中,默認以可連接(joinable)的狀態創建線程。在可連接狀態中,另一個線程可以同步這個線程的終止,使用函數 pthread_join()
來重新獲得其終止代碼。可連接的線程只有在被連接後才釋放線程資源。
Windows 使用 WaitForSingleObject()
來等待某個線程終止:
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds ); |
其中:
hHandle
是指向線程句柄的指針。dwMilliseconds
是以毫秒爲單位的超時值。如果這個值被設置爲 INFINITE,則它會無限期地阻塞進行調用的線程/進程。
Linux 使用 pthread_join()
來完成同樣的功能: int pthread_join(pthread_t *thread, void **thread_return);
。
在分離的狀態中,線程終止後線程資源會立即被釋放。通過對線程屬性對象調用 pthread_attr_setdetachstate()
可以設置分離狀態: int pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate);
。以可連接狀態創建的線程,稍後可以被轉爲分離狀態,方法是使用 pthread_detach()
調用:int pthread_detach (pthread_t id);
。
在 Windows 中,線程的優先級由其進程的優先級等級以及進程優先級等級中的線程優先級層次決定。在 Linux 中,線程本身就是一個執行單位,有其自己的優先級。它與其進程的優先級沒有依賴關係。
在 Windows 中,您可以使用 SetPriorityClass()
來設置特定進程的優先級等級:
BOOL SetPriorityClass( HANDLE hProcess, // handle to the process DWORD dwPriorityClass // Priority class ); |
dwPriorityClass
是進程的優先級等級,它可以設置爲下列值中的任意一個:
IDLE_PRIORITY_CLASS
BELOW_NORMAL_PRIORITY_CLASS
NORMAL_PRIORITY_CLASS
ABOVE_NORMAL_PRIORITY_CLASS
HIGH_PRIORITY_CLASS
REALTIME_PRIORITY_CLASS
一旦設置了進程的優先級等級,就可以使用 SetThreadPriority()
在進程的優先級等級內部設置線程的優先級層次:
BOOL SetThreadPriority( HANDLE hThread, int nPriority ); |
nPriority
是線程的優先級值,它被設置爲下列之一;
THREAD_PRIORITY_ABOVE_NORMAL
將優先級設置爲比優先級等級高 1 級。THREAD_PRIORITY_BELOW_NORMAL
將優先級設置爲比優先級等級低 1 級。THREAD_PRIORITY_HIGHEST
將優先級設置爲比優先級等級高 2 級。THREAD_PRIORITY_IDLE
爲IDLE_PRIORITY_CLASS
、BELOW_NORMAL_PRIORITY_CLASS
、NORMAL_PRIORITY_CLASS
、ABOVE_NORMAL_PRIORITY_CLASS
或HIGH_PRIORITY_CLASS
進程將基優先級設置 1,爲REALTIME_PRIORITY_CLASS
進程將基優先級設置爲 16。THREAD_PRIORITY_LOWEST
將優先級設置爲比優先級等級低 2 級。THREAD_PRIORITY_NORMAL
爲優先級等級設置爲普通優先級。THREAD_PRIORITY_TIME_CRITICAL
爲IDLE_PRIORITY_CLASS
、BELOW_NORMAL_PRIORITY_CLASS
、NORMAL_PRIORITY_CLASS
、ABOVE_NORMAL_PRIORITY_CLASS
或HIGH_PRIORITY_CLASS
進程將基優先級設置 15,爲REALTIME_PRIORITY_CLASS
進程將基優先級設置爲 31。
|
爲了結束這一期文章,讓我們來看下面類型的進程和線程的一些例子:
- 普通的或者常規的進程和線程
- 對時間要求嚴格的(time-critical)或者實時的進程和線程
使用 Linux 系統調用 setpriority()
來設置或者修改普通進程和線程的優先級層次。參數的範圍是 PRIO_PROCESS
。將 id 設置爲 0 來修改當前進程(或線程)的優先級。此外,delta 是優先級的值 —— 這一次是從 -20 到 20。另外,要注意在 Linux 中較低的 delta 值代表較高的優先級。所以,使用 +20 設置 IDLETIME
優先級,使用 0 設置 REGULAR
優先級。
在 Windows 中,常規線程的優先級的範圍是從 1(較低的優先級)到 15(較高的優先級)。不過,在 Linux 中,普通非實時進程的優先級範圍是從 -20(較高的)到 +20(較低的)。在使用之前必須對此進行映射: int setpriority(int scope, int id, int delta);
。
您可以使用 Linux 系統調用 sched_setscheduler()
來修改正在運行的進程的調度優先級: int sched_setscheduler(pit_t pid, int policy, const struct sched_param *param);
。
參數 policy 是調度策略。policy 的可能的值是 SCHED_OTHER
(常規的非實時調度)、SCHED_RR
(實時 round-robin 策略)和 SCHED_FIFO
(實時 FIFO 策略)。
在此,param
是指向描述調度優先級結構體的指針。它的範圍是 1 到 99,只用於實時策略。對於其他的(普通的非實時進程),它爲零。
在 Linux 中,作爲一個大家所熟知的調度策略,也可以通過使用系統調用 sched_setparam
來僅修改進程優先級: int sched_setparam(pit_t pid, const struct sched_param *param);
。
LinuxThreads 庫調用 pthread_setschedparam
是 sched_setscheduler
的線程版本,用於動態修改運行着的線程的調度優先級和策略: int pthread_setschedparam(pthread_t target_thread, int policy, const struct sched_param *param);
。
參數 target_thread
告知線程要修改誰的優先級;param
指定了優先級。
LinuxThreads 庫會調用 pthread_attr_setschedpolicy
,並且您可以在線程被創建之前使用 pthread_attr_setschedparam
來設置線程屬性對象的調度策略和優先級層次:
int pthread_attr_setschedpolicy(pthread attr_t *threadAttr, int policy); int pthread_attr_setschedparam(pthread attr_t *threadAttr, const struct sched_param *param); |
在 Windows 中,實時線程的優先級範圍是從 16(較低的優先級)到 31(較高的優先級)。在 Linux 中,實時線程的優先級範圍是從 99(較高的)到 1(較低的優先級)。在使用前必須對此進行映射。
下面的清單闡述了本節中的概念。
清單 3. Windows 線程示例
Main Thread
enum stackSize = 120 * 1024 ;
// create a thread normal and real time thread
DWORD normalTId, realTID;
HANDLE normalTHandle, realTHandle;
normalTHandle = CreateThread(
NULL, // default security attributes
stackSize, // 120K
NormalThread, // thread function
NULL, // argument to thread function
0, // use default creation flags
&normalTId); // returns the thread identifier
// Set the priority class as "High priority"
SetPriorityClass(pHandle, HIGH_PRIORITY_CLASS);
normalTHandle = CreateThread(
NULL, // default security attributes
stackSize, // 120K
NormalThread, // thread function
NULL, // argument to thread function
0, // use default creation flags
&normalTId); // returns the thread identifier
CloseHandle(threadHandle);
...
...
// Thread function
DWORD WINAPI NormalThread ( LPVOID lpParam )
{
HANDLE tHandle,pHandle;
pHandle = GetCurrentProcess();
tHandle = GetCurrentThread();
// Set the priority class as "High priority"
SetPriorityClass(pHandle, HIGH_PRIORITY_CLASS);
// increase the priority by 2 points above the priority class
SetThreadPriority(tHandle,THREAD_PRIORITY_HIGHEST);
// perform job at high priority
...
...
...
// Reset the priority class as "Normal"
SetPriorityClass(pHandle, NORMAL_PRIORITY_CLASS);
// set the priority back to normal
SetThreadPriority(tHandle,THREAD_PRIORITY_NORMAL);
// Exit thread
ExitThread(0);
}
// Thread function
DWORD WINAPI RealTimeThread ( LPVOID lpParam )
{
HANDLE tHandle, pHandle ;
pHandle = GetCurrentProcess();
tHandle = GetCurrentThread ();
// Set the priority class as "Real time"
SetPriorityClass(pHandle, REALTIME_PRIORITY_CLASS);
// increase the priority by 2 points above the priority class
SetThreadPriority(tHandle,THREAD_PRIORITY_HIGHEST);
// do time critical work
...
...
...
// Reset the priority class as "Normal"
SetPriorityClass(pHandle, NORMAL_PRIORITY_CLASS);
// Reset the priority back to normal
SetThreadPriority(tHandle,THREAD_PRIORITY_NORMAL);
ExitThread(0);
}
|
static void * RegularThread (void *);
static void * CriticalThread (void *);
// Main Thread
pthread_t thread1, thread2; // thread identifiers
pthread_attr_t threadAttr;
struct sched_param param; // scheduling priority
// initialize the thread attribute
pthread_attr_init(&threadAttr);
// Set the stack size of the thread
pthread_attr_setstacksize(&threadAttr, 120*1024);
// Set thread to detached state. No need for pthread_join
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
// Create the threads
pthread_create(&thread1, &threadAttr, RegularThread, NULL);
pthread_create(&thread2, &threadAttr, CriticalThread,NULL);
// Destroy the thread attributes
pthread_attr_destroy(&threadAttr);
...
...
// Regular non-realtime Thread function
static void * RegularThread (void *d) {
int priority = -18;
// Increase the priority
setpriority(PRIO_PROCESS, 0, priority);
// perform high priority job
...
...
// set the priority back to normal
setpriority(PRIO_PROCESS, 0, 0);
pthread_exit(NULL);
}
// Time Critical Realtime Thread function
static void * CriticalThread (void *d) {
// Increase the priority
struct sched_param param; // scheduling priority
int policy = SCHED_RR; // scheduling policy
// Get the current thread id
pthread_t thread_id = pthread_self();
// To set the scheduling priority of the thread
param.sched_priority = 90;
pthread_setschedparam(thread_id, policy, ¶m);
// Perform time critical task
...
...
// set the priority back to normal
param.sched_priority = 0;
policy = 0; // for normal threads
pthread_setschedparam(thread_id, policy, ¶m);
....
....
pthread_exit(NULL);
}
|
|
本系列文章的第 1 部分已經給出了一個指南,能夠幫助您將 Windows 進程和線程映射到 Linux 中的對應物。系列的第 2 部分介紹了同步對象和原語,首先是信號量和事件。第 3 部分介紹了互斥體、關鍵區域和等待函數。
- 您可以參閱本文在 developerWorks 全球站點上的 英文原文。
- Bradford Nichols、Dick Buttlar 和 Jacqueline Proulx Farrel 所著的 Pthreads Programming 一書中的在線代碼示例解釋了本文中的概念。
- 不要忘記去查看 Linux Threads FAQ、 Linux Manpages Online 以及 LinuxThreads library,以獲得關於特定調用以及在 Linux 中進行線程編程的更多細節。
- 要深入瞭解 Linux 中的線程編程,請閱讀以下 developerWorks 文章:“pthreads 的基本用法” (developerWorks,2004 年 1 月)和“POSIX threads explained” (developerWorks,2000 年 7 月)。
- developerWorks 系列文章 “將應用程序從 OS/2 移植到 Linux 上: 第 1 部分,線程、互斥鎖、信號量” (developerWorks,2004 年 2 月)是瞭解遷移過程中哪些被映射的有益參考資料。
- 在 developerWorks Linux 專區 可以找到更多爲 Linux 開發者準備的參考資料。
- 通過參與 developerWorks blogs 加入 developerWorks 社區。
- 在 Developer 的 Bookstore Linux 區購買 打折出售的 Linux 書籍。
- 訂購免費的 SEK for Linux,這套 DVD (兩張),包含了來自 DB2®、Lotus®、Rational®、 Tivoli® 和 WebSphere® 的最新的用於 Linux 的 IBM 試用軟件。
- 使用可以直接從 developerWorks 下載的 IBM 試用軟件 來改革您的下一個 Linux 開發項目。
Srinivasan S. Muthuswamy 是 IBM Global Services Group 的一位軟件工程師。他於 2000 年加入 IBM,他所精通的編程語言涵蓋了多種平臺上(Linux、Windows、WebSphere、Lotus 等等)的腳本語言以及面向對象和麪向過程的語言。他已經開發的解決方案包括 Linux 和 Windows 上的系統編程以及用於 J2EE 的 Web 解決方案。他在印度的 Coimbatore 國立科技大學(Government College of Technology)獲得計算機工程學士學位,主要致力於集成和遷移。您可以通過 [email protected] 與他聯繫。 |
從 2000 年 12 月起,Kavitha Varadarajan 一直在 IBM India Software Lab 擔任軟件工程師。她的工作經驗包括 host-access 客戶機產品(比如 PCOMM)和網絡軟件(比如通信服務器)的開發與支持。Varadarajan 擁有遷移項目的實踐經驗,其中涉及到了面向對象 IPC Windows 應用程序向 Linux 的移植。她擁有印度的 Tanjore 山姆哈工程大學(Shanmugha College of Engineering)的計算機科學與工程碩士學位。您可以通過 [email protected] 與她聯繫。 |