- Kernel Thread – Linux Device Driver Tutorial Part 19
- Process
- Threads
- Thread Management
- Types of Thread
- User Level Thread
- Kernel Level Thread
- Kernel Thread Management Functions
- Create Kernel Thread
- kthread_create
- Start Kernel Thread
- wake_up_process
- Stop Kernel Thread
- kthread_stop
- Other functions in Kernel Thread
- kthread_should_stop
- kthread_bind
- Threads Programming in Linux: Examples
Kernel Thread – Linux Device Driver Tutorial Part 19
This article is a continuation of the Series on Linux Device Driver and carries the discussion on Linux device drivers and their implementation. The aim of this series is to provide easy and practical examples that anyone can understand. This is the Kernel Thread in Linux kernel driver – Linux Device Driver Tutorial Part 19.
Process
An executing instance of a program is called a process. Some operating systems use the term ‘task‘ to refer to a program that is being executed. The process is a heavyweight process. The context switch between the process is time-consuming.
Threads
A thread is an independent flow of control that operates within the same address space as other independent flows of control within a process.
One process can have multiple threads, with each thread executing different code concurrently, while sharing data and synchronizing much more easily than cooperating processes. Threads require fewer system resources than processes and can start more quickly. Threads, also known as lightweight processes.
One of the advantages of the thread is that since all the threads within the processes share the same address space, the communication between the threads is far easier and less time-consuming as compared to processes. This approach has one disadvantage also. It leads to several concurrency issues and requires synchronization mechanisms to handle the same.
Thread Management
Whenever we are creating a thread, it has to manage by someone. So that management follows like below.
- A thread is a sequence of instructions.
- CPU can handle one instruction at a time.
- To switch between instructions on parallel threads, the execution state needs to be saved.
- Execution state in its simplest form is a program counter and CPU registers.
- The program counter tells us what instruction to execute next.
- CPU registers hold execution arguments, for example, addition operands.
- This alternation between threads requires management.
- Management includes saving state, restoring state, deciding what thread to pick next.
Types of Thread
There are two types of threads.
User Level Thread
In this type, the kernel is not aware of these threads. Everything is maintained by the user thread library. That thread library contains code for creating and destroying threads, for passing messages and data between threads, for scheduling thread execution, and for saving and restoring thread contexts. So all will be in User Space.
Kernel Level Thread
Kernel level threads are managed by the OS, therefore, thread operations are implemented in the kernel code. There is no thread management code in the application area.
Anyhow each type of thread has advantages and disadvantages too.
Now we will move into Kernel Thread Programming. First, we will see the functions used in a kernel thread.
Kernel Thread Management Functions
There are many functions used in Kernel Thread. We will see each one by one. We can classify those functions based on functionalities.
- Create Kernel Thread
- Start Kernel Thread
- Stop Kernel Thread
- Other functions in Kernel Thread
For use the below functions you should include linux/kthread.h header file.
Create Kernel Thread
kthread_create
This API creates a kthread.
struct task_struct * kthread_create (int (* threadfn(void *data), void *data, const char namefmt[], . );
threadfn – the function to run until signal_pending(current).
data – data ptr for threadfn .
namefmt[] – printf-style name for the thread.
. – variable arguments
This helper function creates and names a kernel thread. But we need to wake up that thread manually. When woken, the thread will run threadfn() with data as its argument.
threadfn can either call do_exit directly if it is a standalone thread for which no one will call kthread_stop , or return when ‘ kthread_should_stop ‘ is true (which means kthread_stop has been called). The return value should be zero or a negative error number; it will be passed to kthread_stop .
It Returns task_struct or ERR_PTR(-ENOMEM) .
Start Kernel Thread
wake_up_process
int wake_up_process (struct task_struct * p);
p – The process to be woken up.
Attempt to wake up the nominated process and move it to the set of runnable processes.
It returns 1 if the process was woken up, 0 if it was already running.
It may be assumed that this function implies a write memory barrier before changing the task state if and only if any tasks are woken up.
Stop Kernel Thread
kthread_stop
int kthread_stop ( struct task_struct *k);
k – thread created by kthread_create .
Sets kthread_should_stop for k to return true , wakes it and waits for it to exit. Your threadfn must not call do_exit itself, if you use this function! This can also be called after kthread_create instead of calling wake_up_process : the thread will exit without calling threadfn .
It Returns the result of threadfn , or –EINTR if wake_up_process was never called.
Other functions in Kernel Thread
kthread_should_stop
should this kthread return now?
int kthread_should_stop (void);
When someone calls kthread_stop on your kthread, it will be woken and this will return true. You should then return, and your return value will be passed through to kthread_stop .
kthread_bind
void kthread_bind (struct task_struct *k, unsigned int cpu);
Threads Programming in Linux: Examples
One of the important purpose of threads is to achieve concurrency. There may be many independent tasks in a program which can be done in parallel without the influence of the other. One of the first step in using threads is to first recognize the fact whether the program needs threads or not, otherwise the very purpose of threads becomes futile. For example if you are designing a program which involves reading a file and display it, you may utilize two or more threads, one to read the file, the second to update the display and other threads to monitor the user inputs in the form of keyboard interrupts or mouse movements. There can be cases when threads are dependent on each other like in case of our above example, if the concerned application is an editor so a character key pressed should be soon informed to the other thread which updates display so that it may open the menu corresponding to that key shortcut or do some other appropriate action.
Example 1:
Two threads displaying two strings “Hello” and “How are you?” independent of each other.
#include #include #include void * thread1() < while(1)< printf("Hello!!\n"); >> void * thread2() < while(1)< printf("How are you?\n"); >> int main()
Now compile this program (Note the -l option is to load the pthread library)
On running, you can see many interleaved “Hello!!” and “How are you?” messages
Example 2
This example involves a reader and a writer thread. The reader thread reads a string from the user and writer thread displays it. This program uses semaphore so as to achieve synchronization
#include #include #include #include char n[1024]; sem_t len; void * read1() < while(1)< printf("Enter a string"); scanf("%s",n); sem_post(&len); >> void * write1() < while(1)< sem_wait(&len); printf("The string entered is :"); printf("==== %s\n",n); >> int main()
On running, in most cases we may be able to achieve a serial read and write( Thread1reads a string and Thread2 displays the same string). But suppose we insert a sleep function() in write1 like
The thread 1 may read one more string and thread2 displays the last read string. That is no serial read and write is achieved.
So we may need to use the condition variables to achieve serial read and write.
Example 3
This example involves a reader and a writer thread. The reader thread reads a string from the user and writer thread displays it. This program uses condition variables to achieve synchronization and achieve serial programming .
#include #include #include #include #define TRUE 1 #define FALSE 0 char n[1024]; pthread_mutex_t lock= PTHREAD_MUTEX_INITIALIZER; int string_read=FALSE; pthread_cond_t cond; void * read1() < while(1)< while(string_read); pthread_mutex_lock(&lock); printf("Enter a string: "); scanf("%s",n); string_read=TRUE; pthread_mutex_unlock(&lock); pthread_cond_signal(&cond); >> void * write1() < while(1)< pthread_mutex_lock(&lock); while(!string_read) pthread_cond_wait(&cond,&lock); printf("The string entered is %s\n",n); string_read=FALSE; pthread_mutex_unlock(&lock); >> int main()
The output is serial read and write.
In the beginning, I started the discussion that threads are used to achieve concurrency. But the above examples can be easily done by simple scanf and printf i.e.,
scanf (“%s”,n);
printf (“%s”,n);
But these examples were given to demonstrate the semaphores and condition variables. Example 3 can be further modified to design a reader/writer application. Example string_read boolean variable can be converted to a string_count variable.