Man page - select_tut(2)

Packages contains this manual

Available languages:

en fr ja ru

Manual

SELECT_TUT

名 前
書 式
説 明
シ グ ナ ル と デ ー タ イ ベ ン ト を 組 み 合 わ せ る
実 例
SELECT の 掟
返 り 値
注 意

関 連 項 目
こ の 文 書 に つ い て

名 前

select, pselect - 同 期 I/O の 多 重 化

書 式

select (2) 参 照 。

説 明

シ ス テ ム コ ー ル select () や pselect () を 使 う と 、 効 率 的 に 複 数 の フ ァ イ ル デ ィ ス ク リ プ タ ー を 監 視 し 、 そ の フ ァ イ ル デ ィ ス ク リ プ タ ー の い ず れ か が 「 ready (準 備 が で き た )」 状 態 、 つ ま り I/O (入 出 力 ) が 可 能 に な っ て い る か や 、 フ ァ イ ル デ ィ ス ク リ プ タ ー の い ず れ か が 「 例 外 状 態 (exceptional condition)」 が 発 生 し た か 、 を 調 べ る こ と が で き る 。

This page provides background and tutorial information on the use of these system calls. For details of the arguments and semantics of select () and pselect (), see select (2).

シ グ ナ ル と デ ー タ イ ベ ン ト を 組 み 合 わ せ る

フ ァ イ ル デ ィ ス ク リ プ タ ー が 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 () を 使 う と き は 、 タ イ ム ア ウ ト は 設 定 す べ き で な い 。 処 理 す る デ ー タ が 無 い と き に は 、 あ な た の プ ロ グ ラ ム に は 何 も す る こ と は 無 い は ず で あ る 。 タ イ ム ア ウ ト に 依 存 し た コ ー ド は 通 常 移 植 性 が な く 、 デ バ ッ グ も 難 し く な る 。

2.

上 述 し た よ う に 、 効 率 的 な プ ロ グ ラ ム を 書 く に は nfds の 値 を 適 切 に 計 算 し て 与 え な け れ ば な ら な い 。

3.

select () コ ー ル の 終 了 後 に 結 果 を チ ェ ッ ク し て 、 適 切 に 対 応 す る つ も り の な い フ ァ イ ル デ ィ ス ク リ プ タ ー は 、 ど の 集 合 に も 加 え て は な ら な い 。 次 の ル ー ル も 参 照 。

4.

select () か ら 返 っ た 後 に は 、 全 て の 集 合 の 全 て の フ ァ イ ル デ ィ ス ク リ プ タ ー に つ い て 読 み 書 き 可 能 な 状 態 に な っ て い る か を チ ェ ッ ク す べ き で あ る 。

5.

read (2), recv (2), write (2), send (2) と い っ た 関 数 は 、 こ ち ら が 要 求 し た 全 デ ー タ を 読 み 書 き す る 必 要 は な い 。 も し 全 デ ー タ を 読 み 書 き す る な ら 、 そ れ は ト ラ フ ィ ッ ク の 負 荷 が 小 さ く 、 ス ト リ ー ム が 速 い 場 合 だ ろ う 。 こ の 条 件 は 常 に 満 た さ れ る と は 限 ら な い 。 こ れ ら の 関 数 が 頑 張 っ て も 1 バ イ ト し か 送 受 信 で き な い よ う な 場 合 も 考 慮 に 入 れ て や ら な け れ ば な ら な い 。

6.

処 理 す る デ ー タ 量 が 小 さ い こ と が は っ き り と わ か っ て い る 場 合 を 除 い て 、 一 度 に 1 バ イ ト ず つ 読 み 書 き す る よ う な こ と は し て は な ら な い 。 バ ッ フ ァ ー の 許 す か ぎ り の デ ー タ を ま と め て 読 み 書 き し な い と 、 非 常 に 効 率 が 悪 い 。 下 記 の 例 で は バ ッ フ ァ ー は 1024 バ イ ト に し て い る が 、 こ の サ イ ズ を 大 き く す る の は 簡 単 だ ろ う 。

7.

