# 第3章: コンテキストスイッチの実装 タスクの実行状態を保存・復元する仕組みを PendSV 例外の上に実装します。 ## サンプルコード - ディレクトリ: `docs/os-on-arduino/code/03_context_switch` - ビルドと書き込み: ```bash uv run pio run -d 03_context_switch -t upload uv run pio device monitor -b 115200 ``` コードを見る: [docs/os-on-arduino/code/03_context_switch](https://github.com/iory/learning-os-from-arduino/tree/main/docs/os-on-arduino/code/03_context_switch) ## ソースコード この章のスケッチです。ファイル名をクリックすると開きます。コードは原稿リポジトリから自動で同期されているので、ここに出ているものが常に最新です。 :::{dropdown} `src/main.cpp` — 176 行 :icon: code [GitHub で開く](https://github.com/iory/learning-os-from-arduino/blob/main/docs/os-on-arduino/code/03_context_switch/src/main.cpp) ```{literalinclude} ../../../../code/03_context_switch/src/main.cpp :language: cpp :linenos: ``` ::: :::{dropdown} `include/os_kernel.h` — 66 行 :icon: code [GitHub で開く](https://github.com/iory/learning-os-from-arduino/blob/main/docs/os-on-arduino/code/03_context_switch/include/os_kernel.h) ```{literalinclude} ../../../../code/03_context_switch/include/os_kernel.h :language: cpp :linenos: ``` ::: :::{dropdown} `src/os_kernel.cpp` — 200 行 :icon: code [GitHub で開く](https://github.com/iory/learning-os-from-arduino/blob/main/docs/os-on-arduino/code/03_context_switch/src/os_kernel.cpp) ```{literalinclude} ../../../../code/03_context_switch/src/os_kernel.cpp :language: cpp :linenos: ``` ::: ## 実機での出力 Arduino UNO R4 WiFi の実機で実際に取得した出力です(macOS / Windows の 両方で同じ結果を確認しています)。手元の出力と見比べてください。 `os_yield()` で 3 つのタスクが切り替わります。 ```{figure} ../_static/ch03_yield.gif :name: fig-ch03-yield :width: 100% `Task1`(登録名 LED1)→ `Task2`(LED2)→ `Task3`(Serial)と譲り合いながら回り続けるところ。 1 秒あたり 8 周ほどしているので、かなりの速さで流れます ``` ```{figure} ../_static/ch03_timing.svg :name: fig-ch03-timing :width: 100% 3 つのタスクが CPU を使っている時間と、LED が点いている時間(実機で計測した 2 周ぶん)。 タスクは `os_yield()` で順に CPU を譲り合い、1 周は約 117 ms です。そのうち L LED が 点いているのは約 22 ms、マトリクス左半分は約 12 ms だけなので、動画やシミュレータの GIF では点滅には見えません。(各タスクの中で `micros()` の時刻を記録して計測。計測用に 書き足したコードは本書のサンプルには含まれません) ``` ```text === Context Switch Demo === This demonstrates cooperative context switching. Each task must call os_yield() to switch to another task. Initializing OS... OS initialized. TCB size: 36 sp offset: 0 stack_base offset: 4 state offset: 12 name offset: 15 entry offset: 32 Creating tasks... Tasks created: - idle (ID 0) - LED1 (ID 1) - LED2 (ID 2) - Serial (ID 3) Task count: 4 Task stack pointers: Task 0 sp: 0x20000260 ... ``` `TCB`(Task Control Block:タスクごとの管理情報)の大きさとオフセットを表示しているのは、 PendSV のアセンブリが TCB の配列を `TCB size` バイトずつ進んでたどり、各 TCB の 先頭(オフセット 0)にある `sp` を読み書きするからです。コンパイラの設定で構造体の 大きさや並びが変わっても、アセンブリの前提とずれていないかをここで確かめられます。 ## つまずきやすいポイント (読者からの質問に応じて随時追記します。質問は [GitHub Issues](https://github.com/iory/build-your-own-arduino-rtos/issues) へどうぞ)