名 前
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO − 同 期 I/O の 多 重 化
書 式
/*
POSIX.1−2001 に 従 う 場
合 */
#include <sys/select.h>
/* 以
前 の 規 格 に 従
う 場 合 */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int
select(int nfds, fd_set
*readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval
*utimeout);
void
FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set
*set);
void FD_SET(int fd, fd_set
*set);
void FD_ZERO(fd_set *set);
#include <sys/select.h>
int
pselect(int nfds, fd_set
*readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec
*ntimeout,
const sigset_t *sigmask);
glibc 向 け の 機 能 検 査 マ ク ロ の 要 件 (feature_test_macros(7) 参 照 ):
pselect(): _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
説 明
select()
(や pselect()) を 使 う
と 、 効 率 的 に
複 数 の フ ァ イ
ル デ ィ ス ク リ
プ タ ー を 監 視
し 、 そ の フ ァ
イ ル デ ィ ス ク
リ プ タ ー の い
ず れ か が 「 ready (準
備 が で き た )」
状 態 、 つ ま り I/O
(入 出 力 ) が 可 能
に な っ て い る
か や 、 フ ァ イ
ル デ ィ ス ク リ
プ タ ー の い ず
れ か が 「 例 外
状 態 (exceptional condition)」 が
発 生 し た か 、
を 調 べ る こ と
が で き る 。 こ
の 関 数 の 主 要
な 引 き 数 は 、
3種 類 の フ ァ イ
ル デ ィ ス ク リ
プ タ ー の 「 集
合 」 readfds, writefds,
exceptfds で あ る 。
各 々 の 集 合 は
fd_set と し て 宣 言
さ れ 、 そ の 内
容 は FD_CLR(), FD_ISSET(),
FD_SET(), FD_ZERO() と い っ
た マ ク ロ に よ
っ て 操 作 で き
る 。 新 し く 宣
言 さ れ た 集 合
は 、 ま ず 最 初
に FD_ZERO() を 使 っ
て ク リ ア す べ
き で あ る 。 select()
は こ れ ら の 集
合 の 内 容 を 、
以 降 に 述 べ る
規 則 に 従 っ て
修 正 す る 。 select()
を 呼 ん だ 後 、
フ ァ イ ル デ ィ
ス ク リ プ タ ー
が ま だ 集 合 に
存 在 し て い る
か ど う か は 、
FD_ISSET() マ ク ロ に
よ っ て 調 べ る
こ と が で き る
。 FD_ISSET() は 指 定
さ れ た デ ィ ス
ク リ プ タ ー が
集 合 に 存 在 し
て い れ ば 0 以 外
の 値 を 返 し 、
存 在 し な け れ
ば 0 を 返 す 。
FD_CLR() は 集 合 か ら
の フ ァ イ ル デ
ィ ス ク リ プ タ
ー の 削 除 を 行
う 。 引 き 数
readfds こ の 集 合 に
含 ま れ る い ず
れ か の フ ァ イ
ル デ ィ ス ク リ
プ タ ー で 、 デ
ー タ の 読 み 込
み が 可 能 に な
っ た か ど う か
を 監 視 す る 。
select() か ら 戻 る 時
に 、 readfds の う ち
、 直 ち に 読 み
込 み 可 能 な フ
ァ イ ル デ ィ ス
ク リ プ タ ー 以
外 は 集 合 か ら
削 除 さ れ る 。
writefds こ の 集 合 に
含 ま れ る い ず
れ か の フ ァ イ
ル デ ィ ス ク リ
プ タ ー で 、 デ
ー タ を 書 き 込
む ス ペ ー ス が
あ る か ど う か
を 監 視 す る 。
select() か ら 戻 る 時
に 、 writefds の う ち
、 直 ち に 書 き
込 み 可 能 な フ
ァ イ ル デ ィ ス
ク リ プ タ ー 以
外 は 集 合 か ら
削 除 さ れ る 。
exceptfds こ の 集 合 に
含 ま れ る い ず
れ か の フ ァ イ
ル デ ィ ス ク リ
プ タ ー で 、 「
例 外 状 態 (exceptional
condition)」 が 発 生 し
た か ど う か を
監 視 す る 。 実
際 の 動 作 で は
、 普 通 に 起 こ
り 得 る 例 外 状
態 は 一 つ だ け
で あ り 、 そ れ
は TCP ソ ケ ッ ト で
帯 域 外 (out−of−band;
OOB) デ ー タ が 読
み 込 み 可 能 な
場 合 で あ る 。 OOB
デ ー タ の 詳 細
に つ い て は 、
recv(2), send(2), tcp(7) を
参 照 の こ と 。
(こ れ 以 外 で は
、 ま れ な こ と
だ が 、 パ ケ ッ
ト モ ー ド の 擬
似 端 末 (pseudoterminals) で
select() が 例 外 状 態
を 示 す こ と が
あ る 。 ) select() が
返 る 時 に 、
exceptfds の う ち 、
例 外 状 態 が 発
生 し た デ ィ ス
ク リ プ タ ー 以
外 は 集 合 か ら
削 除 さ れ る 。
nfds 全 て の 集 合 に 含 ま れ る フ ァ イ ル デ ィ ス ク リ プ タ ー の う ち 、 値 が 最 大 の も の に |
1 を 足 し
た 整 数 で あ る
。 す な わ ち 、
フ ァ イ ル デ ィ
ス ク リ プ タ ー
を 各 集 合 に 加
え る 作 業 の 途
中 で 、 全 て の
フ ァ イ ル デ ィ
ス ク リ プ タ ー
を 見 て 最 大 値
を 求 め 、 そ れ
に 1 を 加 え て nfds
と し て 渡 さ な
い と い け な い
、 と い う こ と
だ 。
utimeout
(何 も 起 こ ら な か っ た 場 合 に ) select() が 戻 る 前 に 待 つ 最 大 時 間 で あ る 。 こ の 値 に NULL を 渡 す と 、 select() は フ ァ イ ル デ ィ ス ク リ プ タ ー の い ず れ か が ready (準 備 が で き た ) 状 態 に な る ま で 待 ち 続 け て ず っ と 停 止 す る 。 utimeout は 0 秒 に す る こ と も で き 、 こ の 場 合 select() は 直 ち に 返 り 、 呼 び 出 し 時 点 の フ ァ イ ル デ ィ ス ク リ プ タ ー の 状 態 に 関 す る 情 報 が 返 さ れ る 。 構 造 体 struct timeval は 次 の よ う に 定 義 さ れ て い る :
struct timeval
{
time_t tv_sec; /* seconds */
long tv_usec; /* microseconds */ };
ntimeout
pselect() の こ の 引 き 数 は utimeout と 同 じ 意 味 を 持 つ が 、 struct timespec は 次 に 示 す よ う に ナ ノ 秒 の 精 度 を 持 つ 。
struct timespec
{
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */ };
sigmask こ の 引 き 数 は 、 呼 び 出 し 側 が pselect() 内 部 で 停 止 し て い る 間 に 、 カ ー ネ ル が 通 知 を 許 可 す べ き シ グ ナ ル 集 合 (す な わ ち 、 呼 び 出 し た ス レ ッ ド の シ グ ナ ル マ ス ク か ら 削 除 す べ き シ グ ナ ル 集 合 ) を 保 持 す る (sigaddset(3) と sigprocmask(2) を 参 照 )。 こ の 引 き 数 は NULL に す る こ と も で き 、 そ の 場 合 は こ の 関 数 へ 入 る と き ・ 出 る と き に シ グ ナ ル マ ス ク を 変 更 し な い 。 こ の 場 合 、 pselect() は select() と 全 く 同 じ 動 作 と な る 。 シ グ ナ ル と デ ー タ イ ベ ン ト を 組 み 合 わ せ る フ ァ イ ル デ ィ ス ク リ プ タ ー が I/O 可 能 な 状 態 に な る の と 同 時 に シ グ ナ ル も 待 ち た い 場 合 に は 、 pselect() が 便 利 で あ る 。 シ グ ナ ル を 受 信 す る プ ロ グ ラ ム は 、 通 常 は 、 シ グ ナ ル ハ ン ド ラ ー を グ ロ ー バ ル な フ ラ グ を 立 て る た め だ け に 使 う 。 こ の グ ロ ー バ ル な フ ラ グ は 、 そ の イ ベ ン ト を プ ロ グ ラ ム の メ イ ン ル ー プ で 処 理 し な け れ ば な ら な い こ と を 示 す 。 シ グ ナ ル を 受 け る と select() (や pselect()) は errno に EINTR を セ ッ ト し て 戻 る こ と に な る 。 シ グ ナ ル が プ ロ グ ラ ム の メ イ ン ル ー プ で 処 理 さ れ る た め に は こ の 動 作 が 不 可 欠 で 、 こ れ が な い と select() は 永 遠 に 停 止 し 続 け る こ と に な る 。 さ て 、 メ イ ン ル ー プ の ど こ か に こ の グ ロ ー バ ル フ ラ グ を チ ェ ッ ク す る 条 件 文 が あ る と し よ う 。 こ こ で 少 し 考 え て み な い と い け な い 。 「 シ グ ナ ル が 条 件 文 の 後 、 し か し select() コ ー ル の 前 に 到 着 し た ら ど う な る の か ? 」 答 え は 「 そ の select() は 、 た と え 解 決 待 ち の イ ベ ン ト が あ っ た と し て も 、 永 遠 に 停 止 す る 」 で あ る 。 こ の 競 合 状 態 は pselect() コ ー ル に よ っ て 解 決 で き る 。 こ の コ ー ル を 使 う と 、 pselect() で の み 受 信 し た い シ グ ナ ル の 集 合 を シ グ ナ ル マ ス ク に 設 定 す る こ と が で き る 。 例 え ば 、 問 題 と な っ て い る イ ベ ン ト が 子 プ ロ セ ス の 終 了 の 場 合 を 考 え よ う 。 メ イ ン ル ー プ が 始 ま る 前 に 、 SIGCHLD を sigprocmask(2) で ブ ロ ッ ク す る 。 pselect() コ ー ル で は SIGCHLD を 、 も と も と の シ グ ナ ル マ ス ク を 使 っ て 有 効 に す る の だ 。 こ の プ ロ グ ラ ム は 次 の よ う に な る 。
static volatile sig_atomic_t got_SIGCHLD = 0;
static void
child_sig_handler(int sig)
{
got_SIGCHLD = 1; }
int
main(int argc, char *argv[])
{
sigset_t sigmask, empty_mask;
struct sigaction sa;
fd_set readfds, writefds, exceptfds;
int r;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == −1)
{
perror("sigprocmask");
exit(EXIT_FAILURE); }
sa.sa_flags =
0;
sa.sa_handler = child_sig_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGCHLD, &sa, NULL) == −1) {
perror("sigaction");
exit(EXIT_FAILURE); }
sigemptyset(&empty_mask);
for (;;) { /*
main loop */
/* Initialize readfds, writefds, and exceptfds
before the pselect() call. (Code omitted.) */
r =
pselect(nfds, &readfds, &writefds, &exceptfds,
NULL, &empty_mask);
if (r == −1 && errno != EINTR) {
/* Handle error */ }
if
(got_SIGCHLD) {
got_SIGCHLD = 0;
/* Handle
signalled event here; e.g., wait() for all
terminated children. (Code omitted.) */ }
/* main body of program */ } } 実 例 実 際 の と こ ろ select() の 大 事 な 点 は 何 な の か ? デ ィ ス ク リ プ タ ー は 好 き な と き に 読 み 書 き で き る ん じ ゃ な い の ? select() の 重 要 な と こ ろ は 、 複 数 の デ ィ ス ク リ プ タ ー を 同 時 に 監 視 で き 、 な ん の 動 き も な け れ ば プ ロ セ ス を 適 切 に ス リ ー プ 状 態 に 移 行 す る と こ ろ に あ る の だ 。 UNIX プ ロ グ ラ マ は 、 複 数 の フ ァ イ ル デ ィ ス ク リ プ タ ー の 入 出 力 を 同 時 に 扱 わ ね ば な ら ず 、 し か も デ ー タ の 流 れ は 間 欠 的 で あ る 、 と い う 状 況 に よ く 出 会 う 。 単 に read(2) や write(2) コ ー ル の シ ー ケ ン ス を 作 る だ け で は 、 そ れ ら の コ ー ル の ど れ か が フ ァ イ ル デ ィ ス ク リ プ タ ー か ら の デ ー タ を 待 っ て ブ ロ ッ ク し て お り 、 別 の フ ァ イ ル デ ィ ス ク リ プ タ ー に は I/O が 可 能 な の に 使 え な い 、 と い う こ と に な っ て し ま う だ ろ う 。 select() を 使 う と こ の 状 況 に 効 果 的 に 対 処 で き る 。
SELECT
の 掟
select() を 使 お う と
し た 多 く の 人
は 、 理 解 し に
く い 挙 動 に 出
く わ し 、 結 果
的 に で き た も
の は 移 植 性 が
な い か 、 よ く
て も ギ リ ギ リ
の も の に な っ
て し ま う 。 例
え ば 、 上 記 の
プ ロ グ ラ ム は
、 集 合 に 含 ま
れ る フ ァ イ ル
デ ィ ス ク リ プ
タ ー を 非 停 止
(nonblocking) モ ー ド に し
な く て も 、 ど
こ に も ブ ロ ッ
ク が 生 じ な い
よ う 注 意 し て
書 か れ て い る
。 微 妙 な 間 違
い に よ っ て 、
select() を 使 う 利 点
は 簡 単 に 失 わ
れ て し ま う 。
そ こ で 、 select() コ
ー ル を 使 う と
き に 注 意 す べ
き 重 要 事 項 を
列 挙 し て お く
こ と に す る 。
1. |
select() を 使 う と き は 、 タ イ ム ア ウ ト は 設 定 す べ き で な い 。 処 理 す る デ ー タ が 無 い と き に は 、 あ な た の プ ロ グ ラ ム に は 何 も す る こ と は 無 い は ず で あ る 。 タ イ ム ア ウ ト に 依 存 し た コ ー ド は 通 常 移 植 性 が な く 、 デ バ ッ グ も 難 し く な る 。
上 述 し
た よ う に 、 効
率 的 な プ ロ グ
ラ ム を 書 く に
は nfds の 値 を 適
切 に 計 算 し て
与 え な け れ ば
な ら な い 。 select() コ ー ル の 終 了 後 に 結 果 を チ ェ ッ ク し て 、 適 切 に 対 応 す る つ も り の な い フ ァ イ ル デ ィ ス ク リ プ タ ー は 、 ど の 集 合 に も 加 え て は な ら な い 。 次 の ル ー ル も 参 照 。 4. select() か ら 返 っ た 後 に は 、 全 て の 集 合 の 全 て の フ ァ イ ル デ ィ ス ク リ プ タ ー に つ い て 読 み 書 き 可 能 な 状 態 に な っ て い る か を チ ェ ッ ク す べ き で あ る 。 5. read(2), recv(2), write(2), send(2) と い っ た 関 数 は 、 こ ち ら が 要 求 し た 全 デ ー タ を 読 み 書 き す る 必 要 は な い 。 も し 全 デ ー タ を 読 み 書 き す る な ら 、 そ れ は ト ラ フ ィ ッ ク の 負 荷 が 小 さ く 、 ス ト リ ー ム が 速 い 場 合 だ ろ う 。 こ の 条 件 は 常 に 満 た さ れ る と は 限 ら な い 。 こ れ ら の 関 数 が 頑 張 っ て も 1 バ イ ト し か 送 受 信 で き な い よ う な 場 合 も 考 慮 に 入 れ て や ら な け れ ば な ら な い 。
処 理 す
る デ ー タ 量 が
小 さ い こ と が
は っ き り と わ
か っ て い る 場
合 を 除 い て 、
一 度 に 1 バ イ ト
ず つ 読 み 書 き
す る よ う な こ
と は し て は な
ら な い 。 バ ッ
フ ァ ー の 許 す
か ぎ り の デ ー
タ を ま と め て
読 み 書 き し な
い と 、 非 常 に
効 率 が 悪 い 。
下 記 の 例 で は
バ ッ フ ァ ー は 1024
バ イ ト に し て
い る が 、 こ の
サ イ ズ を 大 き
く す る の は 簡
単 だ ろ う 。 read(2), recv(2), write(2), send(2) な ど の 関 数 や select() コ ー ル は 、 errno を EINTR や EAGAIN (EWOULDBLOCK) に し て −1 を 返 す こ と が あ る 。 こ の よ う な 結 果 に 対 し て 適 切 に 対 応 し て や ら な け れ ば な ら な い (上 記 の 例 で は し て い な い )。 書 い て い る プ ロ グ ラ ム が シ グ ナ ル を 受 け る 予 定 が な け れ ば 、 EINTR が 返 さ れ る こ と は あ ま り 考 え ら れ な い 。 書 い て い る プ ロ グ ラ ム で 非 ブ ロ ッ ク I/O を セ ッ ト し て い な い 場 合 は 、 EAGAIN が 返 さ れ る こ と は な い だ ろ う 。
決 し て
、 引 き 数 に 長
さ 0 の バ ッ フ ァ
ー を 指 定 し て
read(2), recv(2), write(2),
send(2) を 呼 び 出 し
て は な ら な い
。 read(2), recv(2), write(2), send(2) が 7. に 示 し た 以 外 の エ ラ ー で 失 敗 し た 場 合 や 、 入 力 系 の 関 数 の 一 つ が フ ァ イ ル 末 尾 を 表 す 0 を 返 し た 場 合 は 、 そ の デ ィ ス ク リ プ タ ー を も う 一 度 select に 渡 し て は な ら な い 。 下 記 の 例 で は 、 そ の デ ィ ス ク リ プ タ ー を た だ ち に ク ロ ー ズ し 、 そ こ に は −1 を セ ッ ト し て 、 そ れ が 集 合 に 含 ま れ 続 け る の を 許 さ な い よ う に し て い る 。
タ イ ム
ア ウ ト の 値 は
select() を 呼 ぶ た び
に 初 期 化 す べ
き で あ る 。 OS に
よ っ て は timeout 構
造 体 が 変 更 さ
れ る 場 合 が あ
る か ら で あ る
。 但 し 、 pselect()
は 自 分 の timeout 構
造 体 を 変 更 す
る こ と は な い
。 select() は フ ァ イ ル デ ィ ス ク リ プ タ ー 集 合 を 変 更 す る の で 、 select() が ル ー プ の 中 で 使 用 さ れ て い る 場 合 に は 、 呼 び 出 し を 行 う 前 に 毎 回 デ ィ ス ク リ プ タ ー 集 合 を 初 期 化 し 直 さ な け れ ば な ら な い 。 usleep
エ ミ ュ レ ー シ
ョ ン struct timeval
tv; 返 り 値成 功 す る と 、 select() は フ ァ イ ル デ ィ ス ク リ プ タ ー 集 合 に 残 っ て い る フ ァ イ ル デ ィ ス ク リ プ タ ー の 総 数 を 返 す 。 select() が タ イ ム ア ウ ト す る と 、 返 り 値 は 0 に な る 。 そ の 時 、 フ ァ イ ル デ ィ ス ク リ プ タ ー 集 合 は す べ て 空 で あ る (し か し そ う な ら な い シ ス テ ム も あ る )。 返 り 値 が −1 の 場 合 は エ ラ ー を 意 味 し 、 errno が 適 切 に セ ッ ト さ れ る 。 エ ラ ー が 起 こ っ た 場 合 、 返 さ れ た 集 合 の 内 容 や 構 造 体 struct timeout の 内 容 は 未 定 義 と な っ て お り 、 使 用 す べ き で は な い 。 し か し pselect() は 決 し て ntimeout を 変 更 し な い 。 注 意一 般 的 に 言 っ て 、 ソ ケ ッ ト を サ ポ ー ト す る 全 て の オ ペ レ ー テ ィ ン グ シ ス テ ム は select() も サ ポ ー ト し て い る 。 select() を 使 う と 、 プ ロ グ ラ マ が ス レ ッ ド 、 フ ォ ー ク 、 IPC、 シ グ ナ ル 、 メ モ リ ー 共 有 、 等 々 を 使 っ て も っ と 複 雑 な 方 法 で 解 決 し よ う と す る 多 く の 問 題 が 、 移 植 性 が あ り か つ 効 率 的 な 方 法 で 解 決 で き る 。 poll(2) シ ス テ ム コ ー ル は select() と 同 じ 機 能 を 持 っ て お り 、 ま ば ら な フ ァ イ ル デ ィ ス ク リ プ タ ー 集 合 を 監 視 す る 場 合 に い く ら か 効 率 が よ い 。 現 在 で は 広 く 利 用 可 能 で あ る が 、 以 前 は select() よ り 移 植 性 の 面 で 劣 っ て い た 。 Linux 独 自 の epoll(7) API は 、 多 数 の フ ァ イ ル デ ィ ス ク リ プ タ ー を 監 視 す る 場 合 に select(2) や poll(2) よ り も 効 率 的 な イ ン タ ー フ ェ ー ス を 提 供 し て い る 。 例select() の 本 当 に 便 利 な 点 を 示 す 、 よ い 例 を 紹 介 す る 。 以 下 の リ ス ト は 、 あ る TCP ポ ー ト か ら 別 の ポ ー ト へ 転 送 を 行 う TCP フ ォ ワ ー ド プ ロ グ ラ ム で あ る 。 #include
<stdlib.h> static int forward_port; #undef max static int if ((s =
socket(AF_INET, SOCK_STREAM, 0)) == −1) { static int s =
socket(AF_INET, SOCK_STREAM, 0); memset(&a,
0, sizeof(a)); if
(!inet_aton(address, (struct in_addr *)
&a.sin_addr.s_addr)) { if (connect(s,
(struct sockaddr *) &a, sizeof(a)) == −1) { #define
SHUT_FD1 do { \ #define
SHUT_FD2 do { \ #define BUF_SIZE 1024 int if (argc != 4)
{ signal(SIGPIPE, SIG_IGN); forward_port = atoi(argv[2]); h =
listen_socket(atoi(argv[1])); for (;;) { FD_ZERO(&rd);
r = select(nfds + 1, &rd, &wr, &er, NULL); if (r ==
−1 && errno == EINTR) if (r ==
−1) { if (FD_ISSET(h,
&rd)) { memset(&client_address,
0, l = sizeof(client_address)); /* NB: read oob data before normal reads */ if (fd1 > 0)
r = recv(fd1,
&c, 1, MSG_OOB); r = recv(fd2,
&c, 1, MSG_OOB); /* check if write data has caught read data */ if
(buf1_written == buf1_avail) /* one side has
closed the connection, keep if (fd1 < 0
&& buf1_avail − buf1_written == 0) 関 連 項 目accept(2), connect(2), ioctl(2), poll(2), read(2), recv(2), select(2), send(2), sigprocmask(2), write(2), sigaddset(3), sigdelset(3), sigemptyset(3), sigfillset(3), sigismember(3), epoll(7) こ の 文 書 に つ い てこ の man ペ ー ジ は Linux man−pages プ ロ ジ ェ ク ト の リ リ ー ス 3.79 の 一 部 で あ る 。 プ ロ ジ ェ ク ト の 説 明 と バ グ 報 告 に 関 す る 情 報 は http://www.kernel.org/doc/man−pages/ に 書 か れ て い る 。 |