Calls to read (2), recv (2), write (2), send (2), and select () can fail with the error EINTR , and calls to read (2), recv (2) write (2), and send (2) can fail with errno set to EAGAIN ( EWOULDBLOCK ). These results must be properly managed (not done properly above). If your program is not going to receive any signals, then it is unlikely you will get EINTR . If your program does not set nonblocking I/O, you will not get EAGAIN .

8.

決 し て 、 引 数 に 長 さ 0 の バ ッ フ ァ ー を 指 定 し て read (2), recv (2), write (2), send (2) を 呼 び 出 し て は な ら な い 。

9.

read (2), recv (2), write (2), send (2) が 7. に 示 し た 以 外 の エ ラ ー で 失 敗 し た 場 合 や 、 入 力 系 の 関 数 の 一 つ が フ ァ イ ル 末 尾 を 表 す 0 を 返 し た 場 合 は 、 そ の フ ァ イ ル デ ィ ス ク リ プ タ ー を も う 一 度 select に 渡 し て は な ら な い 。 下 記 の 例 で は 、 そ の フ ァ イ ル デ ィ ス ク リ プ タ ー を た だ ち に ク ロ ー ズ し 、 そ こ に は -1 を セ ッ ト し て 、 そ れ が 集 合 に 含 ま れ 続 け る の を 許 さ な い よ う に し て い る 。

10.

タ イ ム ア ウ ト の 値 は select () を 呼 ぶ た び に 初 期 化 す べ き で あ る 。 OS に よ っ て は timeout 構 造 体 が 変 更 さ れ る 場 合 が あ る か ら で あ る 。 但 し 、 pselect () は 自 分 の timeout 構 造 体 を 変 更 す る こ と は な い 。

11.

select () は フ ァ イ ル デ ィ ス ク リ プ タ ー 集 合 を 変 更 す る の で 、 select () が ル ー プ の 中 で 使 用 さ れ て い る 場 合 に は 、 呼 び 出 し を 行 う 前 に 毎 回 デ ィ ス ク リ プ タ ー 集 合 を 初 期 化 し 直 さ な け れ ば な ら な い 。

返 り 値

select (2) 参 照 。

注 意

一 般 的 に 言 っ て 、 ソ ケ ッ ト を サ ポ ー ト す る 全 て の オ ペ レ ー テ ィ ン グ シ ス テ ム は select () も サ ポ ー ト し て い る 。 select () を 使 う と 、 プ ロ グ ラ マ が ス レ ッ ド 、 フ ォ ー ク 、 IPC、 シ グ ナ ル 、 メ モ リ ー 共 有 、 等 々 を 使 っ て も っ と 複 雑 な 方 法 で 解 決 し よ う と す る 多 く の 問 題 が 、 移 植 性 が あ り か つ 効 率 的 な 方 法 で 解 決 で き る 。

poll (2) シ ス テ ム コ ー ル は select () と 同 じ 機 能 を 持 っ て お り 、 ま ば ら な フ ァ イ ル デ ィ ス ク リ プ タ ー 集 合 を 監 視 す る 場 合 に い く ら か 効 率 が よ い 。 現 在 で は 広 く 利 用 可 能 で あ る が 、 以 前 は select () よ り 移 植 性 の 面 で 劣 っ て い た 。

Linux 独 自 の epoll (7) API は 、 多 数 の フ ァ イ ル デ ィ ス ク リ プ タ ー を 監 視 す る 場 合 に select (2) や poll (2) よ り も 効 率 的 な イ ン タ ー フ ェ ー ス を 提 供 し て い る 。

select () の 本 当 に 便 利 な 点 を 示 す 、 よ い 例 を 紹 介 す る 。 以 下 の リ ス ト は 、 あ る TCP ポ ー ト か ら 別 の ポ ー ト へ 転 送 を 行 う TCP フ ォ ワ ー ド プ ロ グ ラ ム で あ る 。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

static int forward_port;

#undef max
#define max(x,y) ((x) > (y) ? (x) : (y))

static int
listen_socket(int listen_port)
{
struct sockaddr_in addr;
int lfd;
int yes;

lfd = socket(AF_INET, SOCK_STREAM, 0);
if (lfd == -1) {
perror("socket");
return -1;
}

yes = 1;
if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,
&yes, sizeof(yes)) == -1) {
perror("setsockopt");
close(lfd);
return -1;
}

