Hatena::ブログ(Diary)

ablog このページをアンテナに追加 RSSフィード Twitter

2011-12-11

ノンブロッキング I/O について調べてみた

ノンブロッキングI/Oについて調べてみました*1。ノンブロッキングI/OSQLの「SELECT ... FOR UPDATE NOWAIT」のイメージに近いのかなと思いました。通常は待つケースで待たずにエラーが返ってきて、アプリケーション側でエラーハンドリングしてリトライするといった使い方になるようですね。

ポイントは以下の点だと思います。

膨大な接続があるが、常に全ての接続がアクティブじゃないし、アクティブでもI/Oできる状態でないこともあるネットワークI/Oにおいて有効な方法ではないかと思いました。


Linuxシステムプログラミング

Linuxシステムプログラミング

P.26

O_NONBLOCK

可能ならば、ファイルをノンブロッキングモード(nonblocking mode)でオープンします。open()だけではなく、他のI/O操作でもプロセスがブロック(休眠、待ち状態、block、sleep)されることはありません。*2この動作が必要になるのはFIFOだけと言ってよいでしょう。

P.33

2.2.3. ノンブロッキング I/O

読み取れるデータがまだない場合に、read()がブロックされずにリターンし、データがまだないことを判断する必要がある場合もあります。この動作をノンブロッキングI/O(nonblocking I/O)と訕れると言います。))この動作が必要になるのはFIFOだけと言ってよいでしょう。

P.33

2.2.3. ノンブロッキング I/O

読み取れるデータがまだない場合に、read()がブロックされずにリターンし、データがまだないことを判断する必要がある場合もあります。この動作をノンブロッキングI/O(nonblocking I/O)と言います。ノンブロッキングI/Oでは、複数のファイルに対しも、データがまだないという原因ではブロックされずに、I/O を発行できるようになります。

この場合はerrnoがEAGAINになっているか否かが重要になります。前述のようにファイルをノンブロッキングモードでオープンし(open()に O_NONBLOCK フラグを渡した場合。「2.1.1.1 open()のフラグ」を参照)、読み取れるデータがまだ存在しない場合にread()はブロックされずにその場で-1を返し、errnoへはEAGAINを設定します。ノンブロッキングI/Oを使用する場合はEAGAINの確認が必須です。この確認を怠ると、単にデータがまだない場合と重大なエラーを混同してしまう恐れがあります。

P.36

ノンブロッキング書込み

O_NONBLOCK フラグを使用し、ファイルをノンブッキングモードでオープンすると、本来はブロックされるような書き込みの場合に、write()システムコールは-1を返し、errnoへEAGAINを設定します。書き込みは後でやり直すことになります。この状況は通常ファイルの場合では一般的に発生しません。


Linuxネットワークプログラミング

Linuxネットワークプログラミング

P.166

ソケットは、通常状態では「ブロッキングモード」になっています。ブロッキングモードでは、ソケット入出力システムコールは何らかの処理を行う準備が整うまで制御を返しません。

しかし、シングルスレッドでプログラムを記述したいときや、データが取得可能かを調べるだけのためにシステムコールを使いたい場合もあります。そのような用途のために、ソケットがブロッキングを行わない「ノンブロッキングモード」が用意されています。

一例として、connect()システムコールは通常設定(ブロッキングモード)でブロックします。シングルスレッドでのプログラミングを行っているときに、接続先が応答せずに接続タイムアウト待ちになってしまうと、connect()システムコールでブロックしたままの状態が発生し、プログラム全体が停止する可能性があります。さらにconntect()システムコールタイムアウトカーネルが決定するために、ユーザ側から任意の時間にタイムアウトすることができません。

connect()システムコール利用時にブロッキング状態を発生させずに、任意の時間でタイムアウトさせたいプログラムを書くときには、ソケットをノンブロッキングモードにする必要があります。

(中略)

Linux では、ioctl()システムコールにFIONBIOを渡すことにより、ブロッキング/ノンブロッキングの設定をできます。


そういえば、Linux の iostat の出力結果を銀行のATMに例えて説明してみる - ablog を書いたときに、

% git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git

でとってきた Linux Kernel 2.6 のソースコードが手元にあるので見よう。

  • fs/ioctl.c 468-489 行目
static int ioctl_fionbio(struct file *filp, int __user *argp)
{
	unsigned int flag;
	int on, error;

	error = get_user(on, argp);
	if (error)
		return error;
	flag = O_NONBLOCK;
#ifdef __sparc__
	/* SunOS compatibility item. */
	if (O_NONBLOCK != O_NDELAY)
		flag |= O_NDELAY;
#endif
	spin_lock(&filp->f_lock);
	if (on)
		filp->f_flags |= flag;
	else
		filp->f_flags &= ~flag;
	spin_unlock(&filp->f_lock);
	return error;
}
  • fs/ioctl.c 546-564 行目
int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
	     unsigned long arg)
{
	int error = 0;
	int __user *argp = (int __user *)arg;
	struct inode *inode = filp->f_path.dentry->d_inode;

	switch (cmd) {
	case FIOCLEX:
		set_close_on_exec(fd, 1);
		break;

	case FIONCLEX:
		set_close_on_exec(fd, 0);
		break;

	case FIONBIO:
		error = ioctl_fionbio(filp, argp);
		break;

参考

*1Linuxに限った話ではないですが、自宅の本棚はLinuxの書籍が充実しているので、Linuxに限って調べました。

*2:訳者注:ブロッキングモードでは、例えば同じファイルに対し複数のプロセスが同時にwrite(2)すると、片方のプロセスは競合するwrite(2)が完了するまで待つことになります。この状態をプロセススリープする、またはブロックされると言います。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

リンク元