2013年8月24日 星期六

Linux Kernel Threads

很多的drivers會透過Kernel threads(簡稱,kthread)的協助,讓kthread在背景執行扮演服務的角色,然後等待events發生。在等待的過程中,kthread會進入sleep狀態,當事件發生的時候,kthread會被喚醒執行一些time-consuming的工作,如此一來,可防止main thread被blocking住。可透過下面的指令來查看系統上有哪些kthreads:
$ ps -ef
透過上面的指令,可以看到下圖PPID為2的都屬於kthread: 底下是一個kthread的範例,透過insmod載入模組後,會啟動mykthread,然後等待events發生:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kthread.h>

static DECLARE_WAIT_QUEUE_HEAD(myevent_waitqueue); /* 初始化wait queue */
EXPORT_SYMBOL(myevent_waitqueue);
int mykthread_pid;

static void run_umode_app(void)
{
 int result;
 char path[] = "/bin/touch";
 char *argv[] = { path, "/renee.txt", NULL };
 char *envp[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
 
 /* 執行一個user space的程式 */
 result = call_usermodehelper(path, argv, envp, UMH_WAIT_PROC); 
 
 if (result == 0) {
  printk("mykthread: successfully executed %s application\n", path);
 } else {
  printk("mykthread: failed to execute %s application\n", path);
 }
}

static int mykthread(void* unused)
{
 DECLARE_WAITQUEUE(wait, current); /* 建立一個wait queue的entry */
 daemonize("mykthread"); /* 將用戶資源釋放掉,讓thread變成真正的kthread,
                            既脫離user space */
 allow_signal(SIGKILL); /* 註冊接收SIGKILL signal */
 add_wait_queue(&myevent_waitqueue, &wait); /* 將kthread加入wait queue,並在
                                               wait queue中sleep等待事件發生 */
 
 for (;;) { 
  printk("mykthread: waiting for events to wake up\n");
  set_current_state(TASK_INTERRUPTIBLE); /* 將kthread狀態為interruptible */
  schedule(); /* 將CPU資源讓出來 */
  if (signal_pending(current)) { /* 如果收到SIGKILL signal,則跳出迴圈結束生命 */
   printk("mykthread: received a SIGKILL signal\n");
   break;
  }
  printk("mykthread: woken up\n");
  run_umode_app(); /* 當kthread被喚醒後,執行一個user space的程式 */
 }
 
 set_current_state(TASK_RUNNING); /* 將kthread狀態為running */
 remove_wait_queue(&myevent_waitqueue, &wait); /* 將kthread從wait queue移除 */
 printk("mykthread: exited\n");
 return 0; 
}

static int __init mykthread_module_init(void)
{   
 mykthread_pid = kernel_thread(mykthread, NULL, CLONE_FS | 
  CLONE_FILES | CLONE_SIGHAND | SIGCHLD); /* 建立一個kthread */
 
 if (mykthread_pid > 0) {
  printk("Created mykthread PID = %d\n", mykthread_pid);
 } else {
  printk("Failed to create mykthread\n");
 }
 
 printk("Mykthread module loaded\n");
 return 0; 
}

static void __exit mykthread_module_exit(void)
{
 if (mykthread_pid) {
  kill_pid(find_vpid(mykthread_pid), SIGKILL, 1); /* 向kthread發出SIGKILL signal */
 }
 
 printk("Mykthread module unloaded\n");
}

module_init(mykthread_module_init);
module_exit(mykthread_module_exit);

MODULE_DESCRIPTION("Mykthread Module");
MODULE_LICENSE("GPL");
MODULE_ALIAS("mykthread module");
MODULE_AUTHOR("Renee's Blog");
用來喚醒mykthread:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/sched.h>

extern wait_queue_head_t myevent_waitqueue;

static int __init trigger_module_init(void)
{
 wake_up_interruptible(&myevent_waitqueue); /* 喚醒kthread */
 printk("trigger: trigged mykthread\n");
 printk("Trigger module loaded\n");
 return 0;
}

static void __exit trigger_module_exit(void)
{
 printk("Trigger module unloaded\n");
}

module_init(trigger_module_init);
module_exit(trigger_module_exit);

MODULE_DESCRIPTION("Mykthread Trigger");
MODULE_LICENSE("GPL");
MODULE_ALIAS("trigger module");
MODULE_AUTHOR("Renee's Blog");

沒有留言:

張貼留言