程序和執行緒(修改優先順序 Windows IPC 應用移植到 Linux)
阿新 • • 發佈:2019-01-29
當前,很多全球商務和服務都正在趨於開源 —— 業界的所有主要參與者都在爭取實現此目標。這一趨勢催生了一個重要的遷移模式:為不同平臺(Windows、OS2、Solaris 等)維持的現有產品將被移植到開放原始碼的 Linux 平臺。
很多應用程式在設計時並未考慮到需要將它們移植到 Linux。這有可能使移植成為一件痛苦的事情,但並非絕對如此。本系列文章的目的是,幫助您將涉及到 IPC 和執行緒原語的複雜應用程式從 Windows 遷移到 Linux。我們與您分享遷移這些關鍵應用程式的經驗,包括要求執行緒同步的多執行緒應用程式以及要求程序間同步的多程序應用程式。
簡言之,可以將此係列文章看作是一個對映文件 —— 它提供了與執行緒、程序和程序間通訊元素(互斥體、訊號量等等)相關的各種 Windows 呼叫到 Linux 呼叫的對映。我們將那些對映分為三個部分:
- 第 1 部分涉及的是程序和執行緒。
- 第 2 部分處理的是訊號量與事件。
- 第 3 部分涵蓋了訊號量、關鍵區域和等待函式。
程序
Windows 中和 Linux 中的基本執行單位是不同的。在 Windows 中,執行緒是基本執行單位,程序是一個容納執行緒的容器。
在 Linux 中,基本執行單位是程序。Windows API 所提供的功能可以直接對映到 Linux 系統呼叫:
表 1. 程序對映
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
清單 2. 相應的 Linux 程序程式碼
#include
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 中的執行緒。內容涵蓋了建立執行緒、設定其屬性以及修改其優先順序。
表 2. 執行緒對映
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);
}
清單 4. Linux 相應的執行緒程式碼
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);
}