Kernel Threads
No post on this blog for a long time, but now will try to keep putting something useful (hopefully) :)
As a start just putting some basic starting material for Linux kernel threads which is incomplete :) and in a horrible shape, but yet...
Getting Started with KERNEL THREADS
1. Introduction
First of all Kernel threads are not to be confused with processes or threads created by processes. Kernel threads are a mechanism used by the Linux kernel to perform some of the important tasks in background. Some examples of kernel threads are: pdflush, kblockd etc.
The significant difference between kernel threads and normal processes is that kernel threads do not have an address space (the mm pointer is NULL). A kernel thead executes in kernel-space only and do not context switch into user space. Kernel threads are, however, schedulable and preemptable as normal process. When a kernel thread is created it is assigned a new task_struct.
2. Difference between a kernel thread and a kernel module:
Many newbie assume that a kernel thread is same as a kernel module. This is wrong perception.
• A kernel module is a piece of code placed in the kernel address space. This is similar to shared libraries. The module can contain data and code as you wish. Being in shared memory the module and it's code can be accessed by any thread in kernel mode. When the module is loaded a special initialization function will be called and should quickly return success or failure. Before the module is unloaded an optional cleanup function is called, this must do whatever cleanup needs to be done.
• A kernel thread is a process without an user address space. When a process is in kernel mode there is little difference, they all share the same kernel address space. If a module has registered functions in the kernel, they can be called by any process and even by multiple processes at the same time.
3. The mm_struct and Kernel Threads
Kernel Threads do not have a process address space and, therefore, do not have an associated memory descriptor. Thus, the mm field of a kernel thread’s process descriptor is NULL. Basically, this is the definition of a kernel thread-they have no user context.
This is fine, because kernel threads do not ever access any user-space memory (whose would they access?). Because kernel threads do not have any pages in user-space, they not really deserve their own memory descriptor and page tables. Despite this, kernel threads need some of the data, such as the page tables, even to access kernel memory. To provide kernel threads the needed data, without wasting memory on a memory descriptor and page tables, or wasting processor cycles to switch to a new address space whenever a kernel thread begins running, kernel threads use the memory descriptor of whatever task ran previously.
Whenever a process is scheduled, the process address space referenced by their mm field is loaded. The active_mm field in the process descriptor is then updated to refer to the new address space. Kernel threads do not have an address space and mm is NULL. Therefore, when a kernel thread is scheduled, the kernel notices that mm is NULL and keeps the previous process’s address space loaded. The kernel then updates the active_mm field of the kernel thread’s process descriptor to refer to the previous process’s memory descriptor. The kernel thread can then use the previous process’s page tables as needed. Because kernel threads do not access user-space memory, they only make use of the information in the address space pertaining to kernel memory, which is the same for all process.
4. Creation of a Kernel Thread
A new kernel thread can be spawned using this interface:
int kernel_thread (int (*fn) (void *) arg, unsigned long flags)
The initial function usually implements a loop in which the kernel thread wakes up as needed, performs the required task and sleeps again.
This will create a new kernel thread; the return value from kernel_thread is interpreted in the same way as the return value from sys_fork. That is a positive number is the pid of the created thread, and a negative number indicates an error condition. A return value of zero should be impossible, because the child terminates after calling fn and does not return to the caller of kernel_thread. The action performed by the new thread is equivalent to the statement exit(fn(arg));. The arg parameter is often used to pass a pointer to a struct to the newly created thread, take care to ensure that the struct still exist when the thread needs it. If the struct is a local variable the thread calling kernel_thread could easily have returned before the struct has been read. If arg is not needed it is usually just filled with the value NULL.
5. Example Kernel Thread
#include
#include
#include
#include
int my_thread(void *p){
printk("hello thread\n");
daemonize ("my_thread");
siginitset (¤t->blocked, sigmask(SIGKILL));
while (1){
set_current_state (TASK_INTERRUPTIBLE);
schedule_timeout (5*HZ);
printk ("inside thread: %d\n", current->pid);
};
return 0;
}
int init_module()
{
kernel_thread((int (*)(void *))my_thread,NULL,0);
return 0;
}
void cleanup_module ()
{
kill_proc (current->pid, SIGKILL, 1);
}
MODULE_LICENSE("GPL");
PENDING
Explanation of “daemonize” and (reparent_to_init)
Explanation of “kernel_mode_helper”
References
Linux Kernel Development by Robert Love
http://www.scs.ch/~frey/linux/kernelthreads.html
http://www.brics.dk/~kasperd/comp.os.linux.development.faq.html
