セマフォ(semaphore)とはタスク間の同期制御のためのカウンタのことです。有限個のリソースを複数のタスクで共有したり、イベントの発生回数をタスクに伝播させたりすることに利用します。 wai_sem でセマフォを獲得するとセマフォ内のカウントは1減らされ、逆に sig_sem でセマフォを返すとカウンタは1増やされます。セマフォはカウンタの初期値と最大値のパラメーターをもって生成されます。wai_sem を呼んだ際にセマフォのカウンタが0であった場合は他のタスクがセマフォを返すまで待ち状態になりセマフォの待ち行列に追加されます。
セマフォの最も簡単な利用例としてクリティカルセクション(CriticalSection)の作成が挙げられるでしょう。クリティカルセクションとは、一度に1つのタスクしかアクセスしてはいけないリソースやコードのセクションのことを言います。セマフォのカウンタの初期値、最大値ともに1として利用します。
タスクはクリティカルセクションに入るときに wai_sem でセマフォを取得し、クリティカルセクションを出るときに sig_sem でセマフォを返却します。もし wai_sem を実行したときに他のタスクがクリティカルセクションに入っていた場合(セマフォカウンタが0の場合)はそのタスクがクリティカルセクションを出るまで待ち状態になりますから、同時にクリティカルセクションを実行するタスクは最大で1つに制限されます。
クリティカルセクションはハードウェアに対する制御や共有変数のアクセスなどの排他制御に用いられます。例えばいくつかのタスクが現在の状態を表示するのに LCD を共有してしたいとしましょう。まず LCD の排他制御用に最大値1のセマフォを定義します。LCDに表示する際にはまずそのセマフォを取得し、カーソルを自分の情報を書き出す位置に移動させ、表示を行います。処理が終わったらセマフォを返すようにすれば、一度に LCDに命令を送るのは1つのタスクだけですからきちんと共有できます。共有変数にアクセスする場合もクリティカルセクションを内でアクセスを行えば、あるタスクが変数を読んでいる最中に他のタスクがその値を書き換えてしまうなどといった問題を回避できます。
非同期に発生するイベントの中で発生した事象の回数そのものが情報である場合があります。このような事象を伝達するのにセマフォを利用することができます。
例えばボタンを押すごとにモーターを一回転させるという処理を考えます。ボタンが押されるごとに割り込みがかかり、その時にモーター処理タスクが起床されモーターを一回転させるという処理が想像されます。単にタスクを無限ループの先頭で wai_tsk で待ち状態にし、割り込みタスクから wup_tsk で起床させてもよいのですが、タスク起床のネストをサポートしていないOSではモーター回転中に続けて押された場合にはその分を取りこぼしてしまいます。ここではセマフォを使えば確実に処理できます。その処理の概要を下図に示します。