memset(&addr, 0, sizeof(addr));
addr.sin_port = htons(listen_port);
addr.sin_family = AF_INET;
if (bind(lfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
perror("bind");
close(lfd);
return -1;
}

printf("accepting connections on port %d\n", listen_port);
listen(lfd, 10);
return lfd;
}

static int
connect_socket(int connect_port, char *address)
{
struct sockaddr_in addr;
int cfd;

cfd = socket(AF_INET, SOCK_STREAM, 0);
if (cfd == -1) {
perror("socket");
return -1;
}

memset(&addr, 0, sizeof(addr));
addr.sin_port = htons(connect_port);
addr.sin_family = AF_INET;

if (!inet_aton(address, (struct in_addr *) &addr.sin_addr.s_addr)) {
fprintf(stderr, "inet_aton(): bad IP address format\n");
close(cfd);
return -1;
}

if (connect(cfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
perror("connect()");
shutdown(cfd, SHUT_RDWR);
close(cfd);
return -1;
}
return cfd;
}

#define SHUT_FD1 do { \
if (fd1 >= 0) { \
shutdown(fd1, SHUT_RDWR); \
close(fd1); \
fd1 = -1; \
} \
} while (0)

#define SHUT_FD2 do { \
if (fd2 >= 0) { \
shutdown(fd2, SHUT_RDWR); \
close(fd2); \
fd2 = -1; \
} \
} while (0)

#define BUF_SIZE 1024

int
main(int argc, char *argv[])
{
int h;
int fd1 = -1, fd2 = -1;
char buf1[BUF_SIZE], buf2[BUF_SIZE];
int buf1_avail = 0, buf1_written = 0;
int buf2_avail = 0, buf2_written = 0;

if (argc != 4) {
fprintf(stderr, "Usage\n\tfwd <listen-port> "
"<forward-to-port> <forward-to-ip-address>\n");
exit(EXIT_FAILURE);
}

signal(SIGPIPE, SIG_IGN);

forward_port = atoi(argv[2]);

h = listen_socket(atoi(argv[1]));
if (h == -1)
exit(EXIT_FAILURE);

for (;;) {
int ready, nfds = 0;
ssize_t nbytes;
fd_set readfds, writefds, exceptfds;

FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
FD_SET(h, &readfds);
nfds = max(nfds, h);

if (fd1 > 0 && buf1_avail < BUF_SIZE)
FD_SET(fd1, &readfds);
/* Note: nfds is updated below, when fd1 is added to
exceptfds. */
if (fd2 > 0 && buf2_avail < BUF_SIZE)
FD_SET(fd2, &readfds);

if (fd1 > 0 && buf2_avail - buf2_written > 0)
FD_SET(fd1, &writefds);
if (fd2 > 0 && buf1_avail - buf1_written > 0)
FD_SET(fd2, &writefds);

if (fd1 > 0) {
FD_SET(fd1, &exceptfds);
nfds = max(nfds, fd1);
}
if (fd2 > 0) {
FD_SET(fd2, &exceptfds);
nfds = max(nfds, fd2);
}

ready = select(nfds + 1, &readfds, &writefds, &exceptfds, NULL);

if (ready == -1 && errno == EINTR)
continue;

if (ready == -1) {
perror("select()");
exit(EXIT_FAILURE);
}

if (FD_ISSET(h, &readfds)) {
socklen_t addrlen;
struct sockaddr_in client_addr;
int fd;

addrlen = sizeof(client_addr);
memset(&client_addr, 0, addrlen);
fd = accept(h, (struct sockaddr *) &client_addr, &addrlen);
if (fd == -1) {
perror("accept()");
} else {
SHUT_FD1;
SHUT_FD2;
buf1_avail = buf1_written = 0;
buf2_avail = buf2_written = 0;
fd1 = fd;
fd2 = connect_socket(forward_port, argv[3]);
if (fd2 == -1)
SHUT_FD1;
else
printf("connect from %s\n",
inet_ntoa(client_addr.sin_addr));

/* Skip any events on the old, closed file
descriptors. */

continue;
}
}

/* NB: read OOB data before normal reads */

if (fd1 > 0 && FD_ISSET(fd1, &exceptfds)) {
char c;

nbytes = recv(fd1, &c, 1, MSG_OOB);
if (nbytes < 1)
SHUT_FD1;
else
send(fd2, &c, 1, MSG_OOB);
}
if (fd2 > 0 && FD_ISSET(fd2, &exceptfds)) {
char c;

nbytes = recv(fd2, &c, 1, MSG_OOB);
if (nbytes < 1)
SHUT_FD2;
else
send(fd1, &c, 1, MSG_OOB);
}
if (fd1 > 0 && FD_ISSET(fd1, &readfds)) {
nbytes = read(fd1, buf1 + buf1_avail,
BUF_SIZE - buf1_avail);
if (nbytes < 1)
SHUT_FD1;
else
buf1_avail += nbytes;
}
if (fd2 > 0 && FD_ISSET(fd2, &readfds)) {
nbytes = read(fd2, buf2 + buf2_avail,
BUF_SIZE - buf2_avail);
if (nbytes < 1)
SHUT_FD2;
else
buf2_avail += nbytes;
}
if (fd1 > 0 && FD_ISSET(fd1, &writefds) && buf2_avail > 0) {
nbytes = write(fd1, buf2 + buf2_written,
buf2_avail - buf2_written);
if (nbytes < 1)
SHUT_FD1;
else
buf2_written += nbytes;
}
if (fd2 > 0 && FD_ISSET(fd2, &writefds) && buf1_avail > 0) {
nbytes = write(fd2, buf1 + buf1_written,
buf1_avail - buf1_written);
if (nbytes < 1)
SHUT_FD2;
else
buf1_written += nbytes;
}

/* Check if write data has caught read data */

if (buf1_written == buf1_avail)
buf1_written = buf1_avail = 0;
if (buf2_written == buf2_avail)
buf2_written = buf2_avail = 0;

/* One side has closed the connection, keep
writing to the other side until empty */

if (fd1 < 0 && buf1_avail - buf1_written == 0)
SHUT_FD2;
if (fd2 < 0 && buf2_avail - buf2_written == 0)
SHUT_FD1;
}
exit(EXIT_SUCCESS);
}

