Related Content: CS604 - VU Lectures, Handouts, PPT Slides, Assignments, Quizzes, Papers & Books of Operating Systems
Support for threads may be provided at either user level for user threads or by kernel for kernel threads.
User threads are supported above kernel and are implemented by a thread library at
the user level. The library provides support for thread creation, scheduling, and
management with no support from the kernel. Since the kernel is unaware of user-level
threads, all thread creation and scheduling are done in the user space without the need for
kernel intervention, and therefore are fast to create and manage. If the kernel is single
threaded, then any user level thread performing a blocking system call will cause the
entire process to block, even if other threads are available to run within the application.
User thread libraries include POSIX Pthreads , Solaris 2 UI-threads, and Mach Cthreads.
Kernel threads are supported directly by the operating system. The kernel performs the scheduling, creation, and management in kernel space; the kernel level threads are hence slower to create and manage, compared to user level threads. However since the kernel is managing threads, if a thread performs a blocking system call, the kernel can schedule another thread in the application for execution. Windows NT, Windowss 2000, Solaris, BeOS and Tru64 UNIX support kernel threads.
There are various models for mapping user-level threads to kernel-level threads. We describe briefly these models, their main characteristics, and examples.
Solaris 2 has threads, lightweight processes (LWPs), and processes, as shown in Figure 13.4. At least one LWP is assigned to every user process to allow a user thread to talk to a kernel thread. User level threads are switched and scheduled among LWPs without kernel’s knowledge. One kernel thread is assigned per LWP. Some kernel threads have no LWP associated with them because these threads are not executed for servicing a request by a user-level thread. Examples of such kernel threads are clock interrupt handler, swapper, and short-term (CPU) shceduler.
Pthreads refers to the POSIX standard defining an API for thread creation, scheduling,
and synchronization. This is a specification for thread behavior not an implementation.
OS designers may implement the specification in any way they wish. Generally, libraries
implementing the Pthreads specification are restricted to UNIX-based systems such as
Solaris 2. In this section, we discuss the Pthreads library calls for creating, joining, and
terminating threads and use these calls to write small multi-threaded C programs.
You can create a threads by using the pthread_create() call. Here is the syntax of this call.
int pthread_create(pthread_t *threadp, const pthread_attr_t *attr,
void* (*routine)(void *), arg *arg);
where, ‘threadp’ contains thread ID (TID) of the thread created by the call, ‘attr’ is used
to modify the thread attributes (stack size, stack address, detached, joinable, priority,
etc.), ‘routine’ is the thread function, and ‘arg’ is any argument we want to pass to the
thread function. The argument does not have to be a simple native type; it can be a
‘struct’ of whatever we want to pass in.
The pthread_create() call fails and returns the corresponding value if any of
the following conditions is detected:
You can do error handling by including the <errno.h> file and incorporating proper error handling code in your programs.
You can have a thread wait for another thread within the same process by using the pthread_join() call. Here is the syntax of this call.
int pthread_join(pthread_t aThread, void **statusp);
where, ‘aThread’ is the thread ID of the thread to wait for and ‘statusp’ gets the return
value of pthread_exit() call made in the process for whom wait is being done.
A thread can only wait for a joinable thread in the same process address space; a
thread cannot wait for a detached thread. Multiple threads can join with a thread but only
one returns successfully; others return with an error that no thread could be found with
the given TID
You can terminate a thread explicitly by either returning from the thread function or by
using the pthread_exit() call. Here is the syntax of the pthread_exit() call.
void pthread_exit(void *valuep);
where, ‘valuep’ is a pointer to the value to be returned to the thread which is waiting for
this thread to terminate (i.e., the thread which has executed pthread_join() for this
thread).
A thread also terminates when the main thread in the process terminates. When a
thread terminates with the exit() system call, it terminates the whole process because the
purpose of the exit() system call is to terminate a process and not a thread.
The following code shows the use of the pthread library calls discussed above. The program creates a thread and waits for it. The child thread displays the following message on the screen and terminates.
Hello, world! ... The threaded version.
As soon as the child thread terminates, the parent comes out of wait, displays the following message and terminates.
Exiting the main function.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
/* Prototype for a function to be passed to our thread */
void* MyThreadFunc(void *arg);
int main()
{
pthread_t aThread;
/* Create a thread and have it run the MyThreadFunction */
pthread_create(&aThread, NULL, MyThreadFunc, NULL);
/* Parent waits for the aThread thread to exit */
pthread_join(aThread, NULL);
printf ("Exiting the main function.\n");
return 0;
}
void* MyThreadFunc(void* arg)
{
printf ("Hello, world! ... The threaded version.\n");
return NULL;
}
The following session shows compilation and execution of the above program. Does the output make sense to you?
$ gcc hello.c –o hello –lpthread –D_REENTRANT
$ hello
Hello, world! ... The threaded version.
Exiting the main function.
$
Note that you need to take the following steps in order to be able to use the pthread library.
Here is another program that uses the pthread library to create multiple threads and
have them display certain messages. Read through the code to understand what it does.
Then compile and run it on your UNIX/Linux system to display output of the program
and to see if you really understood the code.
/**********************************************************************
* FILE: hello_arg2.c
* DESCRIPTION:
* A "hello world" Pthreads program which demonstrates another safe way
* to pass arguments to threads during thread creation. In this case,
* a structure is used to pass multiple arguments.
*
* LAST REVISED: 09/04/02 Blaise Barney
**********************************************************************/
#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 8
char *messages[NUM_THREADS];
struct thread_data
{
int thread_id;
int sum;
char *message;
};
struct thread_data thread_data_array[NUM_THREADS];
void *PrintHello(void *threadarg)
{
int taskid, sum;
char *hello_msg;
struct thread_data *my_data;
sleep(1);
my_data = (struct thread_data *) threadarg;
taskid = my_data->thread_id;
sum = my_data->sum;
hello_msg = my_data->message;
printf("Thread %d: %s Sum=%d\n", taskid, hello_msg, sum);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
int *taskids[NUM_THREADS];
int rc, t, sum;
sum=0;
messages[0] = "English: Hello World!";
messages[1] = "French: Bonjour, le monde!";
messages[2] = "Spanish: Hola al mundo";
messages[3] = "Klingon: Nuq neH!";
messages[4] = "German: Guten Tag, Welt!";
messages[5] = "Russian: Zdravstvytye, mir!";
messages[6] = "Japan: Sekai e konnichiwa!";
messages[7] = "Latin: Orbis, te saluto!";
for(t=0; t<NUM_THREADS; t++) {
sum = sum + t;
thread_data_array[t].thread_id = t;
thread_data_array[t].sum = sum;
thread_data_array[t].message = messages[t];
printf("Creating thread %d\n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *) &thread_data_array[t]);
if (rc) {
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
pthread_exit(NULL);
}
The above code was taken from the following website.
http://www.llnl.gov/computing/tutorials/pthreads/samples/hello_arg2.c