Port windows ipc apps to linux part 1 processes and threads

Port windows ipc apps to linux part 1 processes and threads

A mapping guide for complex, multithreaded, multiprocess applications

The wave of migration to open source in business has the potential to cause a tremendous porting traffic jam as developers move the pervasive Windows® applications to the Linux platform™. In this three-part series, get a mapping guide, complete with examples, to ease your transition from Windows to Linux. Part 1 introduces processes and threads.



Today many global businesses and services are going open source — all the major corporate players in the industry are pushing for it. This trend has spurred a major migration exercise in which lots of existing products maintained for various platforms (Windows, OS2, Solaris, etc.) will be ported to open source Linux platforms.

Many applications are designed without considering the need to port them to Linux. This has the potential to be a porting nightmare, but it doesn’t have to be. The goal of this series of articles is to help you migrate complex applications involving IPC and threading primitives from Windows to Linux. We share our experiences in moving these critical Windows IPC applications, applications that include multithreaded apps that require thread synchronization and multiprocess apps that require interprocess synchronization.

In short, think of this series as a mapping document — it provides mapping of various Windows calls to Linux calls related to threads, processes, and interprocess communication elements (mutexes, semaphores, etc.). We’ve divided the mapping into three chunks:

  • Part 1 deals with processes and threads.
  • Part 2 handles semaphores and events.
  • Part 3 covers mutexes, critical sections, and wait functions.


Basic execution units in Windows and Linux are different. In Windows, the thread is the basic execution unit, and the process is a container that holds this thread.

In Linux, the basic execution unit is the process. The functionalities offered by Windows APIs can be mapped directly to Linux system calls:

Table 1. Process mapping


Windows Linux Classification
TerminateProcess() kill() Mappable
GetCurrentProcessID() getpid() Mappable
Exitprocess() exit() Mappable
Using Sys V semaphores, Waitforsingleobject/multipleobject
can be implemented
Context specific

The Classification column (which explains classification constructs used in this article) indicates whether the Windows construct is mappable or context specific:

  • If mappable, the Windows construct can be mapped to the specified Linux construct(s) by closely examining the types, parameters, return codes, and such. Both the Windows and Linux constructs provide similar functionality.
  • If context specific, the given Windows construct may or may not have an equivalent construct in Linux, or Linux may have more than one construct that provides similar functionality. In either case, the decision to use a specific Linux construct(s) depends on the application context.

Creating a process

In Windows, you can use CreateProcess() to create a new process. The CreateProcess() function creates a new process and its main thread as follows:

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 determines whether the handles have to be inherited to the child from the parent. lpApplicationName and lpCommandLine give the name and path of the process to be started. lpEnvironment defines the environment that has to be visible for the process.

In Linux, the exec* family of functions replace the current process image with a new process image (as shown in the following):

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[]);


These versions of exec* are just various calling interfaces for core function execve(): int execve(const char *filename, char *const argv [], char *const envp[]). Here argv is the pointer containing arguments list and envp is the pointer containing list of environment variables which are basically key=value pairs.

This must be used along with the fork() command so both the parent and child processes are running: pid_t fork(void). fork() creates a child process that differs from the parent process only in its PID and PPID; in fact, the resource utilizations are set to 0.

By default, the exec() function inherits the group and user IDs from the parent process, which makes it dependent on the parent process. This can be changed by:

  • Setting the set-uid and set-gid bit on the program file pointed
  • Using the setpgid() and setuid() system call

The CreateProcessAsUser() function is similar to CreateProcess() except that the new process runs in the security context of the user represented by the hToken parameter. There is no one-to-one equivalent for this function in Linux, but it can be replicated using the following logic:

  • fork() to create a new child process with new PID
  • setuid() to switch to the new PID
  • exec() to change the existing process image with the process to execute

Terminating a process

To forcibly terminate a running process, you can use TerminateProcess() in Windows.

