openのO_ASYNCフラグ
Rev.1を表示中。最新版はこちら。
openのオプションにO_ASYNCと言うのがあり、説明によると、シグナル駆動 I/O (signal-driven I/O) を有効にする: このファイルディスクリプタへの 入力または出力が可能になった場合に、シグナルを生成する。と言う事。が、現行バージョンではサポートされなくなった模様(別の機能として何かあるのかもしれませんが・・・)以下はopen/fcntlのO_ASYNCフラグの検証したものです。コマンド引数にて3通りの条件の実行結果で、それぞれ実行毎に、別端末からecho a > /tmp/fasynceとし、パイプに書き込みしています。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> int fd; void sigio(int sig) { printf("recive signal\n"); } void chk_async() { int fflag; fflag = fcntl(fd, F_GETFL, 0); printf("open async %s\n", (fflag & O_ASYNC)? "set": "reset"); } int main(int argc, char *argv[]) { int open_async, fcntl_async; struct sigaction sa = { .sa_handler = sigio }; sigaction(SIGIO, &sa, NULL); open_async = atoi(argv[1]); fcntl_async = atoi(argv[2]); if (open_async == 1) { fd = open("/tmp/fasync", O_RDONLY | O_NONBLOCK | O_ASYNC); } else { fd = open("/tmp/fasync", O_RDONLY | O_NONBLOCK); } chk_async(); fcntl(fd, F_SETOWN, getpid()); if (fcntl_async == 1) { fcntl(fd, F_SETFL, O_ASYNC); } while(1) { sleep(1); } return 0; }
実行結果
以下の結果からopenでO_ASYNCを設定しても、シグナルが送られてきません。それどころかfcntlでO_ASYNCを設定してもシグナルは送られてきません。なお、openのO_ASYNCフラグは、fcntl(fd, F_GETFL, 0)で見る限り、ちゃんと設定されているようです。[root@localhost test]# ./fasync 1 0 open async set ^C [root@localhost test]# ./fasync 0 1 open async reset recive signal ^C [root@localhost test]# ./fasync 1 1 open async set ^C
実装
ファイルopenシステムコールで、openのオプションの処理は、build_open_flags()で行っていて、要はop->open_flag = flagsでオプションフラグをopen_flagsオブジェクトに設定し、do_filp_open()でFILEオブジェクトを取得する時に、filp->f_flags = op->open_flagとする事にあるようです。カーネルではO_ASYNCは定義されていなく、glibでFASYNCとしてコールされます。FASYNCの実装は、キャラクタデバイス依存で、パイプについて言えば、inode->i_pipe->fasync_readers/inode->i_pipe->fasync_writersに、 FILEオブジェクトを有したfasync_structオブジェクトをリストする事にあります。
do_filp_open()ではそのような処理はありません。openでO_ASYNCを設定しても。シグナルが送られて来ないと言う事のようです。
static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op) { int lookup_flags = 0; int acc_mode; if (!(flags & O_CREAT)) mode = 0; op->mode = mode; flags &= ~FMODE_NONOTIFY; if (flags & __O_SYNC) flags |= O_DSYNC; if (flags & O_PATH) { flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH; acc_mode = 0; } else { acc_mode = MAY_OPEN | ACC_MODE(flags); } op->open_flag = flags; if (flags & O_TRUNC) acc_mode |= MAY_WRITE; if (flags & O_APPEND) acc_mode |= MAY_APPEND; op->acc_mode = acc_mode; op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN; if (flags & O_CREAT) { op->intent |= LOOKUP_CREATE; if (flags & O_EXCL) op->intent |= LOOKUP_EXCL; } if (flags & O_DIRECTORY) lookup_flags |= LOOKUP_DIRECTORY; if (!(flags & O_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; return lookup_flags; }F_SETFLでのO_ASYNCのfcntlシステムコールを見てみると、その処理は、setfl()で行われ、FASYNCの処理は、filp->f_op->fasyncのコールバック(inodeのコールバック)がコールされ、ここでfasync_structオブジェクトがリストされるのだが、((arg ^ filp->f_flags) & FASYNC)で、filp->f_flagsにFASYNCがセットされていれば、((arg ^ filp->f_flags) & FASYNC)は0である。従ってO_ASYNCでopenすると、fcntlでO_ASYNCをセットする事はできない。
static int setfl(int fd, struct file * filp, unsigned long arg) { struct inode * inode = filp->f_path.dentry->d_inode; int error = 0; if (((arg ^ filp->f_flags) & O_APPEND) && IS_APPEND(inode)) return -EPERM; if ((arg & O_NOATIME) && !(filp->f_flags & O_NOATIME)) if (!inode_owner_or_capable(inode)) return -EPERM; if (O_NONBLOCK != O_NDELAY) if (arg & O_NDELAY) arg |= O_NONBLOCK; if (arg & O_DIRECT) { if (!filp->f_mapping || !filp->f_mapping->a_ops || !filp->f_mapping->a_ops->direct_IO) return -EINVAL; } if (filp->f_op && filp->f_op->check_flags) error = filp->f_op->check_flags(arg); if (error) return error; if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op && filp->f_op->fasync) { error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); if (error < 0) goto out; if (error > 0) error = 0; } spin_lock(&filp->f_lock); filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK); spin_unlock(&filp->f_lock); out: return error; }