select()、pselect() - ファイルまたはソケットおよびメッセージ・キューに関するアクティビティーのモニター
標準
| 標準/拡張機能 | C/C++ | 依存項目 |
|---|---|---|
XPG4.2
Single UNIX Specification、バージョン 3 |
両方 |
形式
#define _XOPEN_SOURCE_EXTENDED 1
#define _OPEN_MSGQ_EXT
#include <sys/types.h>
#include <sys/time.h>
#include <sys/msg.h>
int select(int nmsgsfds, fd_set *__restrict__ readlist,
fd_set *__restrict__ writelist, fd_set *__restrict__ exceptlist,
struct timeval *__restrict__ timeout);#define _POSIX_C_SOURCE 200112L
#include <sys/select.h>
int pselect(int nmsgsfds, fd_set *__restrict__ readlist,
fd_set *__restrict__ writelist, fd_set *__restrict__ exceptlist,
const struct timespec *__restrict__ timeout,
const sigset *__restrict__ sigmask);#define _OE_SOCKETS
#define _OPEN_MSGQ_EXT
#include <sys/types.h>
#include <sys/time.h>
#include <sys/msg.h>
int select(int nmsgsfds, fd_set *readlist,
fd_set *writelist, fd_set *exceptlist,
struct timeval *timeout);メッセージ・キューを監視する場合 (X/Open ソケットのみ) は、_OPEN_MSGQ_EXT を定義する必要があります。
機能説明
pselect() および select() 関数は、ソケットおよびメッセージ・キューのいずれかで、読み取り、書き込みが行われなかったかを調べ、あるいは保留中の例外処理条件を調べるために、タイムアウトが起こるまで、ソケットのセットのアクティビティーまたはメッセージ・キュー ID のセットのアクティビティー (あるいはその両方) をモニターします。またこの呼び出しは、通常のファイル記述子、パイプ、および端末を指定しても 機能します。
select() 関数は、以下の点を除いて pselect() 関数と同じです。
- select() 関数では、タイムアウト期間は struct timeval 型の引数に秒およびマイクロ秒で与えられますが、pselect() 関数では、タイムアウト期間は struct timespec 型の引数に秒およびナノ秒で与えられます。
- select() 関数には sigmask 引数がなく、sigmask が NULL ポインターの時に pselect() が行うような動作をします。
- 実行が正常に完了すると、select() 関数は timeout 引数が指したオブジェクトを変更できます。
- pselect() 関数は、常に _OPEN_MSGQ_EXT および _OPEN_SYS_HIGH_DESCRIPTORS フィーチャー・テスト・マクロが定義されていないような動作をします。
- パラメーター
- 説明
- nmsgsfds
- 検査を行うメッセージ・キュー数およびファイルまたはソケット記述子数
このパラメーターは、2 つの部分に分かれます。最初の半分 (高位 16 ビット) は、メッセージ・キュー ID を含む配列のエレメント数を 指定します。この数は、値 32767 を超えてはなりません。
後半 (下位 16 ビット) は、検査を行うファイルまたはソケット記述子に 対応するビット・セット内のビット数を指定します。この値は、+ 1 の検査を行う最大記述子数と等しくする必要があります。
どちらの部分の nmsgsfds パラメーターも値 0 と等しい場合には、対応するビット・セットまたは配列が存在していないと想定されます。
_OPEN_MSGQ_EXT が定義されていない場合は、ファイルまたはソケット記述子だけしか監視されないことがあります。この場合、nmsgsfds を FD_SETSIZE (sys/time.h で、2048 に 定義されている) より小さいか等しくて、ゼロより大きいか等しくする必要があります。 また、FD_SETSIZE はユーザーのプログラムで定義できません。
ファイルまたはソケット記述子を指定するのに使用されるビット・セットは、可能なすべてのファイルまたはソケットについて 1 ビットのサイズに固定されます。nmsgsfds パラメーターを使用して、pselect() または select() は、割り振られたビット・セットのサブセットだけを強制的に検査します。
アプリケーションでソケット 3、4、5、6、および 7 が割り振られ、すべての 割り振りを検査したい場合には、nmsgsfds の後半部分は指定 した最高位記述子 7 に 1 をプラスした 8 に設定する必要があります。アプリケーションでソケット 3、4 を検査する場合は、nmsgsfds の後半部分は 5 に設定する必要があります。
2048 から 65534 の間の記述子番号について選択するには、 _OPEN_MSGQ_EXT または _OPEN_SYS_HIGH_DESCRIPTORS フィーチャー・テスト・マクロが 定義されている必要があり、デフォルトのサイズより大きいビット・セットを使用しな ければなりません。 メッセージ・キューについても選択する場合 (_OPEN_MSGQ_EXT が定義されていると きに可能)、最大の記述子番号は 2047 に制限されます。 65535 から 524287 の間の記述子番号について選択するには、フィーチャー・テス ト・マクロ _OPEN_SYS_HIGH_DESCRIPTORS が定義されている必要があり、フィーチャー ・テスト・マクロ _OPEN_MSGQ_EXT は定義されていてはなりません。また、プロセスの MAXFILEPROC の限界は 65536 より大きいことが必要です。このフィーチャーがあれば、(メッセージ・キューなしで) いくつのソケットについ てでも選択できます。 この場合には、FD_SETSIZE も再定義できますが、アプリケーション で malloc() を使って、それより大きいビット・セットを割り振るようにお勧めします。
- readlist,writelist,exceptlist
- fd_set 型のメッセージ・キュー ID の配列、または sellist 構造体への
ポインター (それぞれに、読み取り、書き込み、および例外条件の検査を行う)。渡すパラメーターの型は、監視する対象が、ファイル/ソケット記述子、メッセージ・キュー ID、またはその両方であるかどうかによって決まります。監視する対象がファイル/ソケット記述子だけの場合には、nmsgsfds の
高位ハーフワードを 0 に、下位ハーフワードを (最高位記述子数 + 1) に設定し、fd_set ポインターを使用します。メッセージ・キューだけを監視するには、nmsgsfds の下位ハーフワードを 0 に、高位ハーフワードを select() を検討したい各配列のエレメント数に
設定し、ポインターをメッセージ・キュー ID の配列に渡します。両方を監視するには、上記のように、nmsgsfds を設定し、ポインターを sellist 構造体に渡します。sellist 構造体を使用すると、ファイル/ソケット記述子とメッセージ ・キューの両方を指定できるようになります。sellist 構造体を、プログラムにより、以下の形式で定義する必要が あります。
struct sellist { fd_set fdset; /* file/socket descriptor bit set */ int msgids[max_size]; /* array of message queue identifiers */ };sellist 構造体を使用している場合に、監視できる最高位記述子は 2047 です。
以下で、型 fd_set の説明をします。msgids 配列の各整数により、状況を検査するメッセージ・キュー ID が 指定されます。-1 の値のあるエレメントは受け入れ可能で、無視されることになります。前半部分の nmsgsfds に含まれる値によって、検査される配列の エレメント数が正確に判別されます。
- timeout
- pselect() または select() 呼び出しの完了を待機する時間へのポインター。
- sigmask
- 記述子が試行される前の sigmask が指すシグナルのセットによる呼び出し元のシグナル・マスクで、戻る前に呼び出しスレッドのシグナル・マスクを保管します。
timeout が NULL ポインターではない場合には、選択が完了するのを待機する最大インターバルが指定されます。最大タイムアウト値は、31 日です。timeout が NULL ポインターの場合には、ソケットまたはメッセージが 作動可能になるまで、pselect() および select() 呼び出しがブロックします。ソケットをポーリングし、即時に戻すためには、timeout がゼロ値 timeval 構造体または timespec 構造体の NULL 以外のポインターであることが必要です。
sigmask が NULL ポインターでない場合、pselect() 関数は記述子が試行される前の sigmask が指すシグナルのセットによる呼び出し元のシグナル・マスクを置き換え、戻る前に呼び出しスレッドのシグナル・マスクを保管します。
一度に複数のソケットをテストできるように、テストするソケットは、fd_set 型のビット・セット中に入れられます。ビット・セットは、x がセットのエレメントの場合に、x を表すビットは 1 に設定されるような、ビットのストリングです。x がセットのエレメントでない場合は、 x を表すビットは 0 に設定されます。例えば、ソケット 33 がビット・セットのエレメントである場合には、ビット 33 を 1 に設定します。ソケット 33 がビット・セットのエレメントでない場合には、ビット 33 を 0 に設定します。
- pselect()、select() または selectex() 呼び出しの前に、nmsgsfds に設定されているビット・セット・サイズが FD_SETSIZE より大きいかどうかを調べるための検査を追加します。
- コンパイル時に作成された静的ビット・ストリングに依存するのでは なく、アプリケーション・プログラムの最大記述子の値を保持 できる十分な大きさのビット・ストリングを動的に割り振ります。ユーザー自身のビット・ストリングの割り振り時に、malloc() を 使用して、各ビットを表すのに十分な大きさのエリアを定義 してください (次の 4 バイト倍数に切り上げ)。例えば、最大記述子値が 31 の場合には、4 バイトが必要です。最大記述子値が 32 の場合には、8 バイトが必要です。
- ユーザー自身のビット・ストリングを動的に割り振ると、FD_ZERO() マクロは動作しません。アプリケーションでは、memset 関数、すなわち、memset(ptr,0,mallocsize) を使用して、そのストレージをゼロにする必要があります。その他のマクロは、操作中のビット記述子がビット・ストリング内にある限り、動的に 割り振られたビット・ストリングで使用できます。記述子数がビット・ストリングより大きいと、予測不可能な結果が起こる場合があります。
アプリケーション・プログラムは、パラメーター readlist、writelist、および exceptlist が、パラメーター nmsgsfds の大きさのビット・ストリングを指していることを確認する必要があります。z/OS UNIX サービスは、ビット・ストリングのそれぞれについて、ビット 0 ~ n-1 (この場合、n は nmsgsfds の 2 番目のハーフワード) のアクセスを試みます。ビット・ストリングが短すぎると、アプリケーション・プログラムの実行時に、予測できない結果を受け取ることになります。
- マクロ
- 説明
- FD_ZERO(&fdset)
- ビット・セット fdset のすべてのビットをゼロに設定します。この操作の後では、ソケットはビット・セットにエレメントとして含まれません。このマクロが呼び出されるのは、ソケットをメンバーとして設定する FD_SET() を呼び出す前に、ビット・セットを初期化する場合です。注: malloc() を使用して動的に新しいエリアを割り振る場合、FD_ZERO() マクロにより予測できない結果となることがあるので、使用しないでください。memset() 関数を使用するエリアをゼロにする必要があります。
- FD_SET(sock, &fdset)
- ソケット sock のビットを 1 に設定し、sock を ビット・セット fdset のメンバーにします。
- FD_CLR(sock, &fdset)
- ビット・セット fdset のソケット sock のビットを クリアします。この操作により、該当するビットがゼロに設定されます。
- FD_ISSET(sock, &fdset)
- sock が、ビット・セット fdset のメンバーの場合には、ゼロ以外を戻します。 sock が fdset のメンバーではない場合には、0 を戻します (この操作により、sock を表すビットが戻されます)。
- マクロ
- 説明
- _SET_FDS_MSGS(nmsgsfds, nmsgs, nfds)
- nmsgsfds の高位ハーフワードを nmsgs に設定し、nmsgsfds の下位ハーフワードを nfds に設定します。
- _NFDS(n)
- pselect() または select() からの戻り値 n が負ではない場合には、読み取り、書き込み、および例外基準を満たす記述子数が戻されます。記述子が複数の指定基準に該当する場合、その記述子は複数回カウントされる場合があります。
- _NMSGS(n)
- pselect() または select() からの戻り値 n が負ではない場合には、読み取り、書き込み、および例外基準を満たすメッセージ・キュー数が戻されます。メッセージ・キューが複数の指定基準に該当する場合、そのメッセージ・キューは複数回カウントされる場合があります。
ソケットは、着信データがそのためにバッファーに入れられるか、あるいは 接続要求が保留中のときに、読み取りの作動が可能になります。ソケットのいずれかが読み取り作動可能になっているかどうかをテストす るには、関数が動的に割り振られていた場合は、FD_ZERO() または memset() を使って readlist の fdset ビット・セットを 初期化して、テストする各ソケットごとに FD_SET() を呼び出します。
発信データ用のバッファー・スペースがある場合には、ソケットは書き込み可能です。ソケット上に、受信されるデータがあれば、ソケットは読み取り作動可能です。 接続プロセス中の非ブロッキング・ストリーム・ソケットの場合、connect() は -1 で戻ります。プログラムは、errno を検査する必要があります。 errno が EINPROGRESS の場合、connect() の完了時にソケットは書き込みのために選択されます。errno が EINPROGRESS ではない状態では、ソケットはさらに書き込み用に選択されますが、それは、ソケット上に未解決のエラーがあることを示します。データ量がバッファー・スペース量より少ないと、 write()、send()、または sendto() への呼び出しはブロックしません。ソケットが書き込み用に選択されている場合は、使用可能なバッファー・ス ペース容量は、getsockopt() で SO_SNDBUF を 使用して戻されるサイズ以上になるよう保証されています。ソケットのいずれかが書き込み作動可能状態かどうかをテストするには、関数が動的に割り振られていた場合は FD_ZERO() または memset() を使って writelist 内の fdset ビット・セットを初期化して、テストする各ソケットに対して FD_SET() を使用します。
メッセージ・キューは、メッセージがあるときならいつでも読み取り可能です。これは、満杯でないときならいつでも、書き込み可能です。メッセージ・キューが満杯なのは、メッセージ限界数に達したか、あるいは バイト数制限に達したときです。例外条件は、select() 呼び出し元がメッセージ・キューで待機中に このキューが削除される時に存在します。
プログラマーは、readlist、writelist、および exceptlist パラメーターのいずれかの NULL を渡すことができます。ただし、これらが NULL ではないときには、すべて同じ型の構造体を示す 必要があります。例えば、readlist で sellist が示されるとします。 writelist が NULL ではないときには、これで sellist も示す必要が あります。writelist が、NULL ではないとします。プログラマーが、読み取り状況のみについてファイル記述子のセットを検査する 場合には、writelist により示される sellist 構造体のビット・セットの、該当するビットを 0 に設定する必要があります。プログラマーが書き込み状況のみについて、メッセージ・キューのセットを検査する 場合には、readlist により示される sellist 構造体の配列の該当エレメント を -1 に設定する必要があります。通常ファイルは常に、読み取りおよび書き込み作動可能の状態です。
pselect() および select() に渡されたソケットのセットがビット・セットのため、その状況のソケットをポーリングする前に、pselect() および select() 呼び出しで、各ビット・セットのそれぞれのビットをテストする必要があります。pselect() および select() 呼び出しでテストされるのは、範囲が 0 から n-1 (ここで
は、n = nmsgsfds の 2 番目の
ハーフワードの値) のソケットだけです。
C++ の特殊な動作: この関数を C++ で使用するには、_XOPEN_SOURCE_EXTENDED 1 フィーチャー・テスト・マクロを使用する必要があります。
戻り値
値 -1 は、エラーについて調べる必要があるエラー・コードを示します。値ゼロは、時間制限が満了であることを示します。
戻り値が 0 より大きい場合には、高位 16 ビットで メッセージ・キューが指定され、下位 16 ビットで記述子数が指定されるので、この戻り値は nmsgsfds と類似しています。これらの値は、読み取り、書き込み、および例外のそれぞれの基準を満たす合計値を示します。記述子またはメッセージ・キューは、複数の指定基準を満たす場合には、複数回カウントされる場合があることに注意してください。メッセージ・キューの戻り値が値 32767 を超えていても、32767 だけが報告されることになります。これは、戻り値が負の値にならないようにするためです。ファイル/ソケット記述子の戻り値が 65535 を超えていても、65535 だけが報告されることになります。
- エラー・コード
- 説明
- EBADF
- ビット・セットの 1 つが無効ソケットを指定したか、あるいはメッセージ ・キュー ID が無効です。ソケットが設定される前に、ビット・セットをクリアする FD_ZERO() は呼び出されなかったと考えられます。
- EFAULT
- パラメーターの 1 つに、無効アドレスが含まれていました。
- EINTR
- 選択したイベントのいずれかが発生する前、およびタイムアウト・インターバルが終了する前に、pselect() または select() 関数に割り込みが入りました。
- EINVAL
- timeval 構造体または timespec 構造体のフィールドの 1 つが無効か、あるいは 無効 nmsgsfds 値がありました。
- EIO
- セレクト・マスク内の記述子の 1 つが作動不能になりました。 この記述子に対する他の操作が EIO で失敗しているにもかかわらず、この記述子は繰 り返してセレクトに組み込まれています。 例えば、ソケット記述子は TCP/IP がシャットダウンされれば作動不能になります。 セレクトからの失敗は、どの記述子に失敗が起きたのかは通知されません。 そのため、通常の場合、セレクトは成功し、これらの記述子はセレクト上にいかなるイベントが指定されていようと、作動可能と報告されます。その後に、その記述子が受信または他の操作に使用されたときに、 EIO の失敗が受信され、個々の記述子の問題に対処することができます。 一般的には、記述子をクローズして、それを次のセレクト・マスクから取り外します。呼び出されるたびに EIO で失敗するにもかかわらず、その個々の記述子の失敗の戻りコードが無視されて、作動不能の記述子が繰り返しセレクトされ使用されたなら、最終的にはセレクト呼び出し自体が EIO で失敗します。
例
#define _XOPEN_SOURCE_EXTENDED 1
#define _OPEN_MSGQ_EXT
#include <sys/types.h>
#include <sys/time.h>
#include <sys/msg.h>
struct sellist {
fd_set fdset;
int msgids[2];
};
/*
* sock_msg_stats(sr, sw, se, mr, mw, me) - Print the status of
* sockets sr, sw, and se, and of message queue ids mr, mw,
* and me.
*/
int sock_msg_stats(sr, sw, se, mr, mw, me)
int sr, sw, se, mr, mw, me;
{
struct sellist *reading, *writing, *excepting;
struct sellist read, write, except;
struct timeval timeout;
int rc, max_sock, sock_size, nmsgsfds;
int msgids[1]; /* we only check 1 message queue */
/* What's the maximum socket number? */
max_sock = MAX( sr, sw );
max_sock = MAX( max_sock, se );
/* initialize the static bit sets */
FD_ZERO( &read.fdset ); reading = &read;
FD_ZERO( &write.fdset ); writing = &write;
FD_ZERO( &except.fdset ); excepting = &except;
/* add sr, sw, and se to the appropriate bit set */
FD_SET( sr, &reading->fdset );
FD_SET( sw, &writing->fdset );
FD_SET( se, &excepting->fdset );
/* initialize the message id arrays */
reading->msgids[0] = mr;
writing->msgids[0] = mw;
excepting->msgids[0] = me;
/* set the nmsgsfds parameter */
_SET_FDS_MSGS( nmsgsfds, 1, max_sock+1 );
/* make select poll by sending a 0 timeval */
memset( &timeout, 0, sizeof(timeout) );
/* poll */
rc = select( nmsgsfds, reading, writing, excepting, &timeout);
if ( rc < 0 ) {
/* an error occurred during the SELECT() */
perror( "select" );
}
else if ( rc == 0 ) {
/* no sockets or messages were ready in our little poll */
printf( "nobody is home.¥n" );
} else
if (_NFDS(rc) > 0) {
/* at least one of the sockets is ready */
printf("sr is %s¥n",
FD_ISSET(sr,&reading->fdset) ? "READY" : "NOT READY");
printf("sw is %s¥n",
FD_ISSET(sw,&writing->fdset) ? "READY" : "NOT READY");
printf("se is %s¥n",
FD_ISSET(se,&excepting->fdset) ? "READY": "NOT READY");
} else
if (_NMSGS(rc) > 0) {
/* at least one message queue is ready */
printf("mr is %s¥n",
reading->msgids[0] == -1 ? "NOT READY" : "READY");
printf("mw is %s¥n",
writing->msgids[0] == -1 ? "NOT READY" : "READY");
printf("me is %s¥n",
excepting->msgids[0] == -1 ? "NOT READY" : "READY");
}
}CELEBP72
/* CELEBP72
This example demonstrates the use of pselect()
Expected output:
Parent: Issuing pselect
This is the child
Child: Sending signal to the parent at:
This is the signal handler
Signal received: 14 (14 is SIGALRM)
The pselect call was made at:
The SIGALRM was caught at:
TEST PASSED!
*/
#define _POSIX_C_SOURCE 200112L
#include <sys/select.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
time_t t1,t2;
void incatchr(int signum){
double diff=0;
time(&t2);
printf("¥n¥nThis is the signal handler¥n");
printf("Signal received: %d (14 is SIGALRM) ¥n",signum);
printf("The pselect call was made at: ¥t%s¥n",ctime(&t1));
printf("The SIGALRM was caught at: ¥t%s¥n",ctime(&t2));
diff = difftime(t2,t1);
if(diff < 10) {
printf("TEST FAILED!¥n¥n");
}
else{
printf("TEST PASSED!¥n¥n");
}
}
int main(void){
int fd[1], rc, nfds=3, fd1, fd2, fd3;
pid_t cpid, ppid;
fd_set fdsread;
struct sigaction action, info;
sigset_t pselect_set;
struct timespec t;
time_t t3;
t.tv_sec=10;
t.tv_nsec=0;
FD_ZERO(&fdsread);
action.sa_handler = incatchr;
action.sa_flags = 0;
sigaction(SIGALRM,&action,&info);
sigemptyset(&pselect_set);
sigaddset(&pselect_set, SIGALRM);
fd1 = open("./testchd.txt",O_RDWR|O_CREAT);
fd2 = open("./testchd2.txt",O_RDWR|O_CREAT);
if((rc=pipe(fd)) != 0){
printf("Error in pipe¥n");
return(-1);
}
FD_SET(fd[0],&fdsread);
if ((cpid = fork()) < 0){
printf("Fork error¥n");
return(-1);
}
else{
if (cpid == 0){
fd3 = open("./testchd.txt",O_RDWR|O_CREAT);
printf("This is the child¥n");
sleep(2);
ppid= getppid();
time(&t3);
printf("Child: Sending signal to the parent at: ");
printf("%s",ctime(&t3));
kill(ppid,SIGALRM);
sleep(3);
_exit(0);
}
else{
printf("Parent: Issuing pselect¥n¥n");
time(&t1);
if (pselect(nfds,&fdsread,NULL,NULL,&t,&pselect_set) == -1)
printf("Error in pselect¥n");
}
close(fd[0]);
}
return 0;
}