將 Windows IPC 應用程序移植到 Linux,第 1 部分: 進程和線程

將 Windows IPC 應用移植到 Linux,第 1 部分: 進程和線程

關於複雜的多線程和多進程應用程序的映射指南

developerWorks
文檔選項
將此頁作爲電子郵件發送

將此頁作爲電子郵件發送


拓展 Tomcat 應用

下載 IBM 開源 J2EE 應用服務器 WAS CE 新版本 V1.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 系統調用:

表 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 確定了子進程是否要繼承父進程的句柄。lpApplicationNamelpCommandLine 給出了將要被啓動的進程的名稱與路徑。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-uidset-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_SUCCESSEXIT_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 <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 中的線程。內容涵蓋了創建線程、設置其屬性以及修改其優先級。

表 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_IDLEIDLE_PRIORITY_CLASSBELOW_NORMAL_PRIORITY_CLASSNORMAL_PRIORITY_CLASSABOVE_NORMAL_PRIORITY_CLASSHIGH_PRIORITY_CLASS 進程將基優先級設置 1,爲 REALTIME_PRIORITY_CLASS 進程將基優先級設置爲 16。
  • THREAD_PRIORITY_LOWEST 將優先級設置爲比優先級等級低 2 級。
  • THREAD_PRIORITY_NORMAL 爲優先級等級設置爲普通優先級。
  • THREAD_PRIORITY_TIME_CRITICALIDLE_PRIORITY_CLASSBELOW_NORMAL_PRIORITY_CLASSNORMAL_PRIORITY_CLASSABOVE_NORMAL_PRIORITY_CLASSHIGH_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_setschedparamsched_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, &param);
        // Perform time critical task
        ...
        ...
        // set the priority back to normal
        param.sched_priority = 0;
        policy = 0;             // for normal threads
        pthread_setschedparam(thread_id, policy, &param);
        ....
        ....
        pthread_exit(NULL);
   }





回頁首


結束語

本系列文章的第 1 部分已經給出了一個指南,能夠幫助您將 Windows 進程和線程映射到 Linux 中的對應物。系列的第 2 部分介紹了同步對象和原語,首先是信號量和事件。第 3 部分介紹了互斥體、關鍵區域和等待函數。



參考資料



作者簡介

作者照片

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] 與她聯繫。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章