みかん本輪読会_13

-- Views

August 17, 25

スライド概要

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

みかん本輪読会 13章 マルチタスク(1)

2.

マルチタスクとは? p.310,311 - 複数のプロセスを「同時に」動作させる - 同時に動作してるように見せかけることもある - 並列処理 → 1つのプロセスを分割しそれぞれを別のプロセッサで動作させる - 並行処理 → 複数のプロセスを1つのプロセッサ上で同時に動作させる処理

3.

マルチタスクの例 p.310 - 音楽を再生しつつ、ブラウザで調べ物をしながら、エディタ を開いてプログラミングをする - USB機器やタイマからのイベントを1つずつ処理しつつ、 マウスの移動、キー入力、カーソルの点滅 → どちらも同じマルチタスクだが性質が違う 前者は自己完結型、後者は一極集中型 特に後者では処理に時間がかかると他の処理が遅れる

4.

コンテキスト - 実行に必要なデータや変数をまとめたもの 例) OS上で動くC言語のアプリ - 実行バイナリ - コマンドライン引数 - 環境変数 - スタックメモリ - 各レジスタの値 p.310~311

5.

自己完結型と一極集中型の違い p.311 - 音楽を再生しつつ、ブラウザで調べ物をしながら、エ ディタを開いてプログラミングをする - USB機器やタイマからのイベントを1つずつ処理しつ つ、マウスの移動、キー入力、カーソルの点滅 → 前者はアプリケーションごとにコンテキストを持つ 後者は1つのコンテキストが複数のタスクをこなす → 別々のコンテキストを持つタスクを同時実行したい

6.

コンテキストの切り替えに挑戦 p.311 - コンテキストの切り替えのことをコンテキストスイッチと 呼ぶ - コンテキストの1つはメイン関数を、もう1つはTaskB()関 数を実行するようなコンテキストとする - 2つのコンテキストを交互に切り替えて実行する

7.

詳説コンテキストスイッチ p.311 - CPUはメイン関数を実行中 - RIPレジスタは現在実行中の命令の次の命令を指す → メイン関数内のどこか - RSPレジスタはスタックの先頭を指す → kernel_main_stackのどこか - 汎用レジスタ(RAXなど)を使って次々にメイン関数内の 命令が実行されていく

9.

詳説コンテキストスイッチ p.312 - TaskB()のコンテキストに切り替える処理が必要 - RIPをTaskB()の先頭アドレスに書き換える - TaskB()の2つの引数をRDI、RSIに設定 → これだけだとTaskB()にスイッチした後、メイン関数に 帰ってこれない - PIRの値の保存やRSPの値の復元など必要だが後述

10.

コンテキストスイッチの本質 p.312 - CPUはあくまでもレジスタの値を参照して機械語を1つ ずつ実行しているだけ → RIPが指すメモリを読みRIPを更新、機械語を実行 - 単純なのでレジスタの値をうまく変更すればコンテキス トを切り替えられる → これこそがコンテキストスイッチの本質

11.

コンテキストスイッチの構造 p.313 - リスト13.2はコンテキストを保存するための構造体 - コンテキストを切り替えるときに値の保存と復帰が必要 リスト 13.2 なレジスタをすべて含んでいる - メイン関数内で本来保存 されるべきレジスタ値が 変更されるかもしれない → 呼び出し前後で値の保存

12.

TaskB 関数 p.313 - TaskB関数は一旦起動されると無限ループに入る - 普通の無限ループと違いSwitchContext()を呼ぶ - SwitchContext()はコンテキストを切り替える関数 - task_b_ctx → task_a_ctxに切り替える - レジスタを操作して、コンテキストを切り替える - しかし、C++ではTaskB()を実行中にメイン関数に実行 が移るのは想定していないため処理が必要

13.

TaskB コンテキスト p.314 - TaskBのコンテキスト用に適当なサイズのスタック リスト 13.5 - コンテキスト領域全体を0で初期化 - 設定した値はSwitchCo ntext()の中で利用される - task_b_ctx.ripにはTask Bの先頭アドレスが入る

14.

コンテキストスイッチの実装 p.316~317 - リスト13.7はSwitchContext()のソースコード - この関数の主目的はコンテキストの保存と復帰 - 現在実行中のコンテキストを第2引数(RSI)が指すメモ リ領域に保存し、第1引数が指すメモリ領域からCPU のレジスタ郡を復帰する - CR3、セグメントレジスタ、RBP、RBX、R12~R15を除 く汎用レジスタは保存と復帰が不要

15.

SwitchContext() の構造 p.318~319 - コンテキスト構造体に保存した値は次にそのコンテキス トを復帰するときにレジスタに書き戻される - メイン関数はcall命令によってSwitchContext()を呼び 出す - call命令はスタックに戻り先アドレスを積んでから対象 の関数にジャンプする命令 - RSPが指すスタックにはcall命令の次の命令のアドレス が記録されている

16.

コンテキストスイッチの自動化 p.321 - 2つのタスクがSwitchContext()を互いに呼び合うことで 切り替えを行うことを協調的マルチタスク - しかしSwitchContext()が呼び出されないと破綻 - すべてのアプリにSwitchContext()があれば良いが… - プリエンプティブマルチタスクを実現する - CPUの割り込み処理(通常はタイマ)を使って強制的にタ スクを切り替える

17.

プリエンプティブマルチタスクへの切り替え - TaskB()とメイン関数からSwitchContext()を削除 - 即座に切り替える必要がなくなったのでhlt命令に - タイマは20ms周期で1秒間に50回の切り替え - リスト13.13はタイマによる割り込みハンドラの実装 - SwitchTask()で次に切り替えるべきタスクを決定 - 現在実行中のタスクをcurrent_task変数に格納 - taskaとtaskbでcurrent_taskに残っていない方を実行

18.

タスクを増やす p.326~327 - 今の状態でタスクを増やそうと思うとtask_c_ctxのよ うに増やさないとダメ - これでは不便なので好きなだけ増やせるように - 1つのタスクを表すのにはスタック領域とコンテキスト 構造体で足りそう → Taskクラスを作る - タスクは固有のIDとスタック領域、コンテキスト構造 体を持つ

19.

タスクを増やす p.328 - タスク用のコンテキスト構造に初期値を設定する関数 InitContext()を実装(リスト13.18) - この関数で設定した値はこのタスクが最初に SwithContext()によって起動されたときに読み取り - 複数タスクを管理するTaskManagerクラスの実装 - SwitchTask()メソッドのよって現在実行中のタスクとそ の次のタスクを取得し、次のタスクが持つコンテキスト へと実行を切り替える