NAME
dup, dup2, dup3 - 파일 디스크립터 복제하기
SYNOPSIS
#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);
DESCRIPTION
dup()
시스템 호출은 파일 디스크립터 oldfd
의 사본을 만든다. 안 쓰는 가장 낮은 파일 디스크립터 번호를 새 디스크립터에 쓴다.
성공 반환 후에는 이전 파일 디스크립터와 새 파일 디스크립터를 바꿔 가며 쓸 수도 있다. 같은 열린 파일 기술 항목(open(2) 참고)을 가리키기 때문에 오프셋과 파일 상태를 공유한다. 예를 들어 한쪽 파일 디스크립터에서 lseek(2)으로 파일 오프셋을 변경하면 다른 쪽에서도 오프셋이 바뀐다.
파일 디스크립터 플래그(exec에서 닫기 플래그)는 공유하지 않는다. 복제본 디스크립터에는 exec에서 닫기(FD_CLOEXEC
, fcntl(2) 참고) 플래그가 꺼져 있다.
dup2()
dup2()
시스템 호출은 dup()
과 같은 일을 하되 안 쓰는 가장 낮은 파일 디스크립터 번호를 쓰는 게 아니라 newfd
에 지정한 파일 디스크립터 번호를 쓴다. 파일 디스크립터 newfd
가 이미 열려 있으면 조용히 닫고서 재사용한다.
파일 디스크립터 newfd
를 닫고 재사용하는 단계를 원자적으로 수행한다. 이 점이 중요한 건 close(2)와 dup()
을 이용해 동등한 기능성을 구현하려 하면 newfd
가 두 단계 사이에서 재사용될 수도 있는 경쟁 조건에 걸리게 되기 때문이다. 메인 프로그램을 중단시킨 시그널 핸들러에서 파일 디스크립터를 할당하거나 병렬 스레드에서 파일 디스크립터를 할당한다면 그런 재사용이 일어날 수 있을 것이다.
다음 사항에 주의해야 한다.
-
oldfd
가 유효한 파일 디스크립터가 아닌 경우 호출이 실패하며newfd
는 닫히지 않는다. -
oldfd
가 유효한 파일 디스크립터이고newfd
가oldfd
와 값이 같은 경우dup2()
는 아무것도 하지 않고newfd
를 반환한다.
dup3()
dup3()
는 다음 사항을 제외하면 dup2()
와 같다.
-
호출자가
flags
에O_CLOEXEC
를 지정해서 새 파일 디스크립터에 exec에서 닫기 플래그가 설정되게 할 수 있다. 이게 유용할 수 있는 이유에 대해선 open(2)의 해당 플래그 설명을 보라. -
oldfd
가newfd
와 같은 경우에dup3()
는EINVAL
오류로 실패한다.
RETURN VALUE
성공 시 이 시스템 호출들은 새 파일 디스크립터를 반환한다. 오류 시 -1을 반환하며 오류를 나타내도록 errno
를 설정한다.
ERRORS
EBADF
oldfd
가 열린 파일 디스크립터가 아니다.EBADF
newfd
가 파일 디스크립터 허용 범위를 벗어난다. (getrlimit(2)의RLIMIT_NOFILE
설명 참고.)EBUSY
- (리눅스 한정) open(2)과
dup()
의 경쟁 조건에서dup2()
나dup3()
에서 이 값을 반환할 수 있다. EINTR
dup2()
나dup3()
호출이 시그널에 의해 중단되었다. signal(7) 참고.EINVAL
- (
dup3()
)flags
에 유효하지 않은 값이 있다. EINVAL
- (
dup3()
)oldfd
가newfd
와 같다. EMFILE
- 열린 파일 디스크립터 개수에 대한 프로세스별 제한에 도달했다. (getrlimit(2)의
RLIMIT_NOFILE
설명 참고.)
VERSIONS
리눅스 2.6.27에서 dup3()
가 추가되었다. glibc 버전 2.9부터 지원을 이용할 수 있다.
CONFORMING TO
dup()
, dup2()
: POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.
dup3()
는 리눅스 전용이다.
NOTES
newfd
가 범위를 벗어날 때 dup2()
가 반환하는 오류가 fcntl(..., F_DUPFD, ...)
이 반환하는 오류와 다르다. 어떤 시스템에서는 dup2()
가 F_DUPFD
처럼 EINVAL
을 반환하기도 한다.
newfd
가 열려 있었을 때 close(2) 시점에 오류가 보고되더라도 그 정보가 유실된다. 이 점이 신경쓰이는 경우에 (프로그램이 단일 스레드이고 시그널 핸들러에서 파일 디스크립터를 할당하지 않는 경우가 아니라면) dup2()
호출 전에 newfd
를 닫는 것은 위에서 설명한 경쟁 조건 때문에 올바른 해법이 아니다. 대신 다음과 같은 식의 코드를 쓸 수 있을 것이다.
/* 'newfd'를 복제해서 아래에서 close() 오류 검사에 사용.
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 오류 처리 */
}
}
SEE ALSO
close(2), fcntl(2), open(2), pidfd_getfd(2)
2021-03-22