前回はThread間の通信に便利なMail APIを使ってみました。今回から排他制御に入りたいと思います。しかし似たようなAPIが複数あって何をどこに使ったら良いんだかRTOS素人の私にはサッパリです。今回は一番単純そうなMutexを実験しつつ、近縁?らしいAPIのいくつかについてまとめてみました。
※「モダンOSのお砂場」投稿順Indexはこちら
※実験コードのビルドは、Arm社のWeb開発環境 Mbed Compilerを使わせていただいております。登録すればインストールせずに即使える開発環境です。実機ターゲットは、ST Microelectronics社のNucleo-F401REボード(STM32F401REマイコン搭載。コアは Arm Cortex M4F)です。
Mutex、Semaphore、PlatformMutex
まず Mbed OSのAPIドキュメントのMutexの項へのリンクが以下です。
Mutexは、スレッド間の同期(排他制御)に使えるAPIです。Mutex1個で1個のリソースの排他制御が行えます。上記のドキュメントを読むとその限界というか、使用制限が書いてあります。
ISR(インタラプト・サービス・ルーチン)の中で使ってはいけない
使うとフェイタルエラーになるみたいです。恐ろしい。じゃ、ISRの中でも排他制御したかったらどうするの?という疑問に対しては
ISRの中で排他制御したければ Semaphore を使え
というお答えです。Semaphoreもスレッド間同期(排他制御)に使えるAPIですが、Mutexより高級?(そういう言い方するか?)で複数リソースが入っている「プール」の管理ができるものです。プールしてあるリソースが無くなるまで複数のスレッドにリソース割り当てできると。ドキュメントは以下です。
再びMutexに戻ると
standard libraryのstdio, malloc, new は mutexロックかかっている
とも書かれています。つまりprintfとかstdioを使う関数をISRの中で呼んではいけない、というお約束の根拠です。そういうことだったのね。
ところがですね、もう一つMutexと呼ばれているものが存在しました。PlatformMutexというものです。PlatformMutexに対して、上で述べて来た「普通の」Mutexは RTOS Mutex と呼ばれているみたいです。
PlatformMutexは「RTOS不在の」環境(たとえばベアメタル)でも使えるということです。特に断りは書いてないので「多分」ISRの中でも使えるのではないかしらん。知らんけど。こちらのMutexは、IOを制御するドライバAPIの中でリソース(まさにハードウエアの)を排他制御するために使われているようです。
まあ、このくらいのホンワカした理解で、サンプルコードを書いて走らせてみます。
実験に使用したソース
使用のソースは以下ですが、例によって前回使用のソースのコピペかつチョイ直しの流用です。リソースに見立てたのは外付けのLEDで赤と緑です。Nucleo-F401REのPA_8端子に赤を、PB_10端子に緑が接続してあり、Mutexで制御権を取った方の色(Thread1なら赤、Thread2なら緑)が光るという仕組み。いつMutexにアクセスするのかは、乱数で決めているので、しばらく同じ色で光っていたり、一瞬光って別の色になったりと不規則な動きをするはず。
#include "mbed.h" #include "platform/mbed_thread.h" #include "stdlib.h" #define BLINKING_RATE_MS (500) #define T1_ON (1) #define T2_ON (2) Mutex extLed_mutex; Thread t1; Thread t2; DigitalOut ledA(PA_8); DigitalOut ledB(PB_10); void extLedOnOff(int led) { if (led == T1_ON) { ledA = 0; //LOW ACTIVE ledB = 1; } else { ledA = 1; ledB = 0; //LOW ACTIVE } } void t1_thread() { int waitMS; while (true) { waitMS = (rand() & 0xFF) * 10; extLed_mutex.lock(); extLedOnOff(T1_ON); extLed_mutex.unlock(); ThisThread::sleep_for(waitMS); } } void t2_thread() { int waitMS; while (true) { waitMS = (rand() & 0xFF) * 10; extLed_mutex.lock(); extLedOnOff(T2_ON); extLed_mutex.unlock(); ThisThread::sleep_for(waitMS); } } int main() { printf("\r\n\r\n*** Mutex Sample ***\r\n"); DigitalOut led(LED1); t1.start(t1_thread); t2.start(t2_thread); while (true) { led = !led; thread_sleep_for(BLINKING_RATE_MS); } }
ビルド
Arm MbedのWeb環境でビルドした結果が以下に。FlashもRAMも大した分量は使ってません。ま、ほとんど何の仕事もしないコードなので当然か。
実機実行結果
冒頭のアイキャッチ画像に動いているところの写真を掲げましたが、予定どおりにチカチカと不規則なパターンで赤、緑が点灯いたします。Mutex動いているのかね。実際にどのくらい一方が他方を待たせているんだか測定していないので、イマイチですな。こんど、もっと細かい頻度でMutexをゲットするように(ぶつかる確率を上げる)書き換えた上で、観察してみますかね?