OPEN(2) | Linux Programmer's Manual | OPEN(2) |
open, openat, creat - ファイルのオープン、作成を行う
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); int creat(const char *pathname, mode_t mode); int openat(int dirfd, const char *pathname, int flags); int openat(int dirfd, const char *pathname, int flags, mode_t mode);
glibc
向けの機能検査マクロの要件
(feature_test_macros(7) 参照):
openat():
ファイルの pathname を与えると、 open() はファイルディスクリプターを返す。 ファイルディスクリプターは、この後に続くシステムコール (read(2), write(2), lseek(2), fcntl(2) など) で使用される小さな非負の整数である。 このシステムコールが成功した場合に返されるファイルディスクリプターは そのプロセスがその時点でオープンしていないファイルディスクリプターの うち最小の数字のものとなる。
デフォルトでは、新しいファイルディスクリプターは execve(2) を実行した後も オープンされたままとなる (つまり、 fcntl(2) に説明がある FD_CLOEXEC ファイルディスクリプターフラグは最初は無効である); 後述の O_CLOEXEC フラグ を使うとこのデフォルトを変更することができる。 ファイルオフセット (file offset) はファイルの先頭に設定される (lseek(2) 参照)。
open() を呼び出すと、「オープンファイル記述」 (open file description) が作成される。ファイル記述とは、システム全体のオープン中のファイルのテーブルのエントリーである。 このオープンファイル記述は、ファイルオフセットとファイル状態フラグ (下記参照) が保持する。 ファイルディスクリプターはオープンファイルっ記述への参照である。 この後で pathname が削除されたり、他のファイルを参照するように変更されたりしても、 この参照は影響を受けない。 オープンファイル記述の詳細な説明は「注意」の節を参照。
引き数 flags には、アクセスモード O_RDONLY, O_WRONLY, O_RDWR のどれかひとつが入っていなければならない。 これらはそれぞれ読み込み専用、書き込み専用、読み書き用に ファイルをオープンすることを要求するものである。
さらに、 flags には、ファイル作成フラグ (file creation flag) とファイル状態フラグ (file status flag) を 0 個以上「ビット単位の OR (bitwise-or)」で 指定することができる。 ファイル作成フラグ は O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_TMPFILE, O_TRUNC, O_TTY_INIT である。 ファイル状態フラグ は以下のリストのうち上記以外の残りのものである。 二種類のフラグの違いは、ファイル状態フラグの方はその内容を取得したり (場合によっては) 変更したりできる点にある。詳細は fcntl(2) を参照。
すべてのファイル作成フラグとファイル状態フラグを以下のリストに示す。
ある種のマルチスレッドのプログラムはこのフラグの使用は不可欠である点に注意すること。 なぜなら、個別に FD_CLOEXEC フラグを設定する fcntl(2) F_SETFD 操作を呼び出したとしても、あるスレッドがファイルディスクリプターを オープンするのと同時に別のスレッドが fork(2) と execve(2) を実行するという競合条件を避けるのには十分ではないからである。 実行の順序に依存して、この競合条件の結果、 open() が返したファイルディスクリプターが fork(2) で作成された子プロセスにより実行されるプログラムに意図せず見えてしまう可能性がある。 (この種の競合は、 本質的に、 close-on-exec フラグをセットすべきファイルディスクリプターを作成するどのシステムコールでも起こり得るものであり、 他のいろいろな Linux システムコールでこの問題に対処するために O_CLOEXEC と同等の機能が提供されている。)
mode は新しいファイルを作成する場合に使用するアクセス許可 (permission) を指定する。 flags に O_CREAT か O_TMPFILE が指定されている場合、 mode を指定しなければならない。 O_CREAT も O_TMPFILE も指定されていない場合、 mode は無視される。 有効なアクセス許可は、普段と同じようにプロセスの umask によって修正され、作成されたファイルの許可は (mode & ~umask) となる。 このモードは、新しく作成されたファイルに対するそれ以降のアクセス にのみ適用される点に注意すること。 読み取り専用のファイルを作成する open() コールであっても、 読み書き可能なファイルディスクリプターを返すことがありうる。
mode のために以下のシンボル定数が提供されている :
ブロックデバイスに対する似通った意味のインターフェースが raw(8) で説明されている (但し、このインターフェースは非推奨である)。
write(2) (や同様のコール) が返るまでに、 書き込まれたデータおよびデータを取得するのに必要なファイルメタデータが裏で利用されているハードウェアに転送される (つまり、write(2) の後に fdatasync(2) を呼び出したのと同じようになる)。 下記の「注意」も参照のこと。
これら二つのフラグが指定された際、シンボリックリンクは辿られない。 pathname がシンボリックリンクの場合、 シンボリックリンクがどこを指しているかに関わらず open() は失敗する。
一般的には、 O_CREAT を指定せずに O_EXCL を使用した場合の O_EXCL の動作は規定されていない。 これには一つ例外があり、Linux 2.6 以降では、 pathname がブロックデバイスを参照している場合、 O_CREAT なしで O_EXCL を使用することができる。 システムがそのブロックデバイスを使用中の場合 (例えば、 マウントされているなど)、 open() はエラー EBUSY で失敗する。
NFS では、 O_EXCL は、Linux 2.6 以降で NFSv3 以降を使っている場合でのみサポートされる。 O_EXCL サポートが提供されていない NFS 環境では、このフラグに頼って ロック処理を実行するプログラムは競合状態 (race condition) に出会う 可能性がある。 ロックファイルを使用して不可分 (atomic) なファイルロックを実現し、 NFS が O_EXCL をサポートしているかに依存しないようにしたい場合、 移植性のある方法は、同じファイルシステム上に他と名前の重ならない ファイル (例えばホスト名と PID を組み合わせた名前) を作成し、 link(2) を使用してそのロックファイルへのリンクを作成することである。 link(2) コールの返り値が 0 ならばロックに成功している。 あるいは、そのファイルに stat(2) を使用してリンク数 (link count) が 2 になっているかをチェックする。 そうなっていれば、同じくロックに成功しているということである。
取得したファイルディスクリプターに対して以下の操作を行うことが「できる」。
pathname がシンボリックリンクで O_NOFOLLOW フラグも合わせて指定された場合、 この呼び出しではシンボリックリンクを参照するファイルディスクリプターを返す。 このファイルディスクリプターは、 空のパス名を指定した fchownat(2), fstatat(2), linkat(2), readlinkat(2) の呼び出しで dirfd 引数として使うことで、 そのシンボリックリンクに対して操作を行うことができる。
write(2) (や同様のコール) が返るまでに、 書き込まれたデータと関連するファイルメタデータが裏で利用されているハードウェアに転送される (つまり、write(2) の後に fsync(2) を呼び出したのと同じようになる)。 下記の「注意」も参照のこと。
O_TMPFILE は必ず O_RDWR か O_WRONLY のいずれかと一緒に使わなければならない。 O_EXCL も指定することができる。 O_EXCL が指定されなかった場合、 linkat(2) を使って、そのファイルシステムにこの一時ファイルへのリンクを作成し、ファイルを永続化することができる。 以下のコードのようにすればよい。
char path[PATH_MAX]; fd = open("/path/to/dir", O_TMPFILE | O_RDWR,
S_IRUSR | S_IWUSR); /* 'fd' に対するファイル I/O ... */ snprintf(path, PATH_MAX, "/proc/self/fd/%d", fd); linkat(AT_FDCWD, path, AT_FDCWD, "/path/for/file",
AT_SYMLINK_FOLLOW);
この場合、 open() の mode 引き数は O_CREAT と同様にファイルのアクセス許可モードの決定に使われる。
O_TMPFILE とともに O_EXCL を指定すると、 一時ファイルに対して上記の方法でファイルシステムへのリンクを行うことができなくなる (この場合の O_EXCL の意味は他の場合の O_EXCL の意味とは異なる点に注意)。
O_TMPFILE には主に二つの用途がある。
creat() は flags に O_CREAT|O_WRONLY|O_TRUNC を指定して open() を行うのと等価である。
openat() システムコールは open() と全く同様に動作するが、以下で説明する点が異なる。
pathname で指定されたパス名が相対パスの場合、このパス名はファイルディスクリプター dirfd が参照するディレクトリに対する相対パスと解釈される (open() に相対パス名を渡した場合のように、呼び出したプロセスのカレントワーキングディレクトリに対する相対パスではない)。
pathname で指定されたパス名が相対パスで、 dirfd が特別な値 AT_FDCWD の場合、 (open() と同様に) pathname は呼び出したプロセスのカレントワーキングディレクトリに対する相対パスと解釈される。
pathname で指定されたパス名が絶対パスの場合、 dirfd は無視される。
open(), openat(), creat() は新しいファイルディスクリプターを返す。 エラーが発生した場合は -1 を返す (その場合は errno が適切に設定される)。
open(), openat(), creat() は以下のエラーで失敗する。
openat() では以下のエラーも発生する。
openat() はカーネル 2.6.16 で Linux に追加された。 ライブラリによるサポートはバージョン 2.4 で glibc に追加された。
open(), creat() SVr4, 4.3BSD, POSIX.1-2001, POSIX.1-2008.
openat(): POSIX.1-2008.
フラグ O_DIRECT, O_NOATIME, O_PATH, O_TMPFILE は Linux 特有のものである。 これらのフラグの定義を得るためには _GNU_SOURCE を定義しなければならない。
フラグ O_CLOEXEC, O_DIRECTORY, O_NOFOLLOW は POSIX.1-2001 では規定されていないが、 POSIX.1-2008 では規定されている。 glibc 2.12 以降では、これらの定義を得るには、 _POSIX_C_SOURCE を 200809L 以上の値で定義するか、 _XOPEN_SOURCE を 700 以上の値で定義する。 glibc 2.11 以前では、 これらの定義を得るには _GNU_SOURCE を定義する。
feature_test_macros(7) に注意書きがあるように、 _POSIX_C_SOURCE, _XOPEN_SOURCE, _GNU_SOURCE などの機能検査マクロはどのヘッダーファイルをインクルードするより前に定義しなければならない。
Linux では、 O_NONBLOCK フラグは、 open を実行したいが read または write を実行する意図は 必ずしもないことを意味する。 これは ioctl(2) のためのファイルディスクリプターを取得するために、 デバイスをオープンするときによく用いられる。
O_RDONLY | O_TRUNC の影響は未定義であり、その動作は実装によって異なる。 多くのシステムではファイルは実際に切り詰められる。
open() はスペシャルファイルをオープンすることができるが、 creat() でスペシャルファイルを作成できない点に注意すること。 代わりに mknod(2) を使用する。
ファイルが新しく作成されると、 ファイルの st_atime, st_ctime, st_mtime フィールド (それぞれ最終アクセス時刻、最終状態変更時刻、最終修正時刻である。 stat(2) 参照) が現在時刻に設定される。 さらに親ディレクトリの st_ctime と st_mtime も現在時刻に設定される。 それ以外の場合で、O_TRUNC フラグでファイルが修正されたときは、 ファイルの st_ctime と st_mtime フィールドが現在時刻に設定される。
オープンファイル記述という用語は POSIX で使用されている用語で、オープンされているファイルのシステム共通のテーブルのエントリーを参照するものである。 別の文脈では、このオブジェクトはいろいろな呼び方があり、 「オープンファイルオブジェクト」、「ファイルハンドル」、「オープンファイルテーブルエントリー」、 カーネル開発者の用語では struct file などと呼ばれる。
ファイルディスクリプターが (dup(2) や同様のシステムコールを使って) 複製される際に、 複製されたファイルディスクリプターは元のファイルディスクリプターと同じオープンファイル記述を参照する。 結果として 2 つのファイルディスクリプターはファイルオフセットとファイル状態フラグを共有する。 このような共有はプロセス間でも起こり得る。 fork(2) で作成された子プロセスは親プロセスのファイルディスクリプターの複製を継承し、これらの複製は同じオープンファイル記述を参照する。
1 つのファイルに対して open(2) を行う毎に、新しいオープンファイル記述が作成される。 したがって、 1 つのファイル inode に対して複数のオープンファイル記述が存在することがありえる。
POSIX.1-2008 の「同期 I/O」の選択肢として複数種類が規定されており、 動作を制御するために open() フラグとして O_SYNC, O_DSYNC, O_RSYNC が規定されている。 この選択肢を実装がサポートしているかに関わらず、 各実装では少なくとも通常のファイルに対して O_SYNC が利用できなければならない。
Linux は O_SYNC と O_DSYNC を実装しているが、 O_RSYNC は実装していない (少し間違っているのだが、 glibc では O_RSYNC が O_SYNC と同じ値で定義されている)。
O_SYNC は、 同期 I/O でのファイル完全性完了を提供する。 つまり、 書き込み操作はデータとすべての関連メタデータを裏で利用されているハードウェアにフラッシュすることを意味する。 O_DSYNC は、 同期 I/O でのデータ完全性完了を提供する。 つまり、 書き込み操作はデータを裏で利用されているハードウェアにフラッシュするが、 それ以降の読み出し操作が正常に完了するのに必要なメタデータの更新のみをフラッシュする。 データ完全性完了は、 ファイル完全性完了を必要としないアプリケーションで、 ディスク操作の数を減らすことができる。
2 種類の完了の違いを理解するために、 ファイルメタデータの 2 つの要素、 ファイルの最終修正時刻 (st_mtime) とファイル長、を考える。 すべての書き込み操作は最終修正時刻を更新するが、 ファイルの末尾にデータを追加する書き込み操作のみがファイル長を変更する。 最終修正時刻は、 読み出しが正常に完了するのに必要ではないが、 ファイル長は必要である。 したがって、 O_DSYNC はファイル長のメタデータの更新がフラッシュされることだけを保証する (これに対して O_SYNC では最終修正時刻のメタデータも常にフラッシュされる)。
Linux 2.6.33 より前では、 Linux は open() では O_SYNC フラグのみを実装していた。 しかしながら、 このフラグが指定された場合、 ほとんどのファイルシステムで提供されていたのは実際には同期 I/O でのデータ完全性完了と等価なものであった (つまり、 O_SYNC は実際には O_DSYNC と等価なものとして実装されていた)。
Linux 2.6.33 行こう では、 正しい O_SYNC のサポートが提供されている。 しかしながら、 バイナリレベルの後方互換性を保証するため、 O_DSYNC は以前の O_SYNC と同じ値で定義されており、 O_SYNC は O_DSYNC フラグの値を含む新しい (2 ビットの) フラグ値として定義されている。 これにより、 新しいヘッダーを使ってコンパイルされたアプリケーションで、 2.6.33 より前のカーネルで少なくとも O_DSYNC の動作は同じになることが保証される。
NFS を実現しているプロトコルには多くの不備があり、特に O_SYNC と O_NDELAY に影響する。
UID マッピングを使用している NFS ファイルシステムでは、 open() がファイルディスクリプターを返した場合でも read(2) が EACCES で拒否される場合がある。 これはクライアントがアクセス許可のチェックを行って open() を実行するが、読み込みや書き込みの際には サーバーで UID マッピングが行われるためである。
「アクセスモード」の値 O_RDONLY, O_WRONLY, O_RDWR は、 flags に指定できる他の値と違い、個々のビットを指定するものではなく、 これらの値は flags の下位 2 ビットを定義する。 O_RDONLY, O_WRONLY, O_RDWR はそれぞれ 0, 1, 2 に定義されている。 言い換えると、 O_RDONLY | O_WRONLY の組み合わせは論理的に間違いであり、確かに O_RDWR と同じ意味ではない。
Linux では、特別な、非標準なアクセスモードとして 3 (バイナリでは 11) が 予約されており flags に指定できる。 このアクセスモードを指定すると、ファイルの読み出し/書き込み許可をチェックし、 読み出しにも書き込みにも使用できないディスクリプターを返す。 この非標準のアクセスモードはいくつかの Linux ドライバで、デバイス固有の ioctl(2) 操作にのみ使用されるディスクリプターを返すために使われている。
openat() やディレクトリファイルディスクリプターを引き数を取る他のシステムコールやライブラリ関数 (execveat(2), faccessat(2), fanotify_mark(2), fchmodat(2), fchownat(2), fstatat(2), futimesat(2), linkat(2), mkdirat(2), mknodat(2), name_to_handle_at(2), readlinkat(2), renameat(2), symlinkat(2), unlinkat(2), utimensat(2) mkfifoat(3), scandirat(3)) は二つの理由から用意されている。 ここでは、 openat コールに関して説明するが、この基本原理は他のインターフェースでも同じである。
最初の理由として、 openat() を使うと、 アプリケーションは、 カレントワーキングディレクトリ以外のディレクトリで open() を使ってファイルをオープンする際に起こり得る競合条件を避けることができる。 これらの競合条件は、 open() に渡されたディレクトリプレフィックスの構成要素が open() の呼び出しと並行して変化する可能性があるという点に由来している。 例えば、ファイル path/to/xxx が存在する場合にファイル path/to/xxx.dep を作成したいとする。 問題は、存在確認とファイル作成の間に、 path や to (シンボリックリンクでもよい) が別の場所を指すように変更されることがあるということだ。 このような競合条件は、 対象のディレクトリに対するファイルディスクリプターをオープンし、 それから fstatat(2) や openat() の dirfd 引き数としてそのファイルディスクリプターを指定することで、 避けることができる。
二つ目として、 openat() を使うと、アプリケーションが管理するファイルディスクリプターにより、 スレッド単位の「カレントワーキングディレクトリ」を実装することができる (この機能は、 /proc/self/fd/dirfd を使った方法でも実現することができるが、 効率の面で落とる)。
O_DIRECT フラグを使用する場合、ユーザー空間バッファーの長さやアドレス、 I/O のファイルオフセットに関してアラインメントの制限が課されることがある。 Linux では、アラインメントの制限はファイルシステムやカーネルのバージョンに よって異なり、全く制限が存在しない場合もある。 しかしながら、現在のところ、指定されたファイルやファイルシステムに対して こうした制限があるかを見つけるための、アプリケーション向けのインターフェースで ファイルシステム非依存のものは存在しない。 いくつかのファイルシステムでは、制限を確認するための独自のインターフェースが 提供されている。例えば、 xfsctl(3) の XFS_IOC_DIOINFO 命令である。
Linux 2.4 では、転送サイズ、 ユーザーバッファーのアライメント、ファイルオフセットは、 ファイルシステムの論理ブロックサイズの倍数でなければならない。 Linux 2.6.0 以降では、 内部で使われるストレージの論理ブロックサイズのアライメント (通常は 512 バイト) で十分である。 論理ブロックサイズは ioctl(2) BLKSSZGET 操作や以下のシェルコマンドから知ることができる。
blockdev --getss
メモリーバッファーがプライベートマッピング (mmap(2) の MAP_PRIVATE フラグで作成されたマッピング) の場合には、O_DIRECT I/O は fork(2) システムコールと同時に決して実行すべきではない (プライベートマッピングには、ヒープ領域に割り当てられたメモリーや静的に 割り当てたバッファーも含まれる)。非同期 I/O インターフェース (AIO) 経由 やプロセス内の他のスレッドから発行された、このような I/O は、 fork(2) が呼び出される前に完了されるべきである。 そうしなかった場合、データ破壊や、親プロセスや子プロセスでの予期しない 動作が起こる可能性がある。 O_DIRECT I/O 用のメモリーバッファーが shmat(2) やMAP_SHARED フラグ 付きの mmap(2) で作成された場合には、この制限はあてはまらない。 madvise(2) でメモリーバッファーにアドバイス MADV_DONTFORK が設定され ている場合にも、この制限はあてはまらない(MADV_DONTFORK はそのメモリー バッファーが fork(2) 後に子プロセスからは利用できないことを保証するも のである)。
O_DIRECT フラグは SGI IRIX で導入された。SGI IRIX にも Linux 2.4 と同様の (ユーザーバッファーの) アラインメントの制限がある。 また、IRIX には適切な配置とサイズを取得するための fcntl(2) コールがある。 FreeBSD 4.x も同じ名前のフラグを導入したが、アラインメントの制限はない。
O_DIRECT が Linux でサポートされたのは、カーネルバージョン 2.4.10 である。 古い Linux カーネルは、このフラグを単に無視する。 O_DIRECT フラグをサポートしていないファイルシステムもあり、その場合は、 O_DIRECT を使用すると open() は EINVAL で失敗する。
アプリケーションは、同じファイル、 特に同じファイルの重複するバイト領域に対して、 O_DIRECT と通常の I/O を混ぜて使うのは避けるべきである。 ファイルシステムがこのような状況において一貫性の問題を正しく 扱うことができる場合であっても、全体の I/O スループットは どちらか一方を使用するときと比べて低速になるであろう。 同様に、アプリケーションは、同じファイルに対して mmap(2) と直接 I/O (O_DIRECT) を混ぜて使うのも避けるべきである。
NFS で O_DIRECT を使った場合の動作はローカルのファイルシステムの場合と違う。 古いカーネルや、ある種の設定でコンパイルされたカーネルは、 O_DIRECT と NFS の組み合わせをサポートしていないかもしれない。 NFS プロトコル自体はサーバにフラグを渡す機能は持っていないので、 O_DIRECT I/O はクライアント上のページキャッシュをバイパスするだけになり、 サーバは I/O をキャッシュしているかもしれない。 クライアントは、 O_DIRECT の同期機構を保持するため、サーバに対して I/O を同期して行うように依頼する。 サーバによっては、こうした状況下、特に I/O サイズが小さい場合に 性能が大きく劣化する。 また、サーバによっては、I/O が安定したストレージにまで行われたと、 クライアントに対して嘘をつくものもある。 これは、サーバの電源故障が起こった際にデータの完全性が保たれない 危険は少しあるが、性能面での不利な条件を回避するために行われている。 Linux の NFS クライアントでは O_DIRECT I/O でのアラインメントの制限はない。
まとめると、 O_DIRECT は、注意して使うべきであるが、強力なツールとなる可能性を持っている。 アプリケーションは O_DIRECT をデフォルトでは無効になっている性能向上のためのオプションと 考えておくのがよいであろう。
現在のところ、 open() の呼び出し時に O_ASYNC を指定してシグナル駆動 I/O を有効にすることはできない。 このフラグを有効にするには fcntl(2) を使用すること。
カーネルが O_TMPFILE 機能をサポートしているかを判定する際に、 EISDIR と ENOENT の 2 つのエラーコードをチェックしなければならない。
chmod(2), chown(2), close(2), dup(2), fcntl(2), link(2), lseek(2), mknod(2), mmap(2), mount(2), open_by_handle_at(2), read(2), socket(2), stat(2), umask(2), unlink(2), write(2), fopen(3), fifo(7), path_resolution(7), symlink(7)
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2015-01-22 | Linux |