openのO_ASYNCフラグ


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して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);
 :
 :
}


最終更新 2012/11/06 20:25:30 - north
(2012/11/01 19:06:39 作成)


検索

アクセス数
3689414
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。