openのO_ASYNCフラグ
openのオプションにO_ASYNCと言うのがあり、説明によると、シグナル駆動 I/O (signal-driven I/O) を有効にする: このファイルディスクリプタへの 入力または出力が可能になった場合に、シグナルを生成する。と言う事。が、現行バージョンではサポートされなくなった模様(別の機能として何かあるのかもしれませんが・・・)
以下はopen/fcntlのO_ASYNCフラグの検証したものです。コマンド引数にて3通りの条件の実行結果で、それぞれ実行毎に、別端末からecho a > /tmp/fasynceとし、パイプに書き込みしています。
カーネルではO_ASYNCは定義されていなく、glibでFASYNCとしてコールされます。FASYNCの実装は、キャラクタデバイス依存で、パイプについて言えば、inode->i_pipe->fasync_readers/inode->i_pipe->fasync_writersに、 FILEオブジェクトを有したfasync_structオブジェクトをリストする事にあります。
do_filp_open()ではそのような処理はありません。openでO_ASYNCを設定しても。シグナルが送られて来ないと言う事のようです。
従って、マルチスレッドで、あるスレッドがファイルをcloseしてしまったFILEオブジェクトで、読み書きした場合、その旨ををシグナルとして送信するという事のようですが・・・。後に、具体的に検証してみたいと思います。
以下は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してfilp->f_flagsにFASYNCがセットされてしまうと、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;
}
補足
__fput()はファイルをcloseした時、ファイルオブジェクトの解放処理としてコールされます。この時、if (unlikely(file->f_flags & FASYNC))でFASYNCが設定されていたら、file->f_op->fasync()コールバック関数がコールされ、fasync_structオブジェクトがリストされるようです。従って、マルチスレッドで、あるスレッドがファイルをcloseしてしまったFILEオブジェクトで、読み書きした場合、その旨ををシグナルとして送信するという事のようですが・・・。後に、具体的に検証してみたいと思います。
static void __fput(struct file *file)
{
:
:
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op && file->f_op->fasync)
file->f_op->fasync(-1, file, 0);
}
if (file->f_op && file->f_op->release)
file->f_op->release(inode, file);
security_file_free(file);
:
:
}







