サーバー

Murax64では、ドライバーやファイルシステム、ネットワークスタック、プロセス管理などのサーバーは、すべてユーザータスクとして実行される。
サーバーは同期呼び出し、非同期呼び出しに対応。サーバーへの割り込みは、非同期呼び出しになる。
また、ユーザータスクから関数として呼び出せるスレッドとして設定できる。

サーバーの作成 kern/x86_64/task.c task_create_server()

ELFファイルから、サーバーの仮想メモリーマップが作成される。
add_schedule_next()でタスクスケージュールに登録し、task_switch_exec_server()で作成関数を呼び出したタスクから タスクスイッチして、サーバー固有の初期化処理を実行する。
初期化処理が終了したら、recieve_message()システムコールを呼び出して、kern/server.c server_recieve_message()を呼び出す。
task_exit_schedule()でタスクスケージュールから削除されて、task_switch_direct()で、作成関数を呼び出したタスクにタスクスイッチして、 呼び出し待機になる。
作成関数を呼び出したタスクは、関数からリターンして終了する。

サーバーの同期呼び出し kern/server.c server_sync_send_message()

システムコールから呼び出されると、まずtask_is_deadlock()で、デッドロックの確認をする。
そして、ビジーフラグsync_busyを獲得するためにmutexロックに入って、もし獲得できなければ、put_fifo()で待ち行列に登録し、 task_sleep_direct()でスリープする。
ビジーフラグを獲得したら、もし引数にバッファがあれば、task_map_source_buf()でバッファが該当する仮想メモリーマップをサーバーの仮想メモリーマップに コピーして、サーバーが直接バッファを読み書きできるようにする。
そして、task_switch_direct()でサーバーにタスクスイッチして、サーバーを実行する。
サーバーは、処理が終了した後、recieve_message()システムコールを呼び出して、呼び出し元タスクにタスクスイッチして、呼び出し待機になる。
呼び出しタスクは、get_fifo()を呼び出して、もし待機中のタスクがあれば、task_wake_direct()またはipi_issue_func()で起床させ、 もし、待機中のタスクがなければ、ビジーフラグを開放して、終了する。

サーバーの非同期呼び出し kern/server.c server_async_send_message()

非同期呼び出しでは、サーバーの状態を3つに分けて処理をしている。

1. 割り込み待ちの場合(if (server[server_id]->wait_intr && id == MSG_INTR))
サーバーは割り込み待ちでスリープしているので、サーバーに渡す引数をセットした後、wait_wake()でサーバーを起こす。

2. サーバーが処理中の場合(else if (server[server_id]->is_server_running || add_only)
サーバーに渡す引数を保存する。

3. サーバーが呼び出し待ちの場合(else
サーバーに渡す引数をセットした後、task_wake_direct()でサーバーにタスクスイッチする。
サーバーを呼び出したタスクは、タスクスケージュールから削除されていないので、スケージュールの順番が来たら、task_wake_direct() から復帰する。

関数として呼び出せるスレッドサーバー kern/x86_64/task.c

1. 仮想メモリーを1Gバイトづつ割り当てる
仮想メモリーマップの、ユーザータスクと重ならない一つのレベル1ページテーブルに、すべてのスレッドサーバーの仮想メモリーが設定されるように、 レベル1ページテーブル境界のアドレスから各サーバーに、1Gバイトづつ仮想メモリーを割り当てる。
具体的には、各サーバーに、5レベルページング時では0xFFFE000000000000から、4レベルページング時では0xFFFFFE8000000000から、 仮想アドレスを1Gバイトづつ割り当てたリンカスクリプトを作成し、コンパイルする。

2. 一つのレベル1ページテーブル上にスレッドサーバーを作成する
task_create_server()の中のvm_set_level1_user_table()で、仮想メモリーマップ上に、server_vmem_tableレベル1ページテーブルを、 5レベルページング時では0xFFFE000000000000、4レベルページング時では0xFFFFFE8000000000にセットし、サーバータスクを作成する。

3. ユーザータスクにスレッドサーバーのレベル1ページテーブルをセットする
最初のユーザータスクを作成する task_create_server()と、ユーザータスクをforkするtask_fork()の中で、 vm_set_level1_user_table()でスレッドサーバーのレベル1ページテーブルをセットする。
これにより、スレッドサーバーを関数として呼び出すことができる。

4. BSS領域とheap領域をスレッドサーバーごとに管理する
ユーザータスクとスレッドサーバーは、同じ仮想メモリー空間に共存するが、BSS領域とheap領域は別になるので、スレッドサーバー独自でそれを管理できなくてはならない。
そのため、BSS領域とheap領域を管理するstruct kthread構造体を、ユーザータスクとスレッドサーバーで別のアドレスに持たせる。
具体的には、ユーザータスクは0xFFFFFFFFFFFFE000に、スレッドサーバーは1Gバイト内の最後の4Kバイトアドレスになる。
ページフォルトが起きた時は、task_page_fault_lock()の中で、get_kthread_vaddr()で、フォルトアドレスが ユーザータスク内なのかスレッドサーバー内なのかを判断して、kthread構造体アドレスを取得して処理をする。
メモリーの割り当てや開放は、task_get_heap_mem()task_release_heap_mem()が、メモリー割当開放ライブラリ関数から呼び出される mmap() munmap()システムコールから呼び出されて、それぞれget_kthread_vaddr_from_syscall()で、 ライブラリ関数のアドレスから判断して、kthread構造体アドレスを取得して処理をする。