This first example is not rt-specific, it only should give a framework of a kthread, this module declares a kernel function exec_cmd that is local to this module, a kernel thread is initiated passing this function as the routine to execute and a string via the arg pointer. The call to kernel_thread() initializes a task structure that is visible from user space (the pid of the process is printk'ed) and the thread routine (exec_cmd) is executed once. As we did not set up a specific context for this thread it runs in the inherited context of insmod and thus prints to the current console via the echo command. The thread routine is comparable to a regular user-space function that would call execve except for the privileges and the enabling of the kernels data section to store command arguments in set_fs(KERNEL_DS). This also shows one clear danger of kernel threads - if they are not set up carefully with respect to privileges they can result in a serious security problem - for details on this give the kmod kernel_thread implementation in kernel/kmod.c a look.
What we need specifically for kernel_threads:
#deine __KERNEL__ #deine __KERNEL__SYSCALLS__ #include <linux/unistd.h> #include <asm/unistd.h>
Now on to the actual code for a kernel_thread - as usual... we start with a `Hello World` . But doing this from kernel space is not quite as simple. The setup we are going to use to write to your current console is to invoke /bin/echo and let it print the infamous string, to be able to use /bin/echo we must set up a minimum environment first.
int errno;
char cmd_path[256] = "/bin/echo";
static int
exec_cmd(void * kthread_arg)
{
struct task_struct *curtask = current;
To set up a minimum environment we need to fill out at least TERM and PATH but note that we still inherit the environment of who ever launched insmod of this module ! sounds dangerous ? - it is !
static char * envp[] = {
"HOME=/root ",
"TERM=linux ",
"PATH=/bin",
NULL };
char *argv[] = {
cmd_path,
kthread_arg,
NULL };
int ret;
Give the kthread all effective privileges and allow it to use the kernels data segment KERNEL_DS to store the arguments to execv.
curtask->euid = curtask->fsuid = 0; curtask->egid = curtask->fsgid = 0; cap_set_full(curtask->cap_effective); set_fs(KERNEL_DS);
Now we only need to call execve, which will not return unless it fails, so on success the kernel_thread terminated, on failure we printk and terminate it our selves with return.
printk("calling execve for %s \n",
cmd_path);
ret = execve(cmd_path, argv, envp);
/* if we get here - execve failed */
printk(KERN_ERR "%s failed (%d)\n",
cmd_path,
ret);
return -1;
}
The creation of the kernel_thread is done in init_module, and we printk the PID of the created kernel_thread so we could actually check it with the ps tools to see that it is running (due to the short life-time of our `hello world` thread though, you will hardly be able to see it...). Note that we use pid as return value on failure - init_module will terminate and dealloc'ed any resources allocated automatically by the kernel if init_module returns with anything else but 0 (but the kernel will not free any resources you explicitly requested before init_module failed - those are your job to free...).
int
init_module(void)
{
pid_t pid;
char kthread_arg[]="Hello World !";
pid = kernel_thread(
exec_cmd,
(void*) kthread_arg,
0);
if (pid < 0) {
printk(KERN_ERR \
"fork failed, errno %d\n",
-pid);
return pid;
}
printk("fork ok, pid %d\n",
pid);
return 0;
}
There is nothing to be done in cleanup_module, we did not allocate any resources that will not be freed automatically - note that we don't explicitly reclaim the kernel_thread in any way, the call to execve or, on failure, the return, terminated it and freed any resources associated with this process.