ITRON セマフォの基礎【カウンティングセマフォの使い方】

ITRON入門カリキュラム
この記事は約12分で読めます。

こんにちは、ナナです。

ここまで、イベントフラグ・データキュー・メールボックスと主にタスク間でデータを通信するためのオブジェクトを解説してきました。

「セマフォ」は、ITRONの中では同期・通信機能として同じ部類に定義された機能ですが、通信を主としたこれまでの機能とは役割が異なります。

セマフォには大きく分けて次の2種類があります。

セマフォの種類
  • カウンティングセマフォ(本記事で解説)
  • バイナリセマフォ

まずは「カウンティングセマフォ」から解説し「セマフォ」の基礎を学びましょう!

本記事では次の悩みを解消する内容となっています。

本記事の悩み解決リスト
  • セマフォ機能とは何か?
  • セマフォ機能に関するサービスコールとは?
  • セマフォオブジェクトの生成方法
  • カウンティングセマフォとは?
  • セマフォの獲得と返却とは?
  • カウンティングセマフォを使ったサンプルプログラム

それでは、「カウンティングセマフォ」を解説しましょう!

スポンサー

セマフォ機能の概要

師匠!今日のテーマは「セマフォ」ですか?恥ずかしながら我が人生において、はじめてお目にかかる言葉です。

ナナ
ナナ

そうだね、なかなか日常で「セマフォ」という言葉を耳にすることはないだろうね。でも、マルチタスクのシステムにおいて排他制御を司る「セマフォ」はすごく大事な機能だね。

「セマフォ」という言葉を始めて耳にする方もいることでしょう。まずは、「セマフォ」の概要から学んでいきましょう。

セマフォの仕様

それでは、まずはITRON仕様を確認してみましょう。

ITRON仕様書:4.4.1 セマフォ

セマフォは、使用されていない資源の有無や数量を数値で表現することにより、その資源を使用する際の排他制御や同期を行うためのオブジェクトである。

このように「使用されていない資源の有無や数量を表現する」のがセマフォです。

ここで示されている「使用されていない資源」とは何なのか?

例えばレンタサイクルショップがあったとします。貸し出せる自転車の台数は決まってますよね。

セマフォで管理する資源数

この資源の「残数」を管理するのがセマフォです。

ナナ
ナナ

このようにソフトウェア上で管理する資源に対して、その利用できる数を管理するのが「セマフォ」なんです。

セマフォで使用する代表的なサービスコール

セマフォではよくこれらのサービスコールを使用しますので覚えておきましょう。

セマフォの代表的なサービスコール
  • CRE_SEM
  • cre_sem
  • sig_sem
  • wai_sem
ナナ
ナナ

「セマフォ」は略称で「セマ」と呼んだりすることがあります。

そのため、sig_semは「シグセマ」、wai_semは「ウェイセマ」なんて呼んだりしますよ!

スポンサー

セマフォオブジェクトの生成方法

師匠!それではいつも通り、まずはオブジェクトの生成が必要ですよね。どんな風に「セマフォ」は作ればよいのでしょうか?

ナナ
ナナ

はい、その通り。では、セマフォオブジェクトの生成からやってみましょう。指定するパラメータはシンプルなので難しくありませんよ。

セマフォ生成のための静的API

セマフォを生成するための静的APIを見てみましょう。

CRE_SEM(ID semid, {ATR sematr, UINT isemcnt, UINT maxsem});

引数パラメータを解説しておきます。

IDsemid生成するセマフォのID。整数として一意のIDを割り付ける。
ATRsematrセマフォの属性
UINTisemcnt資源数の初期値
UINTmaxsem資源数の最大値

属性には(TA_TFIFO || TA_TPRI)が指定できます。これはセマフォに対してタスクが待ち状態になった場合の、待ち順を決定するパラメータです。

maxsemとisemcntに関しては管理したい対象資源の数を指定します。

ナナ
ナナ

生成時に必要となる「初期値」は「最大数」に合わせておくのが自然でしょう。

最大資源数が2以上のセマフォのことを「カウンティングセマフォ」と呼びます。

CRE_SEMによるセマフォの生成定義

では、セマフォを生成してみましょう。system.cfgにセマフォを1つ生成定義してみてください。

