dup, dup2, dup3 -
ファイルディスクリプターを複製する
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
#define _GNU_SOURCE /* feature_test_macros(7) 参照 */
#include <fcntl.h> /* 定数 O_* の定義の取得 */
#include <unistd.h>
int dup3(int oldfd, int newfd, int flags);
dup()
システムコールは、
ファイルディスクリプター
oldfd
のコピーを作成し、
最も小さい番号の未使用のファイルディスクリプターを
新しいディスクリプターとして使用する。
成功が返された場合には、
古いファイルディスクリプターと新しいファイルディスクリプターは
互いに可換なものとして使うことができる。
2つのファイルディスクリプターは同じファイル記述
(description) (
open(2) 参照)
を参照しており、したがってファイルオフセットやファイル状態フラグが
共有される。例えば、一方のファイルディスクリプターに対して
lseek(2)
を使ってファイルオフセットを変更した場合、もう一方のファイルディスクリプターの
オフセットも変化する。
2つのファイルディスクリプターはファイルディスクリプターフラグ
(close-on-exec flag)
を共有しない。複製されたディスクリプターの
close-on-exec flag (
fcntl(2) 参照) は off
となる。
dup2() システムコールは
dup()
と同じ処理を実行するが、
番号が最も小さい未使用のファイルディスクリプターを使用する代わりに、
newfd
で指定されたファイルディスクリプター番号を使用する。
ファイルディスクリプター
newfd
が以前にオープンされていた場合には、
黙ってそのファイルディスクリプターをクローズしてから再利用する。
ファイルディスクリプター
newfd
をクローズして再利用する処理は
アトミック(不可分)に実行される。これは重要な点である。
なぜなら、
等価な機能を
close(2) と
dup()
を使って実装しようとすると、
2 つの処理の間に
newfd
が再利用されてしまうという、
競合状態にさらされることになるからだ。
このような再利用が起こるのは、
メインプログラムがファイルディスクリプターを割り当てる
シグナルハンドラーにより割り込まれたり、並行動作するスレッドが
ファイルディスクリプターを割り当てたりすることがあるからだ。
以下の点について注意すること:
- *
-
oldfd
が有効なファイルディスクリプターでない場合、その呼び出しは失敗し、
newfd
はクローズされない。
- *
-
oldfd
が有効なファイルディスクリプターで、
newfd が oldfd
と同じ値の場合、
dup2() は何もせず、
newfd を返す。
dup3() は
dup2()
と同じだが、以下の点が異なる。
- *
- 呼び出し元が、新しいファイルディスクリプターに対して
close-on-exec
フラグを強制的に設定することができる。
これを行うには、
flags に O_CLOEXEC
を指定する。
このフラグが役に立つ理由については、
open(2) の O_CLOEXEC
フラグの説明を参照のこと。
- *
-
oldfd が newfd
と同じ場合、 dup3() は
EINVAL
エラーで失敗する。
成功すると、これらのシステムコールは新しいファイルディスクリプターを返す。
エラーの場合、-1
を返し、
errno
を適切に設定する。
- EBADF
-
oldfd
がオープンされたファイルディスクリプターではない。
- EBADF
-
newfd
がファイルディスクリプターとして許可されている範囲ではない
( getrlimit(2) の RLIMIT_NOFILE
の議論を参照)。
- EBUSY
- (Linux のみ) open(2) や
dup()
との競合状態の場合に、
dup2() や dup3()
はこのエラーを返すかもしれない。
- EINTR
-
dup2() や dup3()
の呼び出しがシグナルにより割り込まれた。
signal(7) 参照。
- EINVAL
- (dup3()) flags
に無効な値が入っている。
- EINVAL
- (dup3()) oldfd が newfd
と同じであった。
- EMFILE
- The per-process limit on the number of open file
descriptors has been reached (see the discussion of RLIMIT_NOFILE
in getrlimit(2)).
dup3() はバージョン 2.6.27 で
Linux に追加された。 glibc
によるサポートはバージョン
2.9 以降で利用できる。
dup(),
dup2(): POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.
dup3() は Linux 固有である。
newfd
が範囲を超えた時に返されるエラーは、
dup2() と
fcntl(...,
F_DUPFD, ...
)
では異っている。
dup2()
が
F_DUPFD と同じように
EINVAL
を返すシステムもある。
newfd
がオープンされていた場合、
close(2)
時に報告されることになるエラーはすべて失われる。
これが心配で、シングルスレッドかつシグナルハンドラーで
ファイルディスクリプターを割り当てるようなプログラムでない場合には、
正しい方法は
dup2()
を呼び出す前に
newfd
をクローズ「しない」ことである。
なぜなら、上で説明した競合状況があるからである。
代わりに、以下のようなコードが使用できることだろう。
/* あとで close() エラーをチェックするのに使用できる
ように 'newfd' の複製を取得する。 EBADF エラーは
'newfd' がオープンされていないことを意味する。 */
tmpfd = dup(newfd);
if (tmpfd == -1 && errno != EBADF) {
/* 予期しない dup() のエラーを処理する */
}
/* アトミックに 'oldfd' を 'newfd' に複製する */
if (dup2(oldfd, newfd) == -1) {
/* dup2() のエラーを処理する */
}
/* ここでもともと 'newfd' で参照されていたファイルの
close() エラーをチェックする */
if (tmpfd != -1) {
if (close(tmpfd) == -1) {
/* close からのエラーを処理する */
}
}
close(2),
fcntl(2),
open(2),
pidfd_getfd(2)
この man ページは Linux
man-pages
プロジェクトのリリース
5.10
の一部である。プロジェクトの説明とバグ報告に関する情報は
https://www.kernel.org/doc/man-pages/
に書かれている。