很多的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");
沒有留言:
張貼留言