追加するセマフォ生成情報
  • IDは「SEMID_SEM1」を指定する
  • 属性は「TA_TFIFO」を指定する
  • 初期値と最大値は「2」を指定する
//------------------------------------------------
//	メールボックス定義
//------------------------------------------------
CRE_MBX(MBXID_MBX1,  {(TA_TFIFO | TA_MFIFO), 1, NULL});

//------------------------------------------------
//	セマフォ定義
//------------------------------------------------
//------------------------------------------------
//	セマフォ定義
//------------------------------------------------
CRE_SEM(SEMID_SEM1,  {TA_TFIFO, 2, 2});
スポンサー

セマフォの資源獲得と返却方法

師匠!「セマフォ」は生成以外にはどのようなサービスコールを持っているのでしょうか?「資源数」に関わるものなんですよね?

ナナ
ナナ

セマフォは資源を管理するため、基本的なサービスコールは「資源の獲得/返却」ができるサービスコールを提供します。

セマフォの概要を再度読んでみましょう。

ITRON仕様書:4.4.1 セマフォ

セマフォは、対応する資源の有無や数量を表現する資源数と、資源の獲得を待つタスクの待ち行列を持つ。

資源を返却する側ではセマフォの資源数を1つ増やす。一方、資源を獲得する側ではセマフォの資源数を1つ減らす。

セマフォの資源数が足りなくなった場合、資源を獲得しようとしたタスクは次に資源が返却されるまでセマフォ資源の獲得待ち状態となる。

セマフォとは資源数の管理をしてくれるオブジェクトです。セマフォに対する操作とは「資源の獲得(wai_sem)」「資源の返却(sig_sem)」です。

セマフォ資源を獲得するサービスコール

では、wai_semのITRON仕様を見てみましょう。

ER wai_sem(ID semid);
ITRON仕様書:wai_sem セマフォ資源の獲得

semidで指定されるセマフォから資源を1つ獲得する。

具体的には、対象セマフォの資源数が1以上の場合には、セマフォの資源数から1を減じ、自タスクを待ち状態とせずにサービスコールの処理を終了する。

対象セマフォの資源数が0の場合には、自タスクを待ち行列につなぎ、セマフォ資源の獲得待ち状態に移行させる。

つまり、wai_semサービスコールが呼び出されることで、残数を示す資源数が減少します。残数が残っている場合は、タスクに特に影響はありません。

wai_semによる資源数の減少

ただし、残数が0の時にwai_semサービスコールが呼び出されると、呼び出したタスクは「待ち状態」へと変化します。

wai_semによる待ち状態へ変化

この場合、セマフォに対する資源返却があるまでタスクは「待ち状態」を続けることになります。

ナナ
ナナ

このように資源の獲得において、残数がない場合はタスクの動きを止めることができる、というのがセマフォの特徴です。

セマフォ資源を返却するサービスコール

それでは次に、sig_semのITRON仕様を見てみましょう。

ER sig_sem(ID semid);
ITRON仕様書:sig_sem セマフォ資源の返却

semidで指定されるセマフォに対して、資源を1つ返却する。

具体的にはセマフォに対して資源の獲得を待っているタスクがある場合には、待ち行列の先頭のタスクを待ち解除する。

つまり、sig_semによる資源が返却されるため、セマフォの資源数が増加することになります。

sig_semによる資源数の増加

資源数が0となっており、別のタスクが「待ち状態」になっている場合は、sig_semによる資源返却により、直ちに待ち状態が解除されます。

sig_semによる待ち状態の解除

このケースではセマフォの資源数は0のままです。なぜなら、返却された資源は待っていたwai_semにすぐに貸し出されてしまうためです。

ナナ
ナナ

wai_sem/sig_sem共に引数はセマフォIDのみで非常にシンプルなサービスコールとなっています。

これらが様々なタスクから呼び出されることで資源の獲得/返却を繰り返すのです。

スポンサー

カウンティングセマフォを使ったプログラム

師匠!「カウンティングセマフォ」ってプログラムからどんな風に使えばいいですか?

ナナ
ナナ

