kthread_runマクロ
Rev.3を表示中。最新版はこちら。
kthread_runマクロはカーネルスレッドを作成し、起動(アクティブキューに接続)に繋ぐ。カーネルスレッドはユーザプロセスのマルチスレッドと同じような感じ。異なるのは、ユーザプロセスの親もプロセスとして動作するが、カーネルスレッドの親は、カーネル本体で、このカーネルはプロセスとして動作しているわけでない。lkmで/procのコールバック処理を実装した場合、この関数はカーネル上の1パスの関数にしか過ぎない。当然カーネルスレッドからコールされる事もありうる。従ってその処理が終了しても(return)、イメージとしての実態はカーネル内に存在し続ける。しかしカーネルスレッドだと、カーネルパスの延長上で動作するのでなく、カーネルからスケジュールされるプロセスとして動作する。従って、その処理が終了したらプロセスとして存在しなくなる。ただしrmmodしない限り、イメージとしてメモリ上に存在する。なお、再度必要に応じてkthread_runすることも可能。
従って、lkmから作成したカーネルスレッド、lkmをrmmmodする時に、その前にそのカーネルスレッドを削除する必要がある。この処理を忘れると、実在しないカーネルスレッドに起動がかかるって事になる。(たぶん)
カーネルスレッド内の処理は、サービスの間retできない。上記のごとくretするとプロセスが削除されてしまう。カーネルの実装的には、カーネルスレッドとなる関数の復帰後に、do_exit関数がコールされるようになっている。
以下はlkm下のカーネルスレッドでjiffiesを表示させるサンプル。
#include <linux/module.h> #include <linux/kthread.h> #include <linux/sched.h> #include <linux/jiffies.h> MODULE_AUTHOR("y.kitamura"); MODULE_DESCRIPTION("kthread test"); MODULE_LICENSE("GPL"); static struct task_struct *kthread_tsk; static void my_kthread_main(void) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ); printk("kthread:%ld\n", jiffies); } static int my_kthread(void *arg) { printk("HZ:%d\n", HZ); while (!kthread_should_stop()) { my_kthread_main(); } return 0; } static int __init kthread_test_init(void) { kthread_tsk = kthread_run(my_kthread, NULL, "param %s&%d", "test", 123); if (IS_ERR(kthread_tsk)) return -1; printk("pid->%d:prio->%d:comm->%s\n", kthread_tsk->pid, kthread_tsk->static_prio, kthread_tsk->comm); return 0; } static void __exit kthread_test_exit(void) { kthread_stop(kthread_tsk); } module_init(kthread_test_init); module_exit(kthread_test_exit);実行結果
[root@localhost lkm]# dmesg [ 3991.325662] pid->7712:prio->120:comm->param test&123 [ 3991.326134] HZ:1000 [ 3992.326479] kthread:3692326 [ 3993.326179] kthread:3693326 [ 3994.326339] kthread:3694326 [ 3995.328651] kthread:3695328rmmod時に、kthread_should_stopがコールされる様にする必要がある。kthread_should_stopはkthread->should_stop = 1として、カーネルスレッドを起床させ、wait_for_completionでスレッドが削除されるまでウエイトする。そして後、モジュールを削除する事が可能になる。
int kthread_should_stop(void) { return to_kthread(current)->should_stop; } int kthread_stop(struct task_struct *k) { struct kthread *kthread; int ret; trace_sched_kthread_stop(k); get_task_struct(k); kthread = to_kthread(k); barrier(); /* it might have exited */ if (k->vfork_done != NULL) { kthread->should_stop = 1; wake_up_process(k); wait_for_completion(&kthread->exited); } ret = k->exit_code; put_task_struct(k); trace_sched_kthread_stop_ret(ret); return ret; }
実装は後日に・・・