kthreadd 初期化処理
今回は、前回に紹介した kthreadd の初期化処理内部について紹介する。
kthreadd は文字どおり、カーネルスレッド処理を行うために常駐するプロセス。
内部、kthread_create_list を監視し、リストにエントリが追加されたら、リストから thread_struct オブジェクトを抽出し、create_kthread() を呼び出す。そして、create_kthread() から kernel_thread() を呼び出し、新しいプロセスが fork される。
新たなカーネルスレッドを生成する場合には、kthread_create() によって行われるが。内部の処理が一風変わっている。以下が、v2.6.24 の kthread_create のコードである。
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[],
...)
{
struct kthread_create_info create;
create.threadfn = threadfn;
create.data = data;
init_completion(&create.started);
init_completion(&create.done);
spin_lock(&kthread_create_lock);
list_add_tail(&create.list, &kthread_create_list);
wake_up_process(kthreadd_task);
spin_unlock(&kthread_create_lock);
wait_for_completion(&create.done);
if (!IS_ERR(create.result)) {
va_list args;
va_start(args, namefmt);
vsnprintf(create.result->comm, sizeof(create.result->comm),
namefmt, args);
va_end(args);
}
return create.result;
}
この内部では、ローカル変数として生成するカーネルスレッドの thread_create_info オブジェクトを生成し、kthread_create_list リストに追加する。直後に kthreadd を起こす。そして、対象のカーネルスレッドが生成されるまで待ち受ける。
この関数内部では、いろいろ面白い事をやっている。まずはローカル変数の共有。
kthread_create() 内部で宣言された kthread_create_info 型オブジェクトの create は、呼び出し元のカーネルのスタック領域に格納される。カーネルスタックは、kthrad_info オブジェクトの中に埋め込まれている。つまり変数 create は、カーネル空間に配置されている。カーネルから見たアドレス空間は単体なので、他のスレッドから直接参照可能となっている。
そのため、kthread_create() で kthread_create_list のグローバルリストに追加した kthread_create_info 型ローカル変数 create を、kthreadd から参照している。
このような共有アクセスをする際に問題になるのが排他制御である。ここでは、セマフォではなく complete という仕組みを使っている。これはずいぶん古い仕組みらしく、v2.4 系から既にあるものらしい。
これについては、古いだけあってたくさんドキュメント [1] [2] [3] があるので、そっちを参照してもらいたい。簡単に言うと、wait_for_completion() で指定した事象が完了するまで、処理を待ち合わせるというもの。そして、その事象は init_completion() で宣言する。
kthread_create() の処理では、kthreadd 側で呼び出した create_kthread() の処理が完了するまで待ち合わせている。
ちなみに、kthreadd もカーネルスレッドであり、プロテクトモードに入った直後に呼び出される start_kernel() から生成される。この時点ではもちろん kthreadd は動作していないので、kthread_create() によるスレッド生成が不可能である。なので start_kernel() では、kthreadd を直接 kernel_thread() を呼び出して生成している。
ならば、kthreadd なんか経由せずにみんな kernel_thread を直接呼び出せばよさそうだが。kthreadd では、新たなプロセスの子プロセスのリストやら、プロセスの属性情報の初期化を行っている。kthreadd が直接 kernel_thread のよって生成されているのは、これ自体は子プロセスの生成等の特殊な処理を行わない為である。
[1] http://www.makelinux.net/ldd3/chp-5-sect-4.shtml (英語だけど、とってもわかりやすい)
[2] http://www.ibm.com/developerworks/jp/linux/library/l-task-killable/
[3] http://sourceforge.jp/projects/linux-kernel-docs/wiki/1.7%E3%80%80%E4%BA%8B%E8%B1%A1%E3%81%AE%E5%BE%85%E3%81%A1%E5%90%88%E3%82%8F%E3%81%9B
- Category(s)
- 学習経過
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/ohyama/kthreadd-521d671f531651e67406/tbping