将 Windows IPC 应用移植到 Linux,第 1 部分: 进程和线程

随着开发者将原本普遍的 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. 进程映射

WindowsLinux类别
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 <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. 线程映射

WindowsLinux类别
CreateThreadpthread_create 
pthread_attr_init 
pthread_attr_setstacksize 
pthread_attr_destroy
可映射
ThreadExitpthread_exit可映射
WaitForSingleObjectpthread_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_CLASSBELOW_NORMAL_PRIORITY_CLASSNORMAL_PRIORITY_CLASSABOVE_NORMAL_PRIORITY_CLASS或 HIGH_PRIORITY_CLASS 进程将基优先级设置 1,为 REALTIME_PRIORITY_CLASS 进程将基优先级设置为 16。
  • THREAD_PRIORITY_LOWEST 将优先级设置为比优先级等级低 2 级。
  • THREAD_PRIORITY_NORMAL 为优先级等级设置为普通优先级。
  • THREAD_PRIORITY_TIME_CRITICAL 为IDLE_PRIORITY_CLASSBELOW_NORMAL_PRIORITY_CLASSNORMAL_PRIORITY_CLASSABOVE_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, &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 部分介绍了互斥体、关键区域和等待函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值