この章では、Solaris スレッドと POSIX スレッドのアプリケーションプログラミングインタフェース (API) を比較し、POSIX スレッドにはない Solaris の機能について説明します。この章の内容は次のとおりです。
Solaris スレッド API と pthread API は、同じ問題に対する 2 つの解決策です。 それは、アプリケーションソフトウェアへの並列性構築です。どちらの API もそれ自体で完結したものですが、Solaris スレッドの関数と pthread の関数を同じプログラムの中で併用することもできます。
ただし、2 つの API は完全に一致しているわけではありません。Solaris スレッドは pthread にない関数をサポートしていて、pthread には Solaris インタフェースでサポートされない関数が含まれています。同じ関数については、機能が実質的に同じでも使用する引数が異なることがあります。
2 つの API を組み合わせて使用すれば、それぞれ他方にない機能を補い合うことができます。また、同じシステムで、Solaris スレッドだけを使用するアプリケーションを実行する一方で、pthread だけを使用するアプリケーションを実行することもできます。
Solaris スレッドと pthread は、API の動作や構文も非常によく似ています。表 6–1 に、主な相違点を示します。
表 6–1 Solaris スレッドと pthread の相違点
Solaris スレッド |
POSIX スレッド |
---|---|
スレッド関数名の接頭辞が thr_ で、セマフォー関数名の接頭辞が sema_ |
スレッド関数名の接頭辞が pthread_ で、セマフォー関数名の接頭辞が sem_ |
デーモンスレッドが生成可能 |
取り消しセマンティクス |
スレッドの停止と再開 |
スケジューリングポリシー |
次の表は、Solaris スレッドの関数と pthread の関数を比較対照したものです。なお、Solaris スレッドの関数と pthread の関数が本質的に同じものとして並記されている場合でも、その引数は異なっていることがあります。
pthread または Solaris スレッドの側に相当するインタフェースがない場合は、「-」が記入されています。pthread 欄の項目で「(3RT)」が付記されているものは、POSIX.1b リアルタイム拡張機能ライブラリである librt 内の関数であり、pthread の一部ではありません。このライブラリ内の関数は、POSIX.1b リアルタイム拡張機能で指定されているほとんどのインタフェースを提供しています。
表 6–2 Solaris スレッドと POSIX pthread の比較
Solaris スレッド |
pthread |
---|---|
thr_create() |
pthread_create() |
thr_exit() |
pthread_exit() |
thr_join() |
pthread_join() |
thr_yield() |
sched_yield(3RT)() |
thr_self() |
pthread_self() |
thr_kill() |
pthread_kill() |
thr_sigsetmask() |
pthread_sigmask() |
thr_setprio() |
pthread_setschedparam() |
thr_getprio() |
pthread_getschedparam() |
thr_setconcurrency() |
pthread_setconcurrency() |
thr_getconcurrency() |
pthread_getconcurrency() |
thr_suspend() |
- |
thr_continue() |
- |
thr_keycreate() |
pthread_key_create() |
- |
pthread_key_delete() |
thr_setspecific() |
pthread_setspecific() |
thr_getspecific() |
pthread_getspecific() |
- |
pthread_once() |
- |
pthread_equal() |
- |
pthread_cancel() |
- |
pthread_testcancel() |
- |
pthread_cleanup_push() |
- |
pthread_cleanup_pop() |
- |
pthread_setcanceltype() |
- |
pthread_setcancelstate() |
mutex_lock() |
pthread_mutex_lock() |
mutex_unlock() |
pthread_mutex_unlock() |
mutex_trylock() |
pthread_mutex_trylock() |
mutex_init() |
pthread_mutex_init() |
mutex_destroy() |
pthread_mutex_destroy() |
cond_wait() |
pthread_cond_wait() |
cond_timedwait() |
pthread_cond_timedwait() |
cond_reltimedwait() |
pthread_cond_reltimedwait_np() |
cond_signal() |
pthread_cond_signal() |
cond_broadcast() |
pthread_cond_broadcast() |
cond_init() |
pthread_cond_init() |
cond_destroy() |
pthread_cond_destroy() |
rwlock_init() |
pthread_rwlock_init() |
rwlock_destroy() |
pthread_rwlock_destroy() |
rw_rdlock() |
pthread_rwlock_rdlock() |
rw_wrlock() |
pthread_rwlock_wrlock() |
rw_unlock() |
pthread_rwlock_unlock() |
rw_tryrdlock() |
pthread_rwlock_tryrdlock() |
rw_trywrlock() |
pthread_rwlock_trywrlock() |
- |
pthread_rwlockattr_init() |
- |
pthread_rwlockattr_destroy() |
- |
pthread_rwlockattr_getpshared() |
- |
pthread_rwlockattr_setpshared() |
sema_init() |
sem_init(3RT)() |
sema_destroy() |
sem_destroy(3RT)() |
sema_wait() |
sem_wait(3RT)() |
sema_post() |
sem_post(3RT)() |
sema_trywait() |
sem_trywait(3RT)() |
fork1() |
fork() |
- |
pthread_atfork() |
forkall() (複数スレッドコピー) |
- |
- |
pthread_mutexattr_init() |
- |
pthread_mutexattr_destroy() |
mutex_init() の type 引数 |
pthread_mutexattr_setpshared() |
- |
pthread_mutexattr_getpshared() |
- |
pthread_mutex_attr_settype() |
- |
pthread_mutex_attr_gettype() |
- |
pthread_condattr_init() |
- |
pthread_condattr_destroy() |
cond_init() の type 引数 |
pthread_condattr_setpshared() |
- |
pthread_condattr_getpshared() |
- |
pthread_attr_init() |
- |
pthread_attr_destroy() |
thr_create() の THR_BOUND フラグ |
pthread_attr_setscope() |
- |
pthread_attr_getscope() |
- |
pthread_attr_setguardsize() |
- |
pthread_attr_getguardsize() |
thr_create() の stack_size 引数 |
pthread_attr_setstacksize() |
- |
pthread_attr_getstacksize() |
thr_create() の stack_addr 引数 |
pthread_attr_setstack() |
- |
pthread_attr_getstack() |
thr_create() の THR_DETACH フラグ |
pthread_attr_setdetachstate() |
- |
pthread_attr_getdetachstate() |
- |
pthread_attr_setschedparam() |
- |
pthread_attr_getschedparam() |
- |
pthread_attr_setinheritsched() |
- |
pthread_attr_getinheritsched() |
- |
pthread_attr_setsschedpolicy() |
- |
pthread_attr_getschedpolicy() |
この章で説明する Solaris スレッドの関数を使用するには、Solaris スレッドライブラリ (-lthread ) とリンクする必要があります。
Solaris スレッドと pthread では、関数名や引数が異なっていても、実質上の操作は変わりません。正しいインクルードファイルと関数プロトタイプを示した簡単な例を示すだけとします。Solaris スレッドの関数の戻り値が記述されていないものについては、『SunOS リファレンスマニュアル 3 : 基本ライブラリ関数』で調べてください。
Solaris 関連の関数の詳細は、pthread の関連マニュアルで類似した名前の関数を調べてください。
Solaris スレッドの関数で pthread にない機能を持つものについて、詳しく説明しています。
この節では、Solaris スレッドに固有の次の関数について説明します。 スレッドの実行を中断する関数と、中断したスレッドを再開する関数です。
thr_suspend(3C) は、target_thread で指定されたスレッドの実行をただちに停止します。thr_suspend() が正常終了した時点で、指定のスレッドは実行状態ではありません。
thr_suspend() が目的のスレッドを停止するとき、そのスレッドが保持しているロックについては考慮されません。したがって、thr_suspend() は慎重に使用する必要があります。一時停止したスレッドに保持されているロックを必要とする関数が呼び出されると、デッドロックが発生します。
#include <thread.h> int thr_suspend(thread_t tid);
停止しているスレッドに対して再度 thr_suspend() を発行しても効果はありません。停止しているスレッドをシグナルで呼び起こすことはできません。スレッドが実行を再開するまでシグナルは保留状態のままです。
次の例では、pthread で定義されている pthread_t
tid と Solaris スレッドの thread_t
tid が同じです。tid 値は、代入によっても型変換によっても使用できます。
thread_t tid; /* tid from thr_create() */ /* pthreads equivalent of Solaris tid from thread created */ /* with pthread_create() */ pthread_t ptid; int ret; ret = thr_suspend(tid); /* using pthreads ID variable with a cast */ ret = thr_suspend((thread_t) ptid);
thr_suspend()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、thr_suspend() は失敗し、対応する値を返します。
ESRCH
説明:現在のプロセスに tid が存在しません。
thr_continue(3C) は、停止しているスレッドの実行を再開します。再開したスレッドに対して再度 thr_continue() を発行しても効果はありません。
#include <thread.h> int thr_continue(thread_t tid);
停止しているスレッドは、シグナルでは呼び起こされません。送られたシグナルは、そのスレッドが thr_continue() で再開されるまで保留されます。
pthread で定義されている pthread_t
tid と Solaris スレッドの thread_t
tid が同じです。tid 値は、代入によっても型変換によっても使用できます。
thread_t tid; /* tid from thr_create()*/ /* pthreads equivalent of Solaris tid from thread created */ /* with pthread_create()*/ pthread_t ptid; int ret; ret = thr_continue(tid); /* using pthreads ID variable with a cast */ ret = thr_continue((thread_t) ptid)
thr_continue()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、thr_continue() は失敗し、対応する値を戻します。
ESRCH
説明:現在のプロセスに tid が存在しません。
読み取り/書き込みロックを使用すると、同時に書き込み操作ができるスレッドを 1 つだけに制限する一方、読み取り操作は同時に複数のスレッドからできるようになります。この節の内容は次のとおりです。
読み取り/書き込みロックの初期化
読み取りロックの獲得
読み取りロックの獲得の試行
書き込みロックの獲得
書き込みロックの獲得の試行
読み取り/書き込みロックの解除
読み取り/書き込みロックの状態の削除
すでに読み取りロックを保持しているスレッドがある場合、ほかのスレッドがさらに読み取りロックを獲得できますが、書き込みロックを獲得するときは待たなければなりません。すでに書き込みロックを保持しているスレッドがある場合、あるいは書き込みロックの獲得を待っているスレッドがある場合、ほかのスレッドは読み取りと書き込みのどちらのロックを獲得するときも待たなければなりません。
読み取り/書き込みロックは、相互排他ロックよりも低速です。しかし、読み取り/書き込みロックでは、書き込みの頻度が低く、かつ多数のスレッドから並行的に読み取られるようなデータを保護するときの性能を改善することができます。
このプロセス内のスレッドと別のプロセス内のスレッド間で同期をとるには、読み取り/書き込みロックを使用します。連携するそれらのプロセスの間で共有される書き込み可能なメモリーに、読み取り/書き込みロックの領域を確保します。この動作の読み取り/書き込みロックのマッピングについては、mmap(2) のマニュアルページを参照してください。
複数のスレッドが読み取り/書き込みロックを待っている場合のロックの獲得順序は、特に指定しなければ不定です。ただし、書き込み側がいつまでもロックを獲得できないような事態を回避するため、Solaris スレッドパッケージでは書き込み側が同じ優先順位の読み取り側より優先されます。
読み取り/書き込みロックは、使用する前に初期化する必要があります。
rwlp が指す読み取り/書き込みロックを初期化して、ロック状態を解除するには、rwlock_init(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rwlock_init(rwlock_t *rwlp, int type, void * arg);
type は、次のいずれかになります。
USYNC_PROCESS 読み取り/書き込みロックを使って、このプロセスとほかのプロセスのスレッド間で同期をとることができます。arg は無視されます。
USYNC_THREAD 読み取り/書き込みロックを使って、このプロセス内のスレッドの間だけで同期をとることができます。arg は無視されます。
複数のスレッドから同じ読み取り/書き込みロックを同時に初期化してはいけません。0 に初期化したメモリーに領域を確保することによって、読み取り/書き込みロックを初期化することもできます。その場合は、type に USYNC_THREAD を指定したものとみなされます。一度初期化した読み取り/書き込みロックは、ほかのスレッドで使われている可能性があるので再初期化してはいけません。
POSIX スレッドの場合については、「pthread_rwlock_init の構文」を参照してください。
#include <thread.h> rwlock_t rwlp; int ret; /* to be used within this process only */ ret = rwlock_init(&rwlp, USYNC_THREAD, 0);
#include <thread.h> rwlock_t rwlp; int ret; /* to be used among all processes */ ret = rwlock_init(&rwlp, USYNC_PROCESS, 0);
rwlock_init()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp または arg が不当なアドレスを指しています。
rwlp が指す読み取り/書き込みロック上で読み取りロックを獲得するには、rw_rdlock(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rw_rdlock(rwlock_t *rwlp);
指定した読み取り/書き込みロックが書き込み用にすでにロックされている場合、呼び出しスレッドは書き込みロックが解放されるまでブロックされます。そうでなければ、呼び出しスレッドは読み取りロックを獲得します。POSIX スレッドの場合については、「pthread_rwlock_rdlock の構文」を参照してください。
rw_rdlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
rwlp が指す読み取り/書き込みロック上に読み取りロックを獲得しようとするには、rw_tryrdlock(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rw_tryrdlock(rwlock_t *rwlp);
読み取り/書き込みロックが書き込み用にロックされているとき、rw_tryrdlock() はエラーを返します。そうでなければ、呼び出しスレッドは読み取りロックを獲得します。POSIX スレッドの場合については、「pthread_rwlock_tryrdlock の構文」を参照してください。
rw_tryrdlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
EBUSY
説明:rwlp が指す読み取り/書き込みロックがすでにロックされています。
rwlp が指す読み取り/書き込みロック上で書き込みロックを獲得するには、rw_wrlock(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rw_wrlock(rwlock_t *rwlp);
指定した読み取り/書き込みロックが、読み取りまたは書き込み用にすでにロックされている場合、呼び出しスレッドは、すべての読み取りロックと書き込みロックが解放されるまでブロックされます。読み取り/書き込みロックの書き込みロックを保持できるスレッドは一度に 1 つに限られます。POSIX スレッドの場合については、「pthread_rwlock_wrlock の構文」を参照してください。
rw_wrlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
rwlp が指す読み取り/書き込みロック上に読み取りロックを獲得しようとするには、rw_trywrlock(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rw_trywrlock(rwlock_t *rwlp);
読み取り/書き込みロックが読み取りまたは書き込み用にロックされているとき、rw_trywrlock() はエラーを返します。POSIX スレッドの場合については、「pthread_rwlock_trywrlock の構文」を参照してください。
rw_trywrlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
EBUSY
説明:rwlp が指す読み取り/書き込みロックがすでにロックされています。
rwlp が指す読み取り/書き込みロックを解除するには、rw_unlock(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rw_unlock(rwlock_t *rwlp);
解除の対象となる読み取り/書き込みロックは、ロックされていて、呼び出しスレッドが読み取り用または書き込み用に保持しているものでなければなりません。その読み取り/書き込みロックが使用可能になるのを待っているスレッドがほかにある場合は、そのスレッドのうちの 1 つがブロック解除されます。POSIX スレッドの場合については、「pthread_rwlock_unlock の構文」を参照してください。
rw_unlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
rwlp が指す読み取り/書き込みロックに関連付けられた状態を削除するには、rwlock_destroy(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rwlock_destroy(rwlock_t *rwlp);
読み取り/書き込みロックの記憶領域は解放されません。POSIX スレッドの場合については、「pthread_rwlock_destroy の構文」を参照してください。
例 6–1 では、銀行口座の例を使って、読み取り/書き込みロックについて説明します。口座残高に対して複数のスレッドが並行的に読み取り専用アクセスできますが、書き込みは 1 つのスレッドだけに制限されます。get_balance() 関数中のロックは、当座預金の残高 (checking_balance) と普通預金の残高 (saving_balance) を合計する演算が、不可分操作によって確実に行われるために必要です。
rwlock_t account_lock; float checking_balance = 100.0; float saving_balance = 100.0; ... rwlock_init(&account_lock, 0, NULL); ... float get_balance() { float bal; rw_rdlock(&account_lock); bal = checking_balance + saving_balance; rw_unlock(&account_lock); return(bal); } void transfer_checking_to_savings(float amount) { rw_wrlock(&account_lock); checking_balance = checking_balance - amount; saving_balance = saving_balance + amount; rw_unlock(&account_lock); }
rwlock_destroy()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
操作 |
参照先 |
---|---|
スレッドの生成 | |
最小のスタックサイズの取得 | |
スレッド識別子の取得 | |
スレッドの実行明け渡し | |
スレッドへのシグナルの送信 | |
呼び出しスレッドのシグナルマスクのアクセス | |
スレッドの終了 | |
スレッドの終了待ち | |
スレッド固有データ用キーの作成 | |
スレッド固有データの設定 | |
スレッド固有データの取得 | |
スレッド優先順位の設定 | |
スレッド優先順位の取得 |
thr_create(3C) ルーチンは、Solaris スレッドインタフェースに含まれるルーチンのうち、もっとも精巧なルーチンの 1 つです。
現在のプロセスに新しい制御スレッドを追加するときは、thr_create(3C) を使用します。POSIX スレッドの場合については、「pthread_create の構文」を参照してください。
#include <thread.h> int thr_create(void *stack_base, size_t stack_size, void *(*start_routine) (void *), void *arg, long flags, thread_t *new_thread); size_t thr_min_stack(void);
新しいスレッドは保留中のシグナルは継承しませんが、優先順位とシグナルマスクを継承します。
stack_base 。新しいスレッドが使用するスタックのアドレスを指定します。NULL を指定すると、新しいスレッドに stack_size バイト以上のサイズをもつスタックが割り当てられます。()
stack_size。新しいスレッドが使用するスタックのバイト数を指定します。stack_size が 0 の場合、デフォルトのサイズが使用されます。通常は 0 を指定してください。stack_size が 0 以外の場合は、thr_min_stack() の戻り値よりも大きい値を指定してください。
通常、スレッド用にスタック空間を割り当てる必要はありません。システムが、各スレッドのスタック用に 1M バイトの仮想メモリーをスワップ空間の予約なしで割り当てます。システムは、mmap(2) の -MAP_NORESERVE オプションを使って割り当てを行います。
start_routine — 新しいスレッドが実行を開始するために使用する関数を指定します。start_routine() で指定した関数が終了すると、スレッドはその関数の戻り値を終了状態に設定して終了します。「thr_exit の構文」を参照してください。
arg — void で記述される任意の変数で、通常は 4 バイト値です。それよりも大きな値を渡す場合は、引数が変数を指す方法で間接的に渡す必要があります。
引数は 1 つしか指定できません。複数の引数をとらせるためには、それらを構造体に入れるなどして、1 つのものとしてコーディングします。
flags — 生成されるスレッドの属性を指定します。通常は 0 を指定してください。
flags の値は、以下に示す引数のビット単位の論理和となります。
THR_SUSPENDED — 新しいスレッドを停止させます。thr_continue でスレッドを再開するまで start_routine() は実行されません。 THR_SUSPENDED は、スレッドを実行する前に優先順位の変更などを行いたいときに使用します。
THR_DETACHED — 新しいスレッドを切り離します。その結果、このスレッドのスレッド識別子やその他のリソースが、スレッド終了後ただちに再利用できるようになります。THR_DETACHED は、スレッドの終了を待つ必要がないときに設定してください。
明示的な同期が割り当てられていないとき、停止していない切り離されたスレッドは失敗することがあります。失敗すると、そのスレッドの生成元が thr_create() から復帰する前に、そのスレッド ID が別の新しいスレッドに割り当てられます。
THR_BOUND — 新しいスレッドを永続的に LWP に結合します。新しいスレッドは結合スレッドになります。Solaris 9 リリースから、結合スレッドと非結合スレッドが区別されなくなりました。すべてのスレッドが結合スレッドとして扱われます。
THR_DAEMON — 新しいスレッドをデーモンにします。デーモンスレッドは常に切り離されます。THR_DAEMONを指定すると、暗黙的に THR_DETACHED が適用されます。デーモンでないスレッドがすべて終了すると、プロセスは終了します。デーモンスレッドは、プロセスの終了状態に影響を与えず、また終了するスレッド数にも含まれません。
プロセスを終了するには、exit() を呼び出すか、プロセス内のスレッドのうち THR_DAEMON フラグを指定せずに生成されたすべてのスレッドから thr_exit(3C) を呼び出します。アプリケーションやそのプロセスによって呼び出されるライブラリでは、終了判断の際に無視される (考慮されない) スレッドを生成できます。THR_DAEMON フラグは、プロセスの終了条件に関係しないスレッドを生成するときに指定します。
new_thread — new_thread が NULL 以外の場合には、thr_create() が正常終了したときに新しいスレッドの ID が格納された場所を指しています。この引数が指す記憶領域は、呼び出し側の責任で確保しなければなりません。このスレッド識別子は、呼び出し側のプロセス内でだけ有効です。
スレッド識別子が特に必要でなければ、new_thread に NULL を指定してください。
thr_create() は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、thr_create() は失敗し、対応する値を戻します。
EAGAIN
説明:システム制限を超えました。たとえば、生成する LWP が多すぎます。
ENOMEM
説明:新しいスレッドを生成するための十分なメモリーがありません。
スレッドの最小スタックサイズを取得するには、thr_min_stack(3C) を使用します。
Solaris スレッドでのスタックの動作は、通常は pthread の場合と同じです。スタックの設定と操作の詳細は、「スタックについて」を参照してください。
#include <thread.h> size_t thr_min_stack(void);
thr_min_stack() は、NULL スレッドの実行に必要な空間の量を返します。NULL スレッドは、NULL 手続きを実行するために生成されたスレッドです。実用的なスレッドに必要なスタック空間は、スタックの絶対最小サイズより大きいので、スタックサイズを小さくするときは十分注意してください。
スレッドが NULL 手続きでなく通常の手続きを実行する場合は、thr_min_stack() の戻り値よりも大きなスタックサイズを割り当てなければなりません。
ユーザーが指定したスタックを使用してスレッドが作成される場合には、このスレッドの実行に必要な空間量を確保する必要があります。動的にリンクされる実行環境の場合には、スレッドの最小スタック要件を決定するのがさらにむずかしくなります。
カスタムスタックは、次の 2 つの方法で指定できます。1 つは、thr_create() でスタックアドレスを NULL に指定し、スタック空間の割り当てをスレッドライブラリに任せる方法です。スタックサイズを指定するパラメータには、希望のサイズを指定します。
もう 1 つの方法は、thr_create() にスタックのポインタを渡して、スタックをすべて自分で管理する方法です。この場合は、スタック空間の割り当てだけでなく、割り当ての解放もユーザー自身で行う必要があります。つまり、スレッドを終了するときに、そのスタックをユーザーが解放しなければなりません。
独自のスタックを割り当てる場合は、mprotect(2) を呼び出して、スタックの最後に必ずレッドゾーンを付加してください。
通常、ユーザー独自のスタックが必要になることはまれです。実際、アプリケーション側が実行環境を完全に制御するなどのごく限られた状況でしか必要になりません。
ユーザーは、システムにスタックの割り当てを任せることができます。システムのデフォルトのスタックは、すべてのスレッドの要求を満たします。
エラーが未定義です。
呼び出しスレッドの ID を取得するには、thr_self(3C) を使用します。POSIX スレッドの場合については、「pthread_self の構文」を参照してください。
#include <thread.h> thread_t thr_self(void);
エラーが未定義です。
thr_yield(3C) は、現在のスレッドから、同じ優先順位かより高い優先順位をもつ別のスレッドに実行を譲ります。thr_yield() には、それ以外の効果はありません。ただし、thr_yield() の呼び出しスレッドが実行権を必ず譲るとは限りません。
#include <thread.h> void thr_yield(void);
thr_yield() は何も返さず、errno を設定しません。
thr_kill(3C) は、スレッドにシグナルを送ります。POSIX スレッドの場合については、「pthread_kill の構文」を参照してください。
#include <thread.h> #include <signal.h> int thr_kill(thread_t target_thread, int sig);
thr_kill() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_kill() は失敗し、対応する値を返します。障害が発生したときには、シグナルは送信されません。
ESRCH
説明:スレッド ID (thread) で指定されたスレッドに関連付けられたスレッドがありません。
EINVAL
説明:sig 引数の値が 0 以外です。sig が無効であるか、サポートされていないシグナル番号です。
呼び出しスレッドのシグナルマスクを変更または検査するには、thr_sigsetmask(3C) を使用します。
#include <thread.h> #include <signal.h> int thr_sigsetmask(int how, const sigset_t *set, sigset_t *oset);
thr_sigsetmask() は、呼び出しスレッドのシグナルマスクを変更または検査します。各スレッドは、スレッド専用のシグナルマスクを持っています。新しいスレッドは、呼び出しスレッドのシグナルマスクと優先順位を継承します。ただし、保留中のシグナルは継承の対象にはなりません。保留中のシグナルは、新しいスレッドでは空になります。
引数 set の値が NULL 以外の場合、set は、現在ブロックされているシグナルセットを変更するシグナルセットになります。set の値が NULL の場合、how の値は意味を持たず、スレッドのシグナルマスクは変更されません。この動作を利用して、現在ブロックされているシグナルについて問い合わせることができます。
how の値には、セットの変更方法を指定します。how は、次のいずれかの値をとります。
SIG_BLOCK — set は、ブロックするシグナルセットになります。これらのシグナルは、現在のシグナルマスクに追加されます。
SIG_UNBLOCK — set は、ブロックを解除するシグナルセットになります。これらのシグナルは、現在のシグナルマスクから削除されます。
SIG_SETMASK — set は、新しいシグナルマスクになります。現在のシグナルマスクは、 set で置き換えられます。
thr_sigsetmask () は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_sigsetmask() は失敗し、対応する値を返します。
EINVAL
説明:set が NULL 以外なのに、how の値が未定義です。
スレッドを終了するには、thr_exit(3C) を使用します。POSIX スレッドの場合については、「pthread_exit Syntax」を参照してください。
#include <thread.h> void thr_exit(void *status);
ターゲットスレッドの終了を待つには、thr_join(3C) を使用します。POSIX スレッドの場合については、「pthread_join の構文」を参照してください。
#include <thread.h> int thr_join(thread_t tid, thread_t *departedid, void **status);
ターゲットスレッドは、現在のプロセスのメンバーでなければなりません。また、切り離されたスレッドやデーモンスレッドであってはなりません。
複数のスレッドが、同じスレッドの終了を待つことはできません。1 つのスレッドだけが正常に終了します。ほかのスレッドは、ESRCH エラーを発行して終了します。
thr_join() は、ターゲットスレッドがすでに終了している場合に、呼び出しスレッドの処理をブロックすることはありません。
#include <thread.h> thread_t tid; thread_t departedid; int ret; void *status; /* waiting to join thread "tid" with status */ ret = thr_join(tid, &departedid, &status); /* waiting to join thread "tid" without status */ ret = thr_join(tid, &departedid, NULL); /* waiting to join thread "tid" without return id and status */ ret = thr_join(tid, NULL, NULL);
tid が (thread_t) 0
の場合は、thr_join() はプロセス内の切り離されていない任意のスレッドの終了を待ちます。つまり、スレッド識別子を指定しなければ、切り離されていないスレッドのどれかが終了すると thr_join() が復帰します。
#include <thread.h> thread_t tid; thread_t departedid; int ret; void *status; /* waiting to join any non-detached thread with status */ ret = thr_join(0, &departedid, &status);
thr_join() でスレッド識別子としてゼロを指定すると、プロセス内の切り離されていない任意のスレッドの終了を待ちます。departedid には、終了したスレッドのスレッド識別子が格納されます。
thr_join() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_join() は失敗し、対応する値を返します。
ESRCH
説明:ターゲットスレッド ID に対応する、切り離されていないスレッドが見つかりません。
EDEADLK
説明:デッドロックが検出されたか、ターゲットスレッドの値に呼び出しスレッドが指定されています。
thr_keycreate(3C) は、プロセス内のスレッド固有データを識別するために使用されるキーを割り当てます。このキーは、プロセス内のすべてのスレッドに大域的なキーです。各スレッドは、このキーの生成時に値を 1 つずつ割り当てます。
スレッド固有のデータは、POSIX スレッドの場合も Solaris スレッドの場合も、関数名と引数を除いて同じです。この節では、Solaris の関数の概要を説明します。POSIX スレッドの場合については、「pthread_key_create の構文」を参照してください。
#include <thread.h> int thr_keycreate(thread_key_t *keyp, void (*destructor) (void *value));
keyp は、各結合スレッド固有の値を個別に管理します。各スレッドは、スレッド固有のデータへのアクセスを許可するために、最初に固有要素の keyp に結合されます。キーが生成されるときに、すべてのアクティブなスレッドに対して、新しいキーに値 NULL が割り当てられます。また、スレッドが生成されるときに、新しいスレッドのすでに生成されているすべてのキーには、値 NULL が割り当てられます。
オプションの destructor 関数は、各 keyp に関連付けることができます。スレッドが終了するときに、keyp に NULL 以外 destructor が関連付けられ、そのスレッドの keyp に NULL 以外の value が関連付けられている場合には、現在関連付けられている value を使用して destructor が呼び出されます。スレッドが終了するときに 1 つ以上の destructor が存在する場合には、デストラクタは不特定に呼び出されます。
thr_keycreate() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_keycreate() は失敗し、対応する値を返します。
EAGAIN
説明:追加のスレッド固有のデータキーを生成するために必要なリソースがありません。または、キーの数が、PTHREAD_KEYS_MAX に指定されたプロセスごとの最大キー数を超えています。
ENOMEM
説明:value と keyp を関連付けるために必要なメモリーが不足しています。
thr_setspecific(3C) は、呼び出しスレッドに対して、スレッド固有データキー key に value を結合します。POSIX スレッドの場合については、「pthread_setspecific の構文」を参照してください。
#include <thread.h> int thr_setspecific(thread_key_t key, void *value);
thr_setspecific() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_setspecific () は失敗し、対応する値を返します。
ENOMEM
説明:value と keyp を関連付けるために必要なメモリーが不足しています。
EINVAL
説明:keyp が無効です。
thr_getspecific(3C) は、呼び出しスレッドの key に結合された現在の値を valuep が指す場所に格納します。POSIX スレッドの場合については、「pthread_getspecific の構文」を参照してください。
#include <thread.h> int thr_getspecific(thread_key_t key, void **valuep);
thr_getspecific() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_getspecific () は失敗し、対応する値を返します。
ENOMEM
説明:value と keyp を関連付けるために必要なメモリーが不足しています。
EINVAL
説明:keyp が無効です。
Solaris スレッドでは、優先順位が親と異なるスレッドを生成する場合、SUSPEND モードで生成します。スレッドの優先順位は、停止している間に thr_setprio(3C) 関数を呼び出して変更されます。thr_setprio() が終了すると、スレッドは実行を再開します。
優先順位の高いスレッドは、同期オブジェクトの競合を考慮しながら、優先順位の低いスレッドより優先されます。
thr_setprio(3C) は、tid に指定されたスレッドの優先順位を、現在のプロセス内で newprio に指定された優先順位に変更します。POSIX スレッドの場合については、「pthread_setschedparam の構文」を参照してください。
#include <thread.h> int thr_setprio(thread_t tid, int newprio)
スレッドの有効な優先順位の範囲は、スケジューリングポリシーによって異なります。
thread_t tid; int ret; int newprio = 20; /* suspended thread creation */ ret = thr_create(NULL, NULL, func, arg, THR_SUSPENDED, &tid); /* set the new priority of suspended child thread */ ret = thr_setprio(tid, newprio); /* suspended child thread starts executing with new priority */ ret = thr_continue(tid);
thr_setprio() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_setprio() は失敗し、対応する値を返します。
ESRCH
説明:tid で指定した値が既存のスレッドを表していません。
EINVAL
説明:priority の値は、指定されたスレッドのスケジューリングポリシーには無効です。
EPERM
説明:呼び出し元は、優先順位を指定された値に設定するための適切なアクセス権を持っていません。
スレッドの現在の優先順位を取得するには、thr_getprio(3C) を使用します。各スレッドは生成側の優先順位を継承します。thr_getprio() は、tid で指定されたスレッドの現在の優先順位を、newprio が指している位置に格納します。POSIX スレッドの場合については、「pthread_getschedparam の構文」を参照してください。
#include <thread.h> int thr_getprio(thread_t tid, int *newprio)
thr_getprio() は、正常終了時に 0 を返します。次の条件が検出された場合、thr_getprio() は失敗し、対応する値を返します。
ESRCH
説明:tid で指定した値が既存のスレッドを表していません。
mutex の初期化
mutex の削除
mutex の獲得
mutex の解除
mutex の獲得の試行
mp が指す mutex を初期化するには、mutex_init(3C) を使用します。POSIX スレッドの場合については、「mutex の初期化」を参照してください。
#include <synch.h> #include <thread.h> int mutex_init(mutex_t *mp, int type, void *arg));
type は、次のいずれかになります。
USYNC_PROCESS — mutex を使って、このプロセス内のスレッドとほかのプロセス内のスレッドとの間で同期をとることができます。arg は無視されます。
USYNC_PROCESS_ROBUST — mutex を使って、このプロセス内のスレッドとほかのプロセス内のスレッドとの間で確実に同期をとることができます。arg は無視されます。
USYNC_THREAD — mutex を使って、このプロセス内のスレッドの間でだけ同期をとることができます。arg は無視されます。
USYNC_PROCESS ロックした状態でプロセスが異常終了すると、次にそのロックを要求したスレッドは滞ります。これは、クライアントプロセスとロックを共有するシステムで起こる問題で、クライアントプロセスが強制的に終了されることがあり得るからです。ロックしたままプロセスが終了する問題を回避するには、USYNC_PROCESS_ROBUST で mutex をロックします。USYNC_PROCESS_ROBUST には次の 2 つの機能があります。
プロセスが終了するときに、そのプロセスで獲得されたロックをすべて解除します。
異常終了したプロセスが獲得したロックを次に要求するスレッドは、そのロックを受け取ります。このロックを受け取るときに、前にロックを獲得していたスレッドがロックしたまま終了したことを示すエラーコードも受け取ります。
0 に初期化されたメモリーに領域を確保することによって mutex を初期化することもできます。その場合は type に USYNC_THREAD を指定したものと仮定されます。
複数のスレッドから同じ mutex を同時に初期化してはいけません。相互排他ロックは、ほかのスレッドで使われている可能性があるので再初期化してはいけません。
#include <thread.h> mutex_t mp; int ret; /* to be used within this process only */ ret = mutex_init(&mp, USYNC_THREAD, 0);
#include <thread.h> mutex_t mp; int ret; /* to be used among all processes */ ret = mutex_init(&mp, USYNC_PROCESS, 0);
#include <thread.h> mutex_t mp; int ret; /* to be used among all processes */ ret = mutex_init(&mp, USYNC_PROCESS_ROBUST, 0);
mutex_init() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、mutex_init() は失敗し、対応する値を返します。
EFAULT
説明:mp が指すアドレスが正しくありません。
EINVAL
説明:mpで指定された値が無効です。
ENOMEM
説明:mutex の初期化に必要なメモリーが不足しています。
EAGAIN
説明:mutex の初期化に必要なリソースが不足しています。
EBUSY
説明:アクティブな mutex の再初期化を試行していることが検出されました。
mp が指す mutex に関連付けられた状態を削除するには、mutex_destroy(3C) を使用します。mutex の記憶領域は解放されません。POSIX スレッドの場合については、「pthread_mutex_destroy の構文」を参照してください。
#include <thread.h> int mutex_destroy (mutex_t *mp);
mutex_destroy() は、正常終了時に 0 を返します。次の条件が検出された場合、mutex_destroy() は失敗し、対応する値を返します。
EFAULT
説明:mp が指すアドレスが正しくありません。
mp が指す mutex をロックするには、mutex_lock(3C) を使用します。mutex がすでにロックされている場合、呼び出しスレッドは mutex が使用可能になるまでブロックされます。ブロックされたスレッドは、優先順位の高いキューで待機します。POSIX スレッドの場合については、「pthread_mutex_lock の構文」を参照してください。
#include <thread.h> int mutex_lock(mutex_t *mp);
mutex_lock() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、mutex_lock() は失敗し、対応する値を返します。
EFAULT
説明:mp が指すアドレスが正しくありません。
EDEADLK
説明:mutex はすでにロックされていて、呼び出しスレッドに保持されています。
mp が指す mutex のロックを解除するには、mutex_unlock(3C) を使用します。この mutex はロックされていなければなりません。呼び出しスレッドは、最後にこの mutex をロックしたスレッド、すなわち所有者でなければなりません。POSIX スレッドの場合については、「pthread_mutex_unlock の構文」を参照してください。
#include <thread.h> int mutex_unlock(mutex_t *mp);
mutex_unlock() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、mutex_unlock() は失敗し、対応する値を返します。
EFAULT
説明:mp が指すアドレスが正しくありません。
EPERM
説明:呼び出しスレッドは mutex を所有していません。
mp が指す mutex のロックを試行するには、mutex_trylock(3C) を使用します。この関数はブロックしない点を除いて、mutex_lock() と同じ働きをします。POSIX スレッドの場合については、「pthread_mutex_trylock の構文」を参照してください。
#include <thread.h> int mutex_trylock(mutex_t *mp);
mutex_trylock() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、mutex_trylock() は失敗し、対応する値を返します。
EFAULT
説明:mp が指すアドレスが正しくありません。
EBUSY
説明:アクティブな mutex の再初期化を試行していることが検出されました。
条件変数の初期化
条件変数の削除
条件変数によるブロック
条件変数による指定時刻付きブロック
時間間隔によるブロック
単一スレッドのブロック解除
全スレッドのブロック解除
cv が指す条件変数を初期化するには、cond_init(3C) を使用します。
#include <thread.h> int cond_init(cond_t *cv, int type, int arg);
type は、次のいずれかになります。
USYNC_PROCESS。条件変数を使って、このプロセス内のスレッドとほかのプロセス内のスレッドとの間で同期をとることができます。arg は無視されます。
USYNC_THREAD。条件変数を使って、このプロセス内のスレッドの間だけで同期をとることができます。arg は無視されます。
0 に初期化されたメモリーに領域を確保することによって、条件変数を初期化することもできます。その場合は、type に USYNC_THREAD を指定したものと仮定されます。
複数のスレッドから、同じ条件変数を同時に初期化してはいけません。一度初期化した条件変数はほかのスレッドが使用している可能性があるので、再初期化してはいけません。
POSIX スレッドの場合については、「pthread_condattr_init の構文」を参照してください。
#include <thread.h> cond_t cv; int ret; /* to be used within this process only */ ret = cond_init(cv, USYNC_THREAD, 0);
#include <thread.h> cond_t cv; int ret; /* to be used among all processes */ ret = cond_init(&cv, USYNC_PROCESS, 0);
cond_init() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、cond_init() は失敗し、対応する値を返します。
EFAULT
説明:cv が指すアドレスが正しくありません。
EINVAL
説明:type を認識できません。
cv が指す条件変数に関連付けられた状態を削除するには、cond_destroy(3C) を使用します。条件変数を格納する領域は解放されません。POSIX スレッドの場合については、「pthread_condattr_destroy の構文」を参照してください。
#include <thread.h> int cond_destroy(cond_t *cv);
cond_destroy() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、cond_destroy() は失敗し、対応する値を返します。
EFAULT
説明:cv が指すアドレスが正しくありません。
EBUSY
説明:アクティブな条件変数の削除を試行していることが検出されました。
mp が指す mutex を不可分的に解放することにより、cv が指す条件変数上で呼び出しスレッドをブロックするには、cond_wait(3C) を使用します。ブロックされたスレッドを呼び起こすには、cond_signal() か cond_broadcast() を使います。また、スレッドはシグナルや fork() の割り込みによっても呼び起こすことができます。
cond_wait() が戻るときは、たとえエラーを戻したときでも、常に mutex は呼び出しスレッドがロックし保持している状態にあります。
#include <thread.h> int cond_wait(cond_t *cv, mutex_t *mp);
cond_wait() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、cond_wait() は失敗し、対応する値を返します。
EFAULT
説明:cv が指すアドレスが正しくありません。
EINTR
説明:シグナルにより、待機中に割り込みが発生しました。
cond_timedwait(3C) は、cond_wait() と非常によく似ています。ただし、cond_timedwait() は、abstime で指定された時刻を過ぎるとブロック状態を解除します。POSIX スレッドの場合については、「pthread_cond_timedwait の構文」を参照してください。
#include <thread.h> int cond_timedwait(cond_t *cv, mutex_t *mp, timestruct_t abstime);
cond_timedwait() が戻るときは、たとえエラーを戻したときでも、常に mutex は呼び出しスレッドがロックし保持している状態にあります。
cond_timedwait() のブロック状態が解除されるのは、条件変数にシグナルが送られたときか、最後の引数で指定した時刻を過ぎたときです。時間切れの指定は時刻で行うため、時間切れの時刻を再計算する必要がないので、効率的に条件を再評価できます。
cond_timedwait() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、cond_timedwait() は失敗し、対応する値を返します。
EFAULT
説明:cv が指すアドレスが正しくありません。
ETIME
説明:abstime で指定された時刻を過ぎています。
EINVAL
説明:abstime が無効です。
cond_reltimedwait(3C) は、3 番目の引数の値を除いて、cond_timedwait() と非常によく似ています。cond_reltimedwait() の 3 番目の引数には、絶対的な時刻ではなく、相対的な時間間隔値を指定します。POSIX スレッドの場合については、pthread_cond_reltimedwait_np(3C) のマニュアルページを参照してください。
cond_reltimedwait() が戻るときは、たとえエラーを戻したときでも、常に mutex は呼び出しスレッドがロックし保持している状態にあります。cond_reltimedwait() のブロック状態が解除されるのは、条件変数にシグナルが送られたときか、最後の引数で指定した時間間隔が経過したときです。
#include <thread.h> int cond_reltimedwait(cond_t *cv, mutex_t *mp, timestruct_t reltime);
cond_reltimedwait() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、cond_reltimedwait () は失敗し、対応する値を返します。
EFAULT
説明:cv が指すアドレスが正しくありません。
ETIME
説明:reltime で指定された時間を過ぎています。
cv が指す条件変数でブロックされている 1 つのスレッドのブロックを解除するには、cond_signal(3C) を使用します。スレッドがブロックされていない条件変数に対して cond_signal() を実行しても無視されます。
#include <thread.h> int cond_signal(cond_t *cv);
cond_signal() は、正常終了時に 0 を返します。次の条件が検出された場合、cond_signal() は失敗し、対応する値を返します。
EFAULT
説明:cv が指すアドレスが正しくありません。
cv が指す条件変数でブロックされているすべてのスレッドのブロックを解除するには、cond_broadcast(3C) を使用します。スレッドがブロックされていない条件変数に対して cond_broadcast() を実行しても無視されます。
#include <thread.h> int cond_broadcast(cond_t *cv);
cond_broadcast() は、正常終了時に 0 を返します。次の条件が検出された場合、cond_broadcast() は失敗し、対応する値を返します。
EFAULT
説明:cv が指すアドレスが正しくありません。
セマフォーの操作は Solaris オペレーティング環境と POSIX 環境の両方で同じです。関数名は、Solaris オペレーティング環境で sema_ だった関数名が pthread では sem_ に変わっています。この節の内容は次のとおりです。
セマフォーの初期化
セマフォーの加算
セマフォーの値によるブロック
セマフォーの減算
セマフォーの削除
sp が指すセマフォー変数を count の値で初期化するには、sema_init(3C) を使用します。
#include <thread.h> int sema_init(sema_t *sp, unsigned int count, int type, void *arg);
type は、次のいずれかになります。
USYNC_PROCESS。セマフォーを使って、このプロセス内のスレッドとほかのプロセス内のスレッドとの間で同期をとることができます。ただし、セマフォーを初期化するプロセスは 1 つだけに制限してください。arg は無視されます。
USYNC_THREAD。セマフォーを使って、このプロセス内のスレッドの間でだけ同期をとることができます。arg は無視されます。
複数のスレッドから同時に同じセマフォーを初期化してはいけません。一度初期化したセマフォーは、ほかのスレッドが使用している可能性があるので、再初期化してはいけません。
#include <thread.h> sema_t sp; int ret; int count; count = 4; /* to be used within this process only */ ret = sema_init(&sp, count, USYNC_THREAD, 0);
#include <thread.h> sema_t sp; int ret; int count; count = 4; /* to be used among all the processes */ ret = sema_init (&sp, count, USYNC_PROCESS, 0);
sema_init() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、sema_init() は失敗し、対応する値を返します。
EINVAL
説明:sp が無効なセマフォーを参照しています。
EFAULT
説明:sp または arg が無効なアドレスを指しています。
sp が指すセマフォーを不可分的に加算するには、sema_post(3C) を使用します。そのセマフォーでブロックされているスレッドがある場合は、そのスレッドのうちの 1 つのスレッドがブロック解除されます。
#include <thread.h> int sema_post(sema_t *sp);
sema_post() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、sema_post() は失敗し、対応する値を返します。
EINVAL
説明:sp が無効なセマフォーを参照しています。
EFAULT
説明:sp が指すアドレスが正しくありません。
EOVERFLOW
説明:sp が指すセマフォーの値が SEM_VALUE_MAX を超えています。
sp が指すセマフォー内のカウントが 0 より大きくなるまで呼び出しスレッドをブロックするには、sema_wait(3C) を使用します。カウントが 0 より大きくなると、不可分的に減算します。
#include <thread.h> int sema_wait(sema_t *sp);
sema_wait() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、sema_wait() は失敗し、対応する値を返します。
EINVAL
説明:sp が無効なセマフォーを参照しています。
EINTR
説明:シグナルにより、待機中に割り込みが発生しました。
カウントが 0 より大きくなったとき、sp が指すセマフォー内のカウントを不可分的に減算するには、sema_trywait(3C) を使用します。この関数はブロックしない点を除いて、sema_wait() と同じ働きをします。
#include <thread.h> int sema_trywait(sema_t *sp);
sema_trywait() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、sema_trywait() は失敗し、対応する値を返します。
EINVAL
説明:sp が無効なセマフォーを参照しています。
EBUSY
説明:sp が指すセマフォーのカウントが 0 です。
sp が指すセマフォーに関連付けられた状態を削除するには、sema_destroy(3C) を使用します。セマフォーの記憶領域は解放されません。
#include <thread.h> int sema_destroy(sema_t *sp);
sema_destroy() は、正常終了時に 0 を返します。次の条件が検出された場合、sema_destroy() は失敗し、対応する値を返します。
EINVAL
説明:sp が無効なセマフォーを参照しています。
今までに説明した 4 種類の同期プリミティブは、プロセスの境界を越えて使用するように設定できます。この境界を越えた設定を行うには、まず、その同期変数が共有メモリーセグメントに確保されるようにします。次に、USYNC_PROCESS 型に設定された適切な初期化ルーチン (init) を呼び出します。
型が USYNC_PROCESS に設定されている場合、同期変数の操作は、type が USYNC_THREAD であるときと同様に行われます。
mutex_init(&m, USYNC_PROCESS, 0); rwlock_init(&rw, USYNC_PROCESS, 0); cond_init(&cv, USYNC_PROCESS, 0); sema_init(&s, count, USYNC_PROCESS, 0);
例 6–2 に、「生産者 / 消費者」問題の生産者と消費者をそれぞれ別のプロセスで示します。メインルーチンは、子プロセスと共有されているメモリーを 0 で初期化し、それを自分のアドレス空間にマッピングします。mutex_init() と cond_init() を呼び出さなければならないのは、それらの同期変数のタイプが USYNC_PROCESS だからです。
子プロセスが 1 つ生成され、消費者の処理が実行されます。親プロセスは生産者の処理を実行します。
この例では、生産者と消費者を呼び出す各駆動ルーチンも示しています。producer_driver は、stdin から文字を読み込み、producer を呼び出します。consumer_driver は、consumer を呼び出して文字を受け取り、stdout に書き出します。
例 6–2 のデータ構造は、条件変数によるソリューションで使用したものと同じです。「片方向リンクリストの入れ子のロック」を参照してください。
main() { int zfd; buffer_t *buffer; zfd = open(“/dev/zero”, O_RDWR); buffer = (buffer_t *)mmap(NULL, sizeof(buffer_t), PROT_READ|PROT_WRITE, MAP_SHARED, zfd, 0); buffer->occupied = buffer->nextin = buffer->nextout = 0; mutex_init(&buffer->lock, USYNC_PROCESS, 0); cond_init(&buffer->less, USYNC_PROCESS, 0); cond_init(&buffer->more, USYNC_PROCESS, 0); if (fork() == 0) consumer_driver(buffer); else producer_driver(buffer); } void producer_driver(buffer_t *b) { int item; while (1) { item = getchar(); if (item == EOF) { producer(b, `\0'); break; } else producer(b, (char)item); } } void consumer_driver(buffer_t *b) { char item; while (1) { if ((item = consumer(b)) == '\0') break; putchar(item); } }
子プロセスが 1 つ生成され、消費者の処理が実行されます。親プロセスは生産者の処理を実行します。
Solaris 10 リリースより前のリリースでは、Solaris スレッドと POSIX スレッドによる fork() の定義が異なっていました。fork() の問題の詳細は、「プロセスの作成: exec と exit の問題」を参照してください。
Solaris libthread は、fork() と fork1() の両方をサポートしていました。fork() 呼び出しには、「汎用 fork」セマンティクスがあります。fork() は、スレッド、LWPを含めて、プロセス内のすべてを複製し、親の完全なクローンを生成しました。一方、fork1() 呼び出しで作成されるクローンはスレッドを 1 つしかもちませんでした。プロセスの状態とアドレス空間は複製されますが、スレッドについては呼び出しスレッドが複製されるだけでした。
POSIX libpthread は 、fork() のみをサポートしていました。そのセマンティクスは、Solaris スレッドにおける fork1() と同じです。
fork() のセマンティクスが「汎用 fork」と「fork1」のどちらになるかは、どちらのライブラリを使用するかで決まりました。-lthread を使ってリンクすれば、fork() に「汎用 fork」セマンティクスが割り当てられます。-lpthread を使ってリンクすれば、fork() に「fork1」セマンティクスが割り当てられます。
Solaris 10 リリースから、Solaris スレッドと POSIX スレッドの fork() のセマンティクスが共通になりました。つまり、 fork1() セマンティクスが呼び出し側だけを複製するようになりました。すべてを複製するセマンティクスが必要なアプリケーションには、新しい関数 forkall () が提供されています。
詳細は、「マルチスレッドプログラムのコンパイルとリンク」を参照してください。