概要
カーネル探検(rest_init()からプロセス番号1生成まで)
の続きのメモ。詳細はリンク元を見たほうがよい。
カーネルスレッド生成
kernel_thread()
カーネルスレッドを生成する入口。引数にスレッドの用の値を渡している?なのでプロセスというよりスレッドを生成する。
kernel_thread()で何している
- do_fork()
- task_strcutのコピーを作り実行可能プロセスのリストに登録する。プロセスディスクリプタからポインタしている各資源は親と共有している。
- EAX、SP、IPは子プロセス独自のもの。
- IPはkernel_thread_helperを設定
- カーネルスレッド関数をcallするやつ。カーネルスレッドの処理が終わったらここに戻りdo_exitする。
カーネルスレッド生成する際はexecはしていないので、ユーザーモードにならずカーネルスレッドとして生成される。親と各資源を共有してスタックやIPなどは別の箇所を指しているスレッド。
kernel_init()について
- kernel_thread()で生成されたカーネルスレッド。
- initramfsを使っている場合はramfsの/initを起動させる。
/init
- kernel_init()の中でinitramfsの/initを実行する。
- 親がカーネルスレッドだが特別でユーザプロセスに変身する
/initがユーザプロセスとして生成されるまでの流れ
- run_init_process()
- kernel_init()が呼び出す。このとき大概はinitramfs使用しているので引数には/init
- kernel_execve()
- sys_execve()を呼び出す。
- sys_execve()
- exec系システムコールの入り口にもなっている。
- 実行ファイルをメモリにマッピング
-
カーネルスレッド(kernel_init)から/initプロセスを生成するときにはカーネルスタックのカーネルモードの値をユーザーモードの値に上書きしてユーザープロセスに変身させるみたい
+ ユーザーモードを表す__USER_CS, __USER_DSの値が使用されている。
+ なのでこのsys_execveを使うとユーザーモードのプロセスが生成される。
+ /initプロセス生成はこれを利用して親がカーネルモードプロセスだがユーザーモードプロセスになる
スタックの上書き
- EIP 動的リンカの開始ポイント(共有ライブラリをリンクしているので)
- ESP ユーザーモードのスタックポインタ
- DS __USER_DS
- ES __USER_DS
- SS __USER_DS
- CS __USER_CS
システムコールの割り込み処理から戻るときにCPUがカーネルモードからユーザモードに移行したことを感知する。(CSが変化したのを感知する)なので、最初にシステムコールした時にはEFLAGS、CS、EIPしか積まないが、システムコールから復帰するときに特権の移行が発生するのでユザーモードに上書きされたSS,ESPもpopされる。よってユーザーモードになる。カーネルモードからユーザーモードのプロセスを生成するのはこれだけ???
- CPUがシステムコールの時に自動でスタックにpush/pop
- 特権移行なし
- EFLAGS、CS、EIP
- 特権移行あり
- SS、ESP、EFLAGS、CS、EIP
- 特権移行なし
メモメモ
ユーザープロセス生成
- fork()してexec()する。
- do_fork()
- ユーザーモードのプロセスコピーはipはret_from_fork()に設定?
- ここではプロセスディスクリプタからポインタしている各資源は親と共有している。
- do_fork()
- exec系システムコール
- 親と共有している資源を自分独自のものにする、mmの切り替え。
カーネルスタックの場所
- 親プロセスのtask_structをコピーするときに作成されるthread_info構造体のとこにある
ユーザーモードスタックの場所
- リンカスクリプトに書かれている_stack(メモリ番地を表す)みたいなのがユーザーモードスタックの開始場所。execしてメモリディスクリプタを新規で作成するときにその情報が書かれるはず(start_stackとか)。
特権レベル移行のスタック動作
カーネルモード → カーネルモード
特権の移行なし。割り込み後はそのまま今使用しているカーネルスタックを使用する。
ユーザーモード → カーネルモード
例:ユーザプログラムでシステムコール
- TSSからそのプロセスのカーネルモードのSP、SSが保存されているよう(プロセス生成時、スケジュールされた時にいれてる?)。
- なのでここから各種カーネルモードの値を取り出して設定。そしてそこでカーネルスタックにユーザーモードのレジスタをいれてカーネルモードの動作をする。終わったら、ユーザーモードのレジスタを取り出してユーザーモードに戻る。