Helping ordinary people create extraordinary websites!
HOME TUTORIALS SCRIPTS WEB HOSTING BLOG FORUM
Get Our Newsletter
Your Email:

Port Windows IPC Apps to Linux, Part 1: Processes and Threads

By Srinivasan S. Muthuswamy, Kavitha Varadarajan
2005-06-16


Threads

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

WindowsLinuxClassification
CreateThreadpthread_create
pthread_attr_init
pthread_attr_setstacksize
pthread_attr_destroy
Mappable
ThreadExitpthread_exitMappable
WaitForSingleObjectpthread_join
pthread_attr_setdetachstate
pthread_detach
Mappable
SetPriorityClass
SetThreadPriority
setpriority
sched_setscheduler
sched_setparam

pthread_setschedparam
pthread_setschedpolicy
pthread_attr_setschedparam
pthread_attr_setschedpolicy
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
);

Where:

  • 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:

  • IDLE_PRIORITY_CLASS
  • BELOW_NORMAL_PRIORITY_CLASS
  • NORMAL_PRIORITY_CLASS
  • ABOVE_NORMAL_PRIORITY_CLASS
  • HIGH_PRIORITY_CLASS
  • REALTIME_PRIORITY_CLASS

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_IDLE sets base priority to 1 for IDLE_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, ABOVE_NORMAL_PRIORITY_CLASS, or HIGH_PRIORITY_CLASS processes, and sets base priority to 16 for REALTIME_PRIORITY_CLASS processes.
  • THREAD_PRIORITY_LOWEST sets the priority to 2 points below the priority class.
  • THREAD_PRIORITY_NORMAL sets to normal priority for the priority class.
  • THREAD_PRIORITY_TIME_CRITICAL sets the base priority to 15 for IDLE_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, ABOVE_NORMAL_PRIORITY_CLASS, or HIGH_PRIORITY_CLASS processes, and sets base priority to 31 for REALTIME_PRIORITY_CLASS.


Tutorial Pages:
» A mapping guide for complex, multithreaded, multiprocess applications
» Processes
» Creating a process
» Terminating a process
» Using wait functions
» Exiting a process
» Environment variables
» Examples
» Threads
» Examples of processes and threads
» Next in the series
» Resources


First published by IBM DeveloperWorks


 | Bookmark
Related Tutorials:
» How to Install PHP 5 on Linux
» How to Install Apache 2 on Linux
» How to Install MySQL 5.0 on Linux
» SMB Caching
» Mound --Bind
» Tar Wild Card Interpretation