BOOL TerminateProcess(
HANDLE hProcess, // handle to the process
UINT uExitCode   // exit code for the process


This function terminates the running process and all the associated threads. Use this function only in extreme scenarios.

In Linux, you can use kill() to forcibly kill a process: int kill(pid_t pid, int sig). This system call terminates the process of id PID. You can also use it to signal to any group or process.

Using wait functions

In cases when the child process is dependent on the parent process, you can use wait functions in parent process to wait for the child process termination. In Windows, you can use the WaitForSingleObject() function call to achieve this.

You can use the WaitForMultipleObject() function to wait for more than one object.

DWORD WaitForMultipleObjects(
DWORD nCount,             // number of handles in array
CONST HANDLE *lpHandles,  // object-handle array
BOOL bWaitAll,            // wait option
DWORD dwMilliseconds      // time-out interval


You can populate the object-handle array with the number of objects to wait for. Based on the bWaitALL option, you can either wait for all the objects to be signaled or wait for any of them to be signaled.

In both of these functions, if you want to wait for a finite time, you can specify the time interval in the second parameter. If you want to wait infinitely, use INFINITE as the value for dwMilliseconds. Setting dwMilliseconds to 0 will just test the state of the object and return.

You can use waitpid() in Linux if you want to just wait infinitely for the process to die. In Linux, there is no way to do a timed wait on a waitpid() call.

In this code: pid_t waitpid(pid_t pid, int *status, int options), waitpid() infinitely waits for the child process to terminate. Wait functions, in both Windows and Linux, suspend the execution of the current process until it completes, but in Windows there is an option to exit by specifying a time value. You can implement a timed wait or NO WAIT functionality similar to WaitForSingleObject() and WaitForMultipleObject() using System V semaphores, which is discussed in Part 2 of this series. Part 3 of this series further discusses wait functions.

Exiting a process

Exiting a process means a graceful exiting of the process with a proper cleanup. In Windows, you use ExitProcess() to perform this operation.

VOID ExitProcess(
UINT uExitCode   // exit code for all threads


ExitProcess() is the preferred method of ending a process. This function provides a clean process shutdown. This includes calling the entry-point function of all attached dynamic-link libraries (DLLs) with a value indicating that the process is detaching from the DLL.

The Linux equivalent for ExitProcess() is exit(): void exit(int status);.

The exit() function causes normal program termination and the value of status &0377 is returned to the parent. The C standard specifies two definitions (EXIT_SUCCESS and EXIT_FAILURE) that can be passed to the status parameter to indicate successful or unsuccessful termination.

Environment variables

Each process has an environment block associated with it, basically name=value pairs that specify various environments the process can access. Even though we can specify the environment when we create the process, there are also specific functions to set and obtain environment variables after the process is created.

In Windows, you can use GetEnvironmentVariable() and SetEnvironmentVariable() to get and set the environment variables.

DWORD GetEnvironmentVariable(
LPCTSTR lpName,  // environment variable name
LPTSTR lpBuffer, // buffer for variable value
DWORD nSize      // size of buffer


This function returns the size of the value buffer on success and 0 if the name specified is not a valid environment variable name. The SetEnvironmentVariable() function sets the contents of the specified environment variable for the current process.

BOOL SetEnvironmentVariable(
LPCTSTR lpName,  // environment variable name
LPCTSTR lpValue  // new value for variable


If the function succeeds, the return value is non-zero. If the function fails, the return value is zero.

In Linux, getenv() and setenv() system calls provide the equivalent functionality.

char *getenv(const char *name);
int setenv(const char *name, const char *value, int overwrite);


The getenv() function searches the environment list for a string that matches the string pointed to by name. This function returns a pointer to the value in the environment or NULL if there is no match. The setenv() function adds the variable name to the environment with the value if the name does not already exist. If the name does exist in the environment, then its value is changed to value if overwrite is non-zero. If overwrite is zero, then the value of name is not changed. The setenv() function returns zero on success or -1 if there was insufficient space in the environment.


The following examples illustrate what we’ve discussed in this section.

Listing 1. Windows process code


//Sample Application that explain process concepts
//Parameters Declaration/Definition
int TimetoWait;
LPTSTR lpszCurrValue,LPTSTR lpszVariable;
BOOL fSuccess;
if(argc > 2)
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)
//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)
WaitForSingleObject( pi.hProcess, INFINITE );
ExitProcess(0); //Success


Listing 2. Equivalent Linux process code


#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 !!");
//Get and display an  environment variable PATH
EnvValue = getenv("PATH");
if(EnvValue == NULL)
printf("Invalid environment variable passed as param !!");
lpszVariable = EnvValue;
while (*lpszVariable)
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.
case -1:
printf("Fork() function failed !!");
ret = -1;
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)
// infinitely waits for child process to die
//Note RC will have PID returned since this is parent process.



In Windows, the thread is the basic unit of execution. One or more threads run in the context of the process. The scheduling code is implemented in the kernel. There is no single “scheduler” module or routine.

The Linux kernel uses a process model rather than a threading model. The Linux kernel provides a lightweight process framework for creating threads; the actual thread implementation is in the user space. There are various threading libraries available (LinuxThreads, NGPT, NPTL, and so on) in Linux. The information in this article is based on the LinuxThreads library, but the information here is also applicable to Red Hat’s Native POSIX Threading Library (NPTL).

This section describes threading in Windows and in Linux. It covers the calls for creating a thread, setting its attributes, and changing its priority.

Table 2. Thread mapping


Windows Linux Classification
CreateThread pthread_create
ThreadExit pthread_exit Mappable
WaitForSingleObject pthread_join


Context Specific

Creating a thread

In Windows, you can use CreateThread() to create a thread to execute under the virtual address space of the calling process.

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 is a pointer to the thread attributes that determines whether the thread handle can be inherited by the child process.

Linux uses the pthread library call pthread_create() to spawn a thread:

int pthread_create (pthread_t *thread_id, pthread_attr_t *threadAttr,
void * (*start_address)(void *), void * arg);


Note: In Windows, the number of threads a process can create is limited by the available virtual memory. By default, every thread has one megabyte of stack space. Therefore, you can create at most 2,028 threads. If you reduce the default stack size, you can create more threads. In Linux, the maximum number of process per user can be found using ULIMIT -a (limits for all users), and you can update it by using ULIMIT -u, but it would be valid only for that logon. The header files under /usr/Include/limit.h and ulimit.h define these constants. You can modify them and recompile kernel to hv permanent effect. For POSIX threadlimits, the THREAD_THREADS_MAX macro defines the maximum limit and is defined in local_lim.h.

Specifying the thread function

The parameter lpStartAddress in the CreateThread() is the address of the function that the newly created thread will execute.

The parameter start_address for the Linux library call pthread_create() is the address of the function that the newly created thread will execute.

Parameter passing to the thread function

In Windows, the parameter lpParameter for the system call CreateThread() specifies the parameter to be passed to the newly created thread. It specifies the address of the data item to be passed to the new thread.

In Linux, the parameter arg for the library call pthread_create() specifies the parameter to be passed to the new thread.

Setting the stack size

In Windows, the parameter dwStackSize for the CreateThread() is the size of stack in bytes that is to be allocated for the new thread. The stack size should be a non-zero multiple of 4 KB and a minimum of 8 KB.

In Linux, the stack size is set in the pthread attributes object; that is, the parameter threadAttr of type pthread_attr_t is passed to the library call pthread_create(). This object needs to be initialized by the call pthread_attr_init() before any attributes are set. The attribute object is destroyed using the call pthread_attr_destroy():

int pthread_attr_init(pthread_attr_t *threadAttr);
int pthread_attr_destroy(pthread_attr_t *threadAttr);


Note that all of the pthread_attr_setxxxx calls achieve similar functionality to the pthread_xxxx calls (if available) except that you can use pthread_attr_xxxx only before thread creation to update the attribute object that will be passed as a parameter to pthread_create. Meanwhile, you can use pthread_xxxx calls at any time after the thread has been created.

The stack size is set using the call pthread_attr_setstacksize(): int pthread_attr_setstacksize(pthread_attr_t *threadAttr, int stack_size);.

Exiting a thread

In Windows, the system call ExitThread() terminates the thread. The dwExitCode is the return value of the thread, and it can be retrieved from another thread by calling GetExitCodeThread().

VOID ExitThread(
DWORD dwExitCode   // exit code for this thread


The Linux equivalent for this is the library call pthread_exit(). The retval is the return value of the thread, and you can retrieve it from another thread by calling pthread_join(): int pthread_exit(void* retval);.

Thread states

In Windows, there are no explicit thread states maintained with respect to thread termination. However, WaitForSingleObject() allows a thread to wait explicitly on the termination of a specific or non-specific thread within the process.

In Linux, threads are by default created in joinable state. In joinable state, another thread can synchronize on the thread’s termination and recover its termination code using the function pthread_join(). The thread resources of the joinable thread are released only after it is joined.

Windows uses WaitForSingleObject() to wait for a thread to terminate:

DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds



  • hHandle is the pointer to the thread handle.
  • dwMilliseconds is the time out value in milliseconds. If the value is set to INFINITE, then it blocks the calling thread/process indefinitely.

Linux uses pthread_join() to do the same: int pthread_join(pthread_t *thread, void **thread_return);.

In the detached state, the thread resources are immediately freed when it terminates. The detached state can be set by calling pthread_attr_setdetachstate() on the thread attribute object: int pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate);. A thread created in a joinable state can later be put into a detached state using the pthread_detach() call: int pthread_detach (pthread_t id);.

Changing priority

In Windows, the priority of the thread is determined by the priority class of its process and the priority level of the thread within the priority class of the process. In Linux, the thread itself is the unit of execution and has its own priority. It has no dependency on the priority of its process.

In Windows, you can use SetPriorityClass() to set the priority class for the specified process:

BOOL SetPriorityClass(
HANDLE hProcess,         // handle to the process
DWORD dwPriorityClass    // Priority class


dwPriorityClass is the priority class of the process, and it is set to any of the following values:


Once the priority class of the process is set, SetThreadPriority() is used to set the priority level of the thread within the priority class of the process:

BOOL SetThreadPriority(
HANDLE hThread,
int nPriority


nPriority is the priority value of the thread, and it is set to one of the following values:

  • THREAD_PRIORITY_ABOVE_NORMAL sets the priority to 1 point above the priority class.
  • THREAD_PRIORITY_BELOW_NORMAL sets the priority to 1 point below the priority class.
  • THREAD_PRIORITY_HIGHEST sets the priority to 2 points above the priority class.
  • THREAD_PRIORITY_LOWEST sets the priority to 2 points below the priority class.
  • THREAD_PRIORITY_NORMAL sets to normal priority for the priority class.



Examples of processes and threads

To wrap up this installment, let’s look at some examples of the following types of processes and threads:

  • Normal or regular processes and threads
  • Time-critical and real-time processes and threads

Normal or regular processes/threads

The Linux system call setpriority() is used to set or modify priority levels for normal processes and threads. The parameter scope is PRIO_PROCESS. Set id to 0 to change the current process (or thread) priority. Again, delta is the priority value — this time in the range -20 to 20. Note also that in Linux, a lower delta value means a higher priority. So you set +20 for IDLETIME priority and 0 for REGULAR priority.

In Windows, the priority range is from 1 (lower priority) to 15 (higher priority) for the regular threads. But in Linux, the priority range for normal non-real-time processes is from -20 (higher) to +20 (lower priority). This has to be mapped before being used: int setpriority(int scope, int id, int delta);.

Time-critical and real-time processes and threads

You can use the Linux system call sched_setscheduler() to change the scheduling priority and policy of a running process: int sched_setscheduler(pit_t pid, int policy, const struct sched_param *param);.

The parameter policy is the scheduling policy. The possible values for policy are SCHED_OTHER (for regular non-real-time scheduling), SCHED_RR (real-time round-robin policy), and SCHED_FIFO (real-time FIFO policy).

Here, param is a pointer to a structure representing scheduling priority. It can range from 1 to 99 only for real-time policies. For others (normal non-real-time processes), it is zero.

In Linux, for a known scheduling policy, it is also possible to change only the process priority by using the system call sched_setparam: int sched_setparam(pit_t pid, const struct sched_param *param);.

The LinuxThreads library call pthread_setschedparam is the thread version of sched_setscheduler and is used to dynamically change the scheduling priority and policy for a running thread: int pthread_setschedparam(pthread_t target_thread, int policy, const struct sched_param *param);.

The parameter target_thread indicates the thread whose priority is to be changed; param indicates the priority.

The LinuxThreads library calls pthread_attr_setschedpolicy, and you can use pthread_attr_setschedparam to set the scheduling policy and the priority level to the thread attribute object before the thread is created:

int pthread_attr_setschedpolicy(pthread attr_t *threadAttr, int policy);
int pthread_attr_setschedparam(pthread attr_t *threadAttr, const struct sched_param *param);


In Windows, the priority range is from 16 (lower priority) to 31 (higher priority) for the real-time threads. In Linux, the priority range for real-time threads is from 99 (higher) to 1 (lower priority). This has to be mapped before being used.


The following listings illustrate the concepts in this section.

Listing 3. Windows thread example


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
// 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
// perform job at high priority
// Reset the priority class as "Normal"
SetPriorityClass(pHandle, NORMAL_PRIORITY_CLASS);
// set the priority back to normal
// Exit thread
// 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
// do time critical work
// Reset the priority class as "Normal"
SetPriorityClass(pHandle, NORMAL_PRIORITY_CLASS);
// Reset the priority back to normal


Listing 4. Equivalent thread code in 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
// 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
// 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);
// 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);


This site uses Akismet to reduce spam. Learn how your comment data is processed.