部内向けスキルアップ研修 組込み OS 自作入門 2013 年 12 月 8th ステップ担当 : 松元
本日の内容 スレッドを実装します スレッドとは? OS とは? もくもく会 スレッドで動作するコマンド応答プログラム 必要に応じてプログラムの説明
たとえばこんな動作をさせる 以下の機能を持つ 0.1 秒ごとにLED 点滅 1 秒ごとに液晶パネルに時刻表示 シリアルからのコマンドに応答 一定時間ごとにサービスを実行できるかチェックする ( ビジー ループ ) 8.1.1 OS はなぜ必要なのか?
メイン ループ 0.1 秒経過? LED を点滅 1 秒経過? 液晶パネルに時刻表示 シリアル受信? ダンプ出力を追加 入力コマンドを実行 8.1.1 OS はなぜ必要なのか?
問題点 ダンプするデータが大きい場合 処理に時間がかかる LED 点滅 時計表示がとまってしまう 一定回数ごとにメイン ループに戻る 8.1.1 OS はなぜ必要なのか?
メイン ループより呼び出し ダンプ処理 ダンプ処理中? 前回の状態を復旧して再開 データ分繰り返す データ出力 一定回数? データがなくなるまで メイン ループへ 状態を保存 メイン ループへ
dump 関数 定期的にメイン ループに戻る 戻る前に実行中の状態を保存 関数の先頭で 前回の中断状態からの再開か調べる 再開の場合 保存された状態を復旧 8.1.1 OS はなぜ必要なのか?
まとめると 時間のかかる処理 回数が特定できないループ処理 では 中断 再開処理が必要 処理が複雑化してバグが発生しやすい 8.1.1 OS はなぜ必要なのか?
やりたいこと 定期的な処理の切り替え 処理状態の中断 保存 再開 サービス単位に区切る ( タスク ) 処理を共通化 スレッド化 8.1.2 タスクとスレッド
スレッドとは タスクごとに独立して動作させる 並行処理 CPU の処理時間を非常に短い単位に分割し 複数のスレッドに順番に割り当てることによって 複数の処理を同時に行っているようにみせる 8.1.2 タスクとスレッド
用語 システム コール OS に対するサービス要求 スケジューリング 次に動作すべきスレッドを選択 ディスパッチ 選択されたスレッドの処理再開 8.1.2 タスクとスレッド
ディスパッチ 中断時に処理状態を保存 スタックはスレッドごとに持つ 再開時にスタック ポインタを再開するスレッドのスタックのものに切り替える 8.1.2 タスクとスレッド
用語 コンテキスト / コンテキスト情報 スレッドの処理中断時に保存が必要な CPU の状態 スタック ポインタ プログラム カウンタなどの各種レジスタ値 コンテキスト切替え / コンテキスト チェンジ スレッドのディスパッチのために そのスレッドのコンテキストを読み込むこと 8.1.2 タスクとスレッド
用語 アプリケーション プログラム OS 上でタスクとして独立して動作するプログラム コア / カーネル OS の中核 基本プログラム 8.1.3 アプリケーション プログラムとは何か?
OS/ 割込み / スレッド OS は割込みの延長で動作し スレッドを管理する 1. 割込みハンドラからスレッドのコンテキスト保存 2. スケジューリング 3. スレッドのコンテキストを復旧 ( ディスパッチ ) 8.1.4 スレッドの切替え契機
システム コール OS は資源を管理するための機能を持つ OS は割込みの延長 スレッドとは切り離されている スレッド側から明示的に割込みを発生 システム コール命令を実行 システム コール割込みが発生 システム コール割込みハンドラで応答 8.1.5 システム コール
もくもく会の前に kz_start() スレッド生成用スレッドを開始 レディー キュー 8.2 OS の実装
レディー キュー スレッドごとにタスク コントロール ブロック (TCB) を生成 タスクの情報を格納 実行可能なスレッドのタスク コントロール ブロックをつなぐ ( レディー キュー ) TCB nextポインタ TCB nextポインタ TCB nextポインタ NULL readyque head tail 各種パラメータ 各種パラメータ 各種パラメータ 8.2 OS の実装
レディー キュー 実行中のスレッド ( カレント スレッド ) の実行に区切りがついたら レディー キューの末尾に追加 ラウンドロビン スケジューリング カレントスレッド TCB nextポインタ TCB nextポインタ TCB nextポインタ NULL NULL 各種パラメータ 各種パラメータ 各種パラメータ readyque head tail 8.2 OS の実装
用語 キュー / 先入れ先出し /FIFO 最初に入ってきたものを最初に処理する構造 キューイング / エンキュー キューへ挿入 デキュー キューからの抜き出し スタック : 後入れ先出し /LIFO 8.2 OS の実装
もくもく会 スレッドで動作するコマンド応答プログラム 教科書 p.290~ のソースコードリストを参照 ブート ローダー OS ld.scr intr.s startup.s を修正 kozos.h kozos.c syscall.h syscall.c test08_1.c を追加 ld.scr startup.s defines.h main.c Makefile を修正 8.2 OS の実装
プログラムの実行 ブートローダー OS make make image make write make コンソールで確認 load kozosを転送 run echo test exit 8.3 プログラムの実行
コードを追ってみよう startup.s より main() が呼ばれる (P.325) kz_start(start_threads, "start", 0x100, 0, NULL); (P.317) 割込みハンドラの登録 setintr(softvec_type_syscall, syscall_intr); setintr(softvec_type_softerr, softerr_intr); current = (kz_thread *)thread_run(func, name, stacksize, argc, argv); dispatch(¤t->context);
割込みハンドラの登録 (P.313) setintr(softvec_type_syscall, syscall_intr); setintr(softvec_type_softerr, softerr_intr); 割込みベクタの登録 softvec_setintr(type, thread_intr);(p.263) SOFTVECS[type] = handler; 割込みハンドラの登録 handlers[type] = handler;
コードを追ってみよう startup.s より main() が呼ばれる (P.325) kz_start(start_threads, "start", 0x100, 0, NULL); (P.317) 割込みハンドラの登録 setintr(softvec_type_syscall, syscall_intr); setintr(softvec_type_softerr, softerr_intr); current = (kz_thread *)thread_run(func, name, stacksize, argc, argv); dispatch(¤t->context);
thread_run() (P.306) タスク コントロール ブロックを設定 thp->init.func = func; スタックを設定 スレッドの戻り先 :thread_end() スレッドの開始位置 :thread_init() 汎用レジスタ thread_init() に渡す引数 (ER0):thp レディー キューに接続
コードを追ってみよう startup.s より main() が呼ばれる (P.325) kz_start(start_threads, "start", 0x100, 0, NULL); (P.317) 割込みハンドラの登録 setintr(softvec_type_syscall, syscall_intr); setintr(softvec_type_softerr, softerr_intr); current = (kz_thread *)thread_run(func, name, stacksize, argc, argv); dispatch(¤t->context);
thread_run() (P.306) タスク コントロール ブロックを設定 thp->init.func = func; start_threads スタックを設定 スレッドの戻り先 :thread_end() スレッドの開始位置 :thread_init() 汎用レジスタ thread_init() に渡す引数 (ER0):thp レディー キューに接続 ER0: 引数 ER1 ER6 開始位置 戻り先
コードを追ってみよう startup.s より main() が呼ばれる (P.325) kz_start(start_threads, "start", 0x100, 0, NULL); (P.317) 割込みハンドラの登録 setintr(softvec_type_syscall, syscall_intr); setintr(softvec_type_softerr, softerr_intr); current = (kz_thread *)thread_run(func, name, stacksize, argc, argv); レディー キューに登録 dispatch(¤t->context); スレッドを実行
スレッドを実行 dispatch() (P.296) 引数 : スレッドのスタック ポインタ (ER0) 汎用レジスタの値を復旧 (ER0~ER6) rte プログラム カウンタとCCRの値を復旧
もう一度 thread_run() (P.306) タスク コントロール ブロックを設定 thp->init.func = func; start_threads スタックを設定 スレッドの戻り先 :thread_end() スレッドの開始位置 :thread_init() 汎用レジスタ thread_init() に渡す引数 (ER0):thp レディー キューに接続 ER0: 引数 ER1 ER6 開始位置 戻り先
スレッドを実行 thread_init() (P.306) thp->init.func(thp->init.argc,thp->init.argv); start_threads thread_end(); start_threads() (P.325) kz_run(test08_1_main, command, 0x100, 0, NULL);
kz_run() (P.322) パラメータを設定 param.un.run.func = func; test08_1_main kz_syscall(kz_syscall_type_run, ¶m); current->syscall.type = type; current->syscall.param = param; asm volatile( trapa #0 ); トラップ割込みを発行 (P.318)
スレッドを実行 thread_init() (P.306) thp->init.func(thp->init.argc,thp->init.argv); start_threads thread_end(); start_threads() (P.325) kz_run(test08_1_main, command, 0x100, 0, NULL);
thread_end() (P.305) kz_exit() (P.323) kz_syscall(kz_syscall_type_exit, NULL); (P.318) current->syscall.type = type; current->syscall.param = param; asm volatile( trapa #0 ); トラップ割込みを発行
トラップ命令による割込み 割込みベクタ
もう一度割込みハンドラの登録 setintr(softvec_type_syscall, syscall_intr); setintr(softvec_type_softerr, softerr_intr); 割込みベクタの登録 softvec_setintr(type, thread_intr); SOFTVECS[type] = handler; 割込みハンドラの登録 handlers[type] = handler;
thread_intr() (P.316) handlers[type](); 割込みハンドラ実行 SOFTVEC_TYPE_SYSCALL syscall_intr SOFTVEC_TYPE_SOFTERR softerr_intr schedule(); dispatch();
syscall_intr() (P.315) syscall_proc(current->syscall.type, current->syscall.param); (P.314) getcurrent(); call_functions(); call_functions(type, p); (P.314) KZ_SYSCALL_TYPE_RUN の場合 thread_run(p->un.run.func,...); KZ_SYSCALL_TYPE_EXITの場合 thread_exit();
もう一度 thread_run() (P.306) タスク コントロール ブロックを設定 thp->init.func = func;??? スタックを設定 スレッドの戻り先 :thread_end() スレッドの開始位置 :thread_init() 汎用レジスタ thread_init() に渡す引数 (ER0):thp レディー キューに接続 ER0: 引数 ER1 ER6 開始位置 戻り先
syscall_intr() (P.315) syscall_proc(current->syscall.type, current->syscall.param); (P.314) getcurrent(); call_functions(); call_functions(type, p); (P.314) KZ_SYSCALL_TYPE_RUN の場合 thread_run(p->un.run.func,...); KZ_SYSCALL_TYPE_EXITの場合 thread_exit();
kz_run() (P.322) パラメータを設定 param.un.run.func = func; test08_1_main kz_syscall(kz_syscall_type_run, ¶m); current->syscall.type = type; current->syscall.param = param; asm volatile( trapa #0 ); トラップ割込みを発行 (P.318)
もう一度 thread_run() (P.306) タスク コントロール ブロックを設定 thp->init.func = func; test08_1_main スタックを設定 スレッドの戻り先 :thread_end() スレッドの開始位置 :thread_init() 汎用レジスタ thread_init() に渡す引数 (ER0):thp レディー キューに接続 ER0: 引数 ER1 ER6 開始位置 戻り先
thread_intr() (P.316) handlers[type](); 割込みハンドラ実行 SOFTVEC_TYPE_SYSCALL syscall_intr SOFTVEC_TYPE_SOFTERR softerr_intr schedule(); スケジューリング dispatch(); スレッドを実行
スレッドを実行 thread_init() (P.306) thp->init.func(thp->init.argc,thp->init.argv); test08_1_main test08_1_main() (P.326) コンソールからのコマンドに応答するアプリケーション プログラム
もうひとつのトラップ割込み
thread_end() (P.305) kz_exit() (P.323) kz_syscall(kz_syscall_type_exit, NULL); (P.318) current->syscall.type = type; current->syscall.param = param; asm volatile( trapa #0 ); トラップ割込みを発行
syscall_intr() (P.315) syscall_proc(current->syscall.type, current->syscall.param); (P.314) call_functions(); call_functions(type, p); (P.314) KZ_SYSCALL_TYPE_RUN の場合 thread_run(p->un.run.func,...); KZ_SYSCALL_TYPE_EXIT の場合 thread_exit(); (P.307) 終了メッセージを表示タスク コントロール ブロックをクリア
まとめ OS の仕組み ( スレッド ) スレッドで動作するコマンド応答プログラムの作成 OS の原型が完成!
次回の開催予定 9th ステップ 優先度スケジューリング 日時 :1 月 15 日 ( 水 )13:00~ 場所 : 技術支援室 担当 : 谷口さん