ロック・サービスの作成
プログラマーによっては、 スレッド・ライブラリーで提供されている標準ロック・サービス (mutex) は使用せず、 独自のハイレベル・ロック・サービスをインプリメントすることを希望します。
例えば、データベース・プロダクトが、 既に一組の内部定義サービスを使用している場合があります。 これらのロック・サービスを新しいシステムに合わせる方が、 このサービスを使用する全内部モジュールを調整するより容易であることがあります。
このため、AIX®は、より高レベルのロックサービスを構築するために使用できるアトミックロッキングサービスプリミティブを提供している。 マルチプロセッサー・セーフのサービス (標準 mutex サービスなど) を作成するには、プログラマーは、 compare_and_swap サブルーチンなどのアトミック操作サービスではなく、このセクションで説明するアトミック・ロック・サービスを使用する必要があります。
マルチプロセッサー・セーフ・ロック・サービス
ロック・サービスは、同時に使用される可能性のあるリソースへのアクセスを逐次化するために使用されます。 例えば、ロック・サービスは、 複数のポインター更新が必要なリンク・リストへの挿入に使用することができます。 1 つのプロセスによる更新シーケンスが同じリストにアクセスしようとする 2 番目のプロセスによって割り込まれた場合、 エラーが起こる可能性があります。 割り込みを受け付けられない操作のシーケンスを、 クリティカル・セクション と呼びます。
ロック・サービスは、ロック・ワードを使用してロックの状況を示します。 ここで、0 (ゼロ) は自由に使用できることを、そして 1 は使用中であることを示します。 そのため、ロック獲得のサービスは、次のようになります。
test the lock word
if the lock is free
set the lock word to busy
return SUCCESS
... - インポート・フェンス
- インポート・フェンス は、既に実行されているすべての命令が完了するまで遅延する、特殊なマシン・インストラクションです。 ロックと連動させて使用すると、そのロックが取得されるまで、命令を推定した実行が防止されます。
- エクスポート・フェンス
- エクスポート・フェンス は、ロックが解除される前に、 保護されるデータが必ず他のすべてのプロセッサーから見えるようにします。
このように複雑なマシン固有の命令の代わりに、 次のサブルーチンが定義されています。
- _check_lock
- 条件によって、シングル・ワードの変数を自動的に更新し、 同時にマルチプロセッサー・システム用のインポート・フェンス を発行します。 compare_and_swap サブルーチンはこれと似ていますが、 インポート・フェンスを発行しないので、ロックのインプリメントには使用できません。
- _clear_lock
- シングル・ワードの変数を自動的に書き込み、 同時にマルチプロセッサー・システム用のエクスポート・フェンス を発行します。
カーネル・プログラミング
カーネル・プログラミングの詳細については、「 カーネル・エクステンション機能とデバイス・サポート・プログラミングの概念」を参照してください。 この項では、マルチプロセッサー・システムで必要とされる機能の主な相違点に焦点を当てます。
ある種のクリティカル・リソースにアクセスするときに、 逐次化が必要になることがよくあります。 ロック・サービスを使用することによって、 プロセス環境においてスレッド・アクセスを逐次化することができますが、 これでは、割り込み環境で起こるアクセスから保護することはできません。 新しいコードや移植されたコードでは、カーネルサービスのdisable_lock と unlock_enableを使用する必要があります。これらのカーネルサービスは、i_disableカーネルサービスの代わりに、割り込み制御に加えて単純なロックを使用します。 これらのカーネル・サービスは、 ロックのない割り込みサービスを使用するだけのユニプロセッサー・システムでも使用することができます。 詳しくは、「 Kernel Extensions and Device Support Programming Concepts」の「 Locking Kernel Services 」を参照してください。
デフォルトのデバイス・ドライバーは、論理的ユニプロセッサー環境で実行され、 この環境ではファネル・モードと呼ばれています。 ユニプロセッサー・システム用の適切なドライバーの大部分は、 このモードでもそのまま動きますが、注意深く調べて、 マルチプロセッシングによる効果が得られるように変更する必要があります。 最後に、タイマーのカーネル・サービスは、 マルチプロセッサー環境では常に成功するとは限らないので、戻り値を持っています。 したがって、新しいコードや移植されたコードの場合は、この戻り値を調べる必要があります。 詳しくは、「 Kernel Extensions and Device Support Programming Concepts」の「 Using Multiprocessor-Safe Timer Services 」を参照してください。
ロック・サービスの例
#include <sys/atomic_op.h> /* for locking primitives */
#define SUCCESS 0
#define FAILURE -1
#define LOCK_FREE 0
#define LOCK_TAKEN 1
typdef struct {
atomic_p lock; /* lock word */
tid_t owner; /* identifies the lock owner */
... /* implementation dependent fields */
} my_mutex_t;
...
int my_mutex_lock(my_mutex_t *mutex)
{
tid_t self; /* caller's identifier */
/*
Perform various checks:
is mutex a valid pointer?
has the mutex been initialized?
*/
...
/* test that the caller does not have the mutex */
self = thread_self();
if (mutex->owner == self)
return FAILURE;
/*
Perform a test-and-set primitive in a loop.
In this implementation, yield the processor if failure.
Other solutions include: spin (continuously check);
or yield after a fixed number of checks.
*/
while (_check_lock(mutex->lock, LOCK_FREE, LOCK_TAKEN))
yield();
mutex->owner = self;
return SUCCESS;
} /* end of my_mutex_lock */
int my_mutex_unlock(my_mutex_t *mutex)
{
/*
Perform various checks:
is mutex a valid pointer?
has the mutex been initialized?
*/
...
/* test that the caller owns the mutex */
if (mutex->owner != thread_self())
return FAILURE;
_clear_lock(mutex->lock, LOCK_FREE);
return SUCCESS;
} /* end of my_mutex_unlock */