うん、それじゃあ「wai_sem」と「sig_sem」をタスクから呼んでみてどんな動きをするかを確認してみましょうね。

それでは、資源数「2」のセマフォを3人のタスクで獲得/返却を繰り返してみましょう。

つまり、3人のうち1人は必ず待たされることになりますね。

まずはタスクを3人分用意しましょう。

//------------------------------------------------
//	タスク定義
//------------------------------------------------
CRE_TSK(TSKID_MAIN,  {TA_HLNG|TA_ACT, 0, MAIN,  1, 128, NULL});
CRE_TSK(TSKID_TASK1, {TA_HLNG|TA_ACT, 0, TASK1, 1, 128, NULL});
CRE_TSK(TSKID_TASK2, {TA_HLNG|TA_ACT, 0, TASK2, 1, 128, NULL});

各タスク関数では次のように「wai_sem」と「sig_sem」を実施しながらシリアル通信で動作ログをパソコンに送信します。

//------------------------------------------------
//	概	要:MAINタスク
//------------------------------------------------
void MAIN(VP_INT exinf)
{
	while(1)
	{
		Sci_putString("MAIN wai_sem()\n");
		wai_sem(SEMID_SEM1);
		Sci_putString("MAIN SEM get!\n");
				
		dly_tsk(10000);

		Sci_putString("MAIN sig_sem()\n");
		sig_sem(SEMID_SEM1);

		dly_tsk(1000);
	}

	return;
}

//------------------------------------------------
//  概  要:練習用TASK1関数
//------------------------------------------------
void TASK1(VP_INT exinf)
{
	while(1)
	{
		Sci_putString("TASK1 wai_sem()\n");
		wai_sem(SEMID_SEM1);
		Sci_putString("TASK1 SEM get!\n");
				
		dly_tsk(10000);

		Sci_putString("TASK1 sig_sem()\n");
		sig_sem(SEMID_SEM1);

		dly_tsk(1000);
	}

	return;
}


//------------------------------------------------
//  概  要:練習用TASK2関数
//------------------------------------------------
void TASK2(VP_INT exinf)
{
	while(1)
	{
		Sci_putString("TASK2 wai_sem()\n");
		wai_sem(SEMID_SEM1);
		Sci_putString("TASK2 SEM get!\n");
				
		dly_tsk(10000);

		Sci_putString("TASK2 sig_sem()\n");
		sig_sem(SEMID_SEM1);

		dly_tsk(1000);
	}

	return;
}

実行して、パソコン側のTeraTermでログを受信すると定期的に「タスク名 wai_sem()」のログが出力されて停止する様子が見えるでしょう。

MAIN wai_sem()
TASK1 sig_sem()
MAIN SEM get!
TASK1 wai_sem()
TASK2 sig_sem()
TASK1 SEM get!
TASK2 wai_sem()

次のように、「wai_sem」にて停止しているタスクは、別タスクからの「sig_sem」により資源獲得ができているのがわかりますね。

MAIN wai_sem()		MAINタスクが獲得待ち状態へ
TASK1 sig_sem()		TASK1タスクが資源を返却
MAIN SEM get!		MAINタスクが資源を獲得
TASK1 wai_sem()
TASK2 sig_sem()
TASK1 SEM get!
TASK2 wai_sem()
ナナ
ナナ

このように複数のタスクが数が限られた資源を共有しながら使いたい場合に、待ち合わせができる仕組みを「セマフォ」は提供します。

スポンサー

カウンティングセマフォによるタスク間同期のまとめ

それでは「カウンティングセマフォ」の特徴をまとめます。

  1. セマフォはオブジェクトとして生成が必要
  2. セマフォは資源数を管理するオブジェクトであり、生成時に資源数を決定
  3. 資源数が2以上のセマフォを「カウンティングセマフォ」と呼ぶ
  4. セマフォ資源は「wai_sem」による獲得と「sig_sem」による返却ができる
  5. セマフォ資源が0の時に「wai_sem」を呼び出したタスクは「待ち状態」へとタスク状態が変化する。

次に読むべきカリキュラム

「カウンティングセマフォ」を学んだ次は「バイナリセマフォ」を学びましょう。「バイナリセマフォ」こそが実践的によく利用されるセマフォなのです。