上 記 の プ ロ グ ラ ム は 、 ほ と ん ど の 種 類 の TCP 接 続 を フ ォ ワ ー ド す る 。 telnet サ ー バ に よ っ て 中 継 さ れ る OOB シ グ ナ ル デ ー タ も 扱 え る 。 こ の プ ロ グ ラ ム は 、 デ ー タ フ ロ ー を 双 方 向 に 同 時 に 送 る と い う 、 や や こ し い 問 題 も 処 理 で き る 。 fork (2) コ ー ル を 使 っ て 、 各 ス ト リ ー ム ご と に 専 用 の ス レ ッ ド を 用 い る ほ う が 効 率 的 だ 、 と い う 人 も い る か も し れ な い 。 し か し 、 こ れ は 考 え て い る よ り ず っ と や や こ し い 。 あ る い は 、 fcntl (2) を 使 っ て 非 ブ ロ ッ ク I/O を セ ッ ト す れ ば 良 い 、 と い う ア イ デ ア も あ る だ ろ う 。 こ れ に も 実 際 に は 問 題 が あ り 、 タ イ ム ア ウ ト が 非 効 率 的 に 起 こ っ て し ま う 。

こ の プ ロ グ ラ ム は 一 度 に ひ と つ 以 上 の 同 時 接 続 を 扱 う こ と は で き な い が 、 そ の 様 に 拡 張 す る の は 簡 単 で 、 バ ッ フ ァ ー の リ ン ク リ ス ト を (接 続 ご と に ひ と つ ず つ ) 使 え ば よ い 。 現 時 点 の も の で は 、 新 し い 接 続 が く る と 古 い 接 続 は 落 ち て し ま う 。

関 連 項 目

accept (2), connect (2), poll (2), read (2), recv (2), select (2), send (2), sigprocmask (2), write (2), epoll (7)

こ の 文 書 に つ い て

こ の man ペ ー ジ は Linux man-pages プ ロ ジ ェ ク ト の リ リ ー ス 5.10 の 一 部 で あ る 。 プ ロ ジ ェ ク ト の 説 明 と バ グ 報 告 に 関 す る 情 報 は https://www.kernel.org/doc/man-pages/ に 書 か れ て い る 。