NAME
open, openat, creat - 파일을 열고 때에 따라 생성하기
SYNOPSIS
#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);
/* openat2(2)에서 따로 설명: */
int openat2(int dirfd, const char *pathname,
const struct open_how *how, size_t size);
glibc 기능 확인 매크로 요건 (feature_test_macros(7) 참고):
openat()
:-
- glibc 2.10부터:
_POSIX_C_SOURCE >= 200809L
- glibc 2.10 전:
_ATFILE_SOURCE
DESCRIPTION
open()
시스템 호출은 pathname
에 지정한 파일을 연다. 지정한 파일이 존재하지 않는 경우 선택적으로 (flags
에 O_CREAT
을 지정한 경우) open()
에서 생성할 수도 있다.
open()
의 반환 값은 파일 디스크립터이다. 이는 작은 음수 아닌 정수이며 이후의 시스템 호출들(read(2), write(2), lseek(2), fcntl(2) 등)에서 그 열린 파일을 가리키는 데 쓴다. 호출이 성공하면 프로세스에서 현재 열려 있지 않은 가장 낮은 번호의 파일 디스크립터를 반환하게 된다.
기본적으로 새 파일 디스크립터는 execve(2)를 거칠 때 계속 열려 있도록 설정돼 있다. (즉 fcntl(2)에서 설명하는 파일 디스크립터 플래그 FD_CLOEXEC
가 처음에는 비활성화돼 있다.) 아래에서 설명하는 O_CLOEXEC
플래그를 써서 이 기본 설정을 바꿀 수 있다. 파일 오프셋은 파일 시작점으로 설정돼 있다. (lseek(2) 참고.)
open()
호출은 새로운 열린 파일 기술 항목(open file description)을 만든다. 이는 시스템 전역 열린 파일 테이블의 항목이다. 열린 파일 기술 항목에서는 파일 오프셋과 파일 상태 플래그(아래 참고)를 기록한다. 파일 디스크립터(file descriptor)는 열린 파일 기술 항목에 대한 참조이다. 이 참조는 pathname
이 이후 제거되거나 다른 파일을 가리키게 바뀌더라도 영향을 받지 않는다. 열린 파일 기술 항목에 대한 더 자세한 내용은 NOTES를 보라.
인자 flags
에는 접근 모드 O_RDONLY
, O_WRONLY
, O_RDWR
중 하나가 포함돼야 한다. 각각 파일을 읽기 전용, 쓰기 전용, 읽기/쓰기로 열도록 요청한다.
flags
에 추가로 파일 생성 플래그와 파일 상태 플래그를 0개 이상 비트 OR 할 수 있다. 파일 생성 플래그는 O_CLOEXEC
, O_CREAT
, O_DIRECTORY
, O_EXCL
, O_NOCTTY
, O_NOFOLLOW
, O_TMPFILE
, O_TRUNC
이다. 파일 상태 플래그는 아래 나열된 플래그들 중 나머지 전부이다. 이 두 플래그 종류의 차이는 파일 생성 플래그가 열기 동작 자체의 동작 방식에 영향을 주는 반면 파일 상태 플래그는 이어지는 I/O 동작들의 동작 방식에 영향을 준다는 점이다. 파일 상태 플래그는 읽어 올 수 있으며 (일부 경우에) 변경할 수 있다. 자세한 내용은 fcntl(2) 참고.
파일 생성 플래그 및 파일 상태 플래그에 대한 전체 목록은 다음과 같다.
O_APPEND
-
파일을 덧붙이기 모드로 연다. write(2) 전마다 파일 오프셋을 lseek(2)으로 하듯 파일 끝으로 옮긴다. 그 파일 오프셋 변경과 쓰기 동작은 한 개의 원자적 단계로 수행된다.
여러 프로세스가 동시에 파일에 데이터를 덧붙이는 경우 NFS 파일 시스템 상에선
O_APPEND
를 해도 파일 내용에 오류가 생길 수 있다. 파일에 덧붙이기 동작을 NFS에서 지원하지 않아서 클라이언트 커널에서 그걸 모사해야 하는데, 경쟁 조건 없이 하기가 불가능하기 때문이다. O_ASYNC
- 시그널 주도 I/O를 켠다. 이 파일 디스크립터에서 입력이나 출력이 가능해지면 시그널(기본은
SIGIO
이되 fcntl(2)로 변경 가능)을 생성한다. 이 기능은 터미널, 유사 터미널, 소켓, 그리고 (리눅스 2.6부터) 파이프 및 FIFO에 대해서만 사용 가능하다. 자세한 내용은 fcntl(2)을 보라. 아래 BUGS도 참고. O_CLOEXEC
(리눅스 2.6.23부터)-
새 파일 디스크립터에 exec에서 닫기 플래그를 켠다. 이 플래그를 지정하면 프로그램에서 따로 fcntl(2)
F_SETFD
동작으로FD_CLOEXEC
플래그를 설정하지 않아도 된다.참고로 일부 다중 스레드 프로그램에서는 이 플래그 사용이 필수적이다. 한 스레드가 파일 디스크립터를 열어서 fcntl(2)로 exec에서 닫기 플래그를 설정하려는데 동시에 다른 스레드에서 fork(2)와 execve(2)를 하는 경우에서 별도의 fcntl(2)
F_SETFD
동작으로FD_CLOEXEC
플래그를 설정하는 방식은 경쟁 조건을 피하는 데 충분치 않기 때문이다. 경쟁으로 인해 실행 순서에 따라선open()
이 반환한 파일 디스크립터가 fork(2)로 생긴 자식 프로세스에서 실행하는 프로그램에게 의도치 않게 누출될 수 있다. (원칙적으로 이런 종류의 경쟁은 exec에서 닫기 플래그를 설정해야 할 파일 디스크립터를 생성하는 모든 시스템 호출에서 가능하며, 그래서 여러 리눅스 시스템 호출에서O_CLOEXEC
플래그 같은 방법을 제공해서 이 문제를 처리한다.) O_CREAT
-
pathname
이 존재하지 않으면 그 경로명으로 정규 파일을 만든다.새 파일의 소유자(사용자 ID)는 프로세스의 실효 사용자 ID로 설정한다.
새 파일의 그룹 소유(그룹 ID)는 프로세스의 실효 그룹 ID(시스템 V 방식)나 부모 디렉터리의 그룹 ID(BSD 방식) 중 하나로 설정한다. 리눅스에서는 부모 디렉터리에 set-group-ID 모드 비트가 설정돼 있는지 여부에 따라 동작이 다르다. 그 비트가 설정돼 있으면 BSD 방식을 따르고, 아니면 시스템 V 방식을 적용한다. 일부 파일 시스템에서는 mount(8)에서 설명하는 마운트 옵션
bsdgroups
및sysvgroups
에 따라서도 동작이 달라진다.mode
인자는 새 파일 생성 시 적용할 파일 모드 비트를 나타낸다.flags
에O_CREAT
과O_TMPFILE
어느 쪽도 지정하지 않은 경우에는mode
가 무시된다. (따라서 0으로 지정할 수도 있고, 그냥 생략할 수도 있다.)flags
에O_CREAT
이나O_TMPFILE
를 지정할 때는mode
인자를 반드시 제공해야 한다. 주지 않으면 스택에 있는 임의의 바이트들이 파일 모드로 적용된다.실제 적용되는 모드는 프로세스의
umask
를 일반적 방식으로 써서 변경한 값이다. 즉 기본 ACL이 없는 경우 생성 파일의 모드는(mode & ~umask)
이다.참고로
mode
는 새로 생성되는 파일의 향후 접근에만 적용된다. 즉 읽기 전용 파일을 생성하게 돼 있는open()
호출이 읽기/쓰기 가능한 파일 디스크립터를 반환하는 것도 충분히 가능하다.mode
를 위한 다음 상수 심볼들이 제공된다.S_IRWXU
00700 사용자(파일 소유자)가 읽기, 쓰기, 실행 권한을 가짐 S_IRUSR
00400 사용자가 읽기 권한을 가짐 S_IWUSR
00200 사용자가 쓰기 권한을 가짐 S_IXUSR
00100 사용자가 실행 권한을 가짐 S_IRWXG
00070 그룹이 읽기, 쓰기, 실행 권한을 가짐 S_IRGRP
00040 그룹이 읽기 권한을 가짐 S_IWGRP
00020 그룹이 쓰기 권한을 가짐 S_IXGRP
00010 그룹이 실행 권한을 가짐 S_IRWXO
00007 기타가 읽기, 쓰기, 실행 권한을 가짐 S_IROTH
00004 기타가 읽기 권한을 가짐 S_IWOTH
00002 기타가 쓰기 권한을 가짐 S_IXOTH
00001 기타가 실행 권한을 가짐 POSIX에 따르면
mode
에 설정된 다른 비트들의 효과는 명세되어 있지 않다. 리눅스에서는mode
에서 다음 비트들도 받는다.S_ISUID
0004000 set-user-ID 비트 S_ISGID
0002000 set-group-ID 비트 (inode(7) 참고) S_ISVTX
0001000 스티키 비트 (inode(7) 참고) O_DIRECT
(리눅스 2.4.10부터)-
이 파일에 대한 I/O에서 캐시 효과를 최소화하려 노력한다. 일반적으로는 그렇게 하면 성능이 떨어지지만 응용에서 자체 캐싱을 하는 경우처럼 특수한 상황에서는 쓸모가 있다. 사용자 공간 버퍼와 직접 파일 I/O가 이뤄진다.
O_DIRECT
플래그 그 자체에서 데이터를 동기적으로 전송하려는 노력을 하기는 하지만 데이터와 관련 메타데이터가 전송되는O_SYNC
플래그의 보장을 해 주지는 않는다. 동기적 I/O를 보장하려면O_DIRECT
에 더해O_SYNC
를 사용해야 한다. 추가 설명은 아래 NOTES를 보라.블록 장치에 대한 비슷한 (하지만 구식이 된) 인터페이스를
raw(8)
에서 설명한다. O_DIRECTORY
pathname
이 디렉터리가 아니면 열기가 실패하도록 한다. 이 플래그는 커널 버전 2.1.126에서 추가된 것인데, FIFO 내지 테이프 장치에서 opendir(3)을 호출할 때의 서비스 거부 문제를 피하기 위해서였다.O_DSYNC
-
파일에서의 쓰기 동작이 동기 I/O 데이터 무결성 완료 요건에 따라 완료되게 한다.
write(2)(또는 비슷한 함수)가 반환하는 시점에 출력 데이터가, 그리고 그 데이터를 가져오는 데 필요할 파일 메타데이터가 있으면 그것까지 기반 하드웨어로 전송돼 있다. (즉 각 write(2) 뒤에 fdatasync(2) 호출을 붙인 것과 같다.) 아래 NOTES를 보라.
O_EXCL
-
이 호출에서 꼭 파일을 만들도록 한다. 이 플래그가
O_CREAT
와 함께 지정돼 있는데pathname
이 이미 존재하면open()
이EEXIST
오류로 실패한다.그 두 플래그가 지정된 경우에는 심볼릭 링크를 따라가지 않는다.
pathname
이 심볼릭 링크면 그게 어디를 가리키는지와 상관없이open()
이 실패한다.일반적으로
O_CREAT
없이 사용 시O_EXCL
의 동작 방식은 규정돼 있지 않다. 여기에 한 가지 예외가 있는데, 리눅스 2.6 및 이후에서는pathname
이 블록 장치를 가리키는 경우O_EXCL
을O_CREAT
없이 쓸 수 있다. 그 블록 장치를 시스템에서 사용 중이면 (가령 마운트 돼 있으면)open()
이EBUSY
오류로 실패한다.NFS인 경우, 커널 2.6 내지 이후에서 NFSv3 내지 이후 버전을 쓸 때만
O_EXCL
을 지원한다. 락킹 작업 수행에 있어 이에 의존하는 프로그램이 있다면O_EXCL
을 지원하지 않는 NFS 환경에서 경쟁 조건이 있게 된다. 락 파일을 이용해 원자적 파일 락킹을 수행하고 싶은데 NFS의O_EXCL
지원에 의지하는 건 피하고 싶은 이식 가능한 프로그램에서는 같은 파일 시스템 상에 (가령 호스트명과 PID를 합쳐서) 유일한 파일을 만들고 link(2)를 사용해 그 락 파일에 대한 링크를 만들 수 있다. link(2)가 0을 반환한다면 락이 성공한 것이다. 그렇지 않은 경우 그 유일한 파일에 stat(2)을 해서 링크 카운트가 2로 올라갔는지 확인한다. 그렇다면 그 경우 역시 락이 성공한 것이다. O_LARGEFILE
- (LFS)
off_t
로 크기를 표현할 수 없는 (하지만off64_t
로는 할 수 있는) 파일을 여는 것을 허용한다. 이 정의를 쓸 수 있으려면 (어떤 헤더 파일도 포함시키기 전에)_LARGEFILE64_SOURCE
매크로가 정의돼 있어야 한다. 하지만 32비트 시스템에서 큰 파일에 접근하는 바람직한 방법은 (O_LARGEFILE
을 쓰는 게 아니라) 기능 확인 매크로_FILE_OFFSET_BITS
를 64로 설정하는 것이다. (feature_test_macros(7) 참고.) O_NOATIME
(리눅스 2.6.8부터)-
파일을 read(2) 할 때 파일 최근 접근 시간(아이노드의
st_atime
)을 갱신하지 않는다.다음 중 한 조건이라도 참인 경우에만 이 플래그를 쓸 수 있다.
-
프로세스의 실효 UID가 파일의 소유자 UID와 일치한다.
-
호출 프로세스가 자기 사용자 네임스페이스에서
CAP_FOWNER
역능을 가지고 있으며 그 네임스페이스에 파일 소유자 UID의 매핑이 있다.
이 플래그는 사용 시 디스크 활동량을 크게 떨어뜨릴 수 있는 색인 프로그램이나 백업 프로그램에서 쓰기 위한 것이다. 모든 파일 시스템에서 이 플래그가 효과가 있지는 않을 수도 있다. 한 예로 서버에서 접근 시간을 유지하는 NFS가 있다.
-
O_NOCTTY
pathname
이 터미널 장치(tty(4) 참고)를 가리키는 경우에 프로세스에 제어 터미널이 없더라도 그 장치가 프로세스의 제어 터미널이 되지 않게 한다.O_NOFOLLOW
-
pathname
의 마지막 부분(즉 basename)이 심볼릭 링크면 열기가ELOOP
오류로 실패한다. 경로명 앞쪽 부분의 심볼릭 링크는 여전히 따라간다. (참고로 이 경우 발생할 수 있는ELOOP
오류는 경로명 선두부 구성 항목들을 해석하는 과정에서 너무 많은 심볼릭 링크를 발견해서 열기가 실패하는 경우와 구별이 불가능하다.)이 플래그는 FreeBSD 확장이며 리눅스에는 버전 2.1.126에 추가되었다. 그 뒤 POSIX.1-2008에서 표준화되었다.
아래의
O_PATH
도 참고. O_NONBLOCK
또는O_NDELAY
-
가능한 경우 파일을 논블로킹 모드로 연다. 그
open()
동작이나 반환된 파일 디스크립터에 대한 이후의 어떤 I/O 동작에서도 호출 프로세스가 대기하지 않게 된다.참고로 이 플래그 설정은 poll(2), select(2), epoll(7) 등의 동작에는 아무 영향도 주지 않는다. 그 인터페이스들은 파일 디스크립터가 "준비" 상태인지를, 즉
O_NONBLOCK
플래그가 설정 안 된 파일 디스크립터에 수행하는 I/O 동작이 블록 할 것인지를 호출자에게 알려줄 뿐이다.참고로 정규 파일과 블록 장치에 대해선 이 플래그가 효과가 없다. 즉
O_NONBLOCK
이 설정돼 있는지와 관계없이 장치 활동이 필요할 때는 I/O 동작이 (잠시) 블록 하게 된다. 결국에는O_NONBLOCK
동작 방식이 구현될 수도 있으므로 응용에서는 정규 파일 및 블록 장치에 이 플래그 지정 시 블로킹 동작을 확신하지 않는 게 좋다.FIFO(이름 있는 파이프) 처리에 대해선 fifo(7)를 보라. 강제적 파일 락 및 파일 리스와 관련한
O_NONBLOCK
의 효과에 대한 설명은 fcntl(2)을 보라. O_PATH
(리눅스 2.6.39부터)-
파일 시스템 트리 내 위치를 나타내는 것과 순수하게 파일 디스크립터 수준에서만 이뤄지는 동작을 수행하는 두 용도에 쓸 수 있는 파일 디스크립터를 얻는다. 파일 자체를 열지 않으며 다른 파일 동작들(가령 read(2), write(2), fchmod(2), fchown(2), fgetxattr(2), ioctl(2), mmap(2))은
EBADF
오류로 실패한다.결과 파일 디스크립터에 다음 동작을 수행할 수 있다.
-
fchdir(2), 파일 디스크립터가 디렉터리를 가리키는 경우 (리눅스 3.5부터)
-
fstat(2) (리눅스 3.6부터)
-
fstatfs(2) (리눅스 3.12부터)
-
파일 디스크립터 플래그 얻기 및 설정하기 (fcntl(2)의
F_GETFD
및F_SETFD
) -
fcntl(2)
F_GETFL
동작으로 열린 파일 상태 플래그 얻기. 반환되는 플래그에O_PATH
비트가 포함돼 있게 된다. -
파일 디스크립터를
openat()
및 기타 "*at()" 시스템 호출의dirfd
인자로 전달하기. 파일이 디렉터리가 아닌 경우에도AT_EMPTY_PATH
로 (또는AT_SYMLINK_FOLLOW
로 procfs를 통해) linkat(2) 하는 것도 포함된다. -
파일 디스크립터를 유닉스 도메인 소켓을 통해 다른 프로세스로 보내기. (unix(7)의
SCM_RIGHTS
참고)
flags
에O_PATH
가 지정돼 있을 시O_CLOEXEC
,O_DIRECTORY
,O_NOFOLLOW
외의 플래그 비트들은 무시한다.O_PATH
플래그로 파일이나 디렉터리를 열 때는 객체 자체에 대한 어떤 권한도 필요치 않다. (하지만 경로 선두부 디렉터리들에 대한 실행 권한은 필요하다.) 이후의 동작에 따라 적절한 파일 권한 검사가 이뤄질 수 있다. (가령 fchdir(2)에서는 파일 디스크립터 인자가 가리키는 디렉터리에 대한 실행 권한이 필요하다.) 반면 파일 시스템 객체를O_RDONLY
로 열어서 참조를 얻으려면 그 객체에 대한 읽기 권한이 호출자에게 있어야 한다. 이후의 동작(가령 fchdir(2), fstat(2))에서 그 객체에 대한 읽기 권한이 필요하지 않더라도 그렇다.pathname
이 심볼릭 링크이고O_NOFOLLOW
플래그가 함께 지정돼 있는 경우에는 그 심볼릭 링크를 가리키는 파일 디스크립터를 반환한다. 이 파일 디스크립터를 fchownat(2), fstatat(2), linkat(2), readlinkat(2)의dirfd
인자에 빈 경로명과 함께 쓸 수 있으며, 그러면 호출이 그 심볼릭 링크에 대해서 동작한다.pathname
이 automount 지점을 가리키는데 아직 작동을 안 해서 거기 다른 파일 시스템이 마운트 돼 있지 않은 경우에는 마운트를 작동시키지 않으면서 그 automount 디렉터리를 가리키는 파일 디스크립터를 반환한다. 그러면 fstatfs(2)를 사용해 그게 실제로 작동 안 한 automount 지점인지 (.f_type == AUTOFS_SUPER_MAGIC
) 알아낼 수 있다.정규 파일에 있어
O_PATH
의 용도 한 가지는 POSIX.1의O_EXEC
와 동등한 기능성을 제공하는 것이다. 다음과 같은 단계를 통해 실행 권한은 있지만 읽기 권한은 없는 파일을 열어서 그 파일을 실행할 수 있다.char buf[PATH_MAX]; fd = open("some_prog", O_PATH); snprintf(buf, PATH_MAX, "/proc/self/fd/%d", fd); execl(buf, "some_prog", (char *) NULL);
O_PATH
파일 디스크립터를 fexecve(3) 인자로 줄 수도 있다. O_SYNC
-
파일에서의 쓰기 동작이 (
O_DSYNC
에서 제공하는 동기 I/O 데이터 무결성 완료가 아니라) 동기 I/O 파일 무결성 완료 요건에 따라 완료되게 한다.write(2)(또는 비슷한 함수)가 반환하는 시점에 출력 데이터와 관련 파일 메타데이터가 기반 하드웨어로 전송돼 있다. (즉 각 write(2) 뒤에 fsync(2) 호출을 붙인 것과 같다.) 아래 NOTES를 보라.
O_TMPFILE
(리눅스 3.11부터)-
이름 없는 임시 정규 파일을 만든다.
pathname
인자가 디렉터리를 지정하며 그 디렉터리의 파일 시스템 내에 이름 없는 아이노드가 생기게 된다. 그렇게 생긴 파일에 써넣은 내용은 그 파일에 이름을 주지 않는 한 마지막 파일 디스크립터가 닫힐 때 사라지게 된다.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_ISUSR | S_IWUSR); /* 'fd'에서 파일 I/O... */ linkat(fd, NULL, AT_FDCWD, "/path/for/file", AT_EMPTY_PATH); /* 호출자에게 (linkat(2)에 AT_EMPTY_PATH를 쓰려면 필요한) CAP_DAC_READ_SEARCH 역능이 없고 proc(5) 파일 시스템이 마운트 되어 있다면 위의 linkat(2) 호출을 다음으로 대체할 수 있다. 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
의 주된 용도가 두 가지가 있다.-
향상된 tmpfile(3) 기능성: (1) 닫힐 때 자동으로 삭제되고 (2) 어떤 경로명을 통해서도 절대 도달할 수 없으며 (3) 심볼릭 링크 공격 대상이 아니며 (4) 호출자가 유일한 이름을 만들어 낼 필요가 없는 임시 파일을 경쟁 없는 방식으로 생성한다.
-
처음에는 안 보이는 파일을 만들고서 데이터를 채우고 적절한 파일 시스템 속성을 갖도록 조정(fchown(2), fchmod(2), fsetxattr(2) 등)한 다음에 (위에 설명한 것처럼 linkat(2)을 써서) 완전한 상태로 파일 시스템 내로 원자적으로 링크 하기.
O_TMPFILE
에는 기반 파일 시스템의 지원이 필요하다. 리눅스 파일 시스템들 중 일부만 지원을 제공한다. 최초 구현에서는 ext2, ext3, ext4, UDF, Minix, tmpfs 파일 시스템에서 지원을 제공했다. 이어서 XFS (리눅스 3.15), Btrfs (리눅스 3.16), F2FS (리눅스 3.16), ubifs (리눅스 4.9) 파일 시스템 지원이 추가되었다. -
O_TRUNC
- 파일이 이미 존재하고 정규 파일이며 접근 모드에서 쓰기를 허용(즉
O_RDWR
나O_WRONLY
)하면 파일을 길이 0으로 잘라낸다. 파일이 FIFO나 터미널 장치 파일이면O_TRUNC
플래그를 무시한다. 그 외 경우에O_TRUNC
의 효과는 명세돼 있지 않다.
creat()
creat()
호출은 flags
를 O_CREAT|O_WRONLY|O_TRUNC
로 해서 open()
을 호출하는 것과 동등하다.
openat()
openat()
시스템 호출은 여기 설명하는 차이점을 빼면 open()
과 똑같이 동작한다.
pathname
에 준 경로명이 상대 경로이면 (상대 경로명에 대해 open()
에서 하듯 호출 프로세스의 현재 작업 디렉터리를 기준으로 하는 게 아니라) 파일 디스크립터 dirfd
가 가리키는 디렉터리를 기준으로 경로명을 해석한다.
pathname
이 상대 경로이고 dirfd
가 특수 값 AT_FDCWD
이면 (open()
처럼) 호출 프로세스의 현재 작업 디렉터리를 기준으로 pathname
을 해석한다.
pathname
이 절대 경로이면 dirfd
를 무시한다.
openat2(2)
openat2(2) 시스템 호출은 openat()
의 확장판이며 openat()
을 포괄하는 기능을 제공한다. openat2(2)에서 따로 설명한다.
RETURN VALUE
성공 시 open()
, openat()
, creat()
은 새 파일 디스크립터(음수 아닌 정수)를 반환한다. 오류 시 -1을 반환하며 오류를 나타내도록 errno
를 설정한다.
ERRORS
open()
, openat()
, creat()
이 다음 오류로 실패할 수 있다.
EACCES
- 파일에 요청한 접근 방식이 허용되지 않거나,
pathname
경로 선두부의 한 디렉터리에 대해 탐색 권한이 거부되었거나, 파일이 아직 존재하지 않고 부모 디렉터리에 대한 쓰기 접근이 허용되지 않는다. (path_resolution(7)도 참고.) EACCES
O_CREAT
을 지정했는데, sysctl 항목protected_fifos
나protected_regular
가 켜져 있고, 파일이 이미 존재하며 FIFO 또는 정규 파일이고, 파일 소유자가 현재 사용자도 아니고 그 파일을 담은 디렉터리의 소유자도 아니며, 담은 디렉터리가 기타 또는 그룹이 쓰기 가능이면서 스티키이다. 자세한 내용은 proc(5)의/proc/sys/fs/protected_fifos
와/proc/sys/fs/protected_regular
설명을 보라.EBUSY
flags
에O_EXCL
을 지정했는데pathname
이 시스템에서 사용 중인 (가령 마운트 되어 있는) 블록 장치를 가리킨다.EDQUOT
O_CREAT
를 지정한 경우에서 파일이 존재하지 않으며 그 파일 시스템 상에서 사용자의 디스크 블록 내지 아이노드 쿼터가 고갈되었다.EEXIST
pathname
이 이미 존재하는데O_CREAT
와O_EXCL
을 썼다.EFAULT
pathname
이 접근 가능한 주소 공간 밖을 가리킨다.EFBIG
EOVERFLOW
를 보라.EINTR
- 느린 장치(가령 FIFO. fifo(7) 참고) 열기가 끝나기를 기다리며 블록돼 있는 동안 호출이 시그널 인터럽트에 의해 중단되었다. signal(7) 참고.
EINVAL
- 파일 시스템에서
O_DIRECT
플래그를 지원하지 않는다. 추가 정보는 NOTES 참고. EINVAL
flags
에 유효하지 않은 값.EINVAL
flags
에O_TMPFILE
을 지정했는데O_WRONLY
나O_RDWR
어느 것도 지정하지 않았다.EINVAL
flags
에O_CREAT
을 지정했는데 새 파일pathname
의 마지막 부분("basename")이 유효하지 않다. (가령 기반 파일 시스템에서 허용하지 않는 문자를 담고 있다.)EINVAL
pathname
의 마지막 부분("basename")이 유효하지 않다. (가령 기반 파일 시스템에서 허용하지 않는 문자를 담고 있다.)EISDIR
pathname
이 디렉터리를 가리키는데 요청한 접근 방식에 쓰기가 수반된다. (즉O_WRONLY
나O_RDWR
를 설정했다.)EISDIR
pathname
이 기존 디렉터리를 가리키며flags
에O_TMPFILE
을O_WRONLY
나O_RDWR
와 함께 지정했는데 이 커널 버전에서O_TMPFILE
기능을 제공하지 않는다.ELOOP
pathname
을 해석하는 동안 너무 많은 심볼릭 링크를 만났다.ELOOP
pathname
이 심볼릭 링크인데flags
에O_NOFOLLOW
를 지정하고O_PATH
는 지정하지 않았다.EMFILE
- 열린 파일 디스크립터 개수에 대한 프로세스별 제한에 도달했다. (getrlimit(2)의
RLIMIT_NOFILE
설명 참고.) ENAMETOOLONG
pathname
이 너무 길다.ENFILE
- 열린 파일 총개수에 대한 시스템 전역 제한에 도달했다.
ENODEV
pathname
이 장치 특수 파일을 가리키는데 대응하는 장치가 존재하지 않는다. (이는 리눅스 커널 버그이다. 이 경우에ENXIO
를 반환해야 한다.)ENOENT
O_CREAT
을 설정하지 않았고 지명한 파일이 존재하지 않는다.ENOENT
pathname
의 어느 디렉터리 요소가 존재하지 않거나 깨진 심볼릭 링크이다.ENOENT
pathname
이 존재하지 않는 디렉터리를 가리키며,flags
에O_TMPFILE
을O_WRONLY
나O_RDWR
와 함께 지정했는데 이 커널 버전에서O_TMPFILE
기능을 제공하지 않는다.ENOMEM
- 지명한 파일이 FIFO인데 파이프용 메모리 할당에 대한 사용자별 경성 제한에 도달했고 호출자에게 특권이 없어서 FIFO 버퍼를 위한 메모리를 할당할 수 없다. pipe(7) 참고.
ENOMEM
- 사용 가능한 커널 메모리가 충분하지 않다.
ENOSPC
pathname
을 생성해야 하는데pathname
을 담은 장치에 새 파일을 위한 공간이 없다.ENOTDIR
pathname
에서 디렉터리로 쓰인 요소가 실제로는 디렉터리가 아니거나,O_DIRECTORY
를 지정했는데pathname
이 디렉터리가 아니다.ENXIO
O_NONBLOCK | O_WRONLY
를 설정했고 지명한 파일이 FIFO인데 그 FIFO를 읽기용으로 열고 있는 프로세스가 없다.ENXIO
- 파일이 장치 특수 파일인데 대응하는 장치가 존재하지 않는다.
ENXIO
- 파일이 유닉스 도메인 소켓이다.
EOPNOTSUPP
pathname
을 담고 있는 파일 시스템에서O_TMPFILE
을 지원하지 않는다.EOVERFLOW
pathname
이 열기에 너무 큰 정규 파일을 가리키고 있다. 일반적인 시나리오는 32비트 플랫폼에서-D_FILE_OFFSET_BITS=64
없이 컴파일 한 응용이 크기가(1<<31)-1
바이트를 넘는 파일을 열려고 하는 경우이다. 위의O_LARGEFILE
도 보라. POSIX.1에 명세된 오류인데, 리눅스 커널 2.6.24 전에선 이 경우에EFBIG
오류를 내놓았다.EPERM
O_NOATIME
플래그를 지정했지만 호출자의 실효 사용자 ID가 파일의 소유자와 일치하지 않으며 호출자에게 특권이 없다.EPERM
- 파일 봉인 때문에 동작이 막혔다. fcntl(2) 참고.
EROFS
pathname
이 읽기 전용 파일 시스템 상의 파일을 가리키는데 쓰기 접근을 요청했다.ETXTBSY
pathname
이 현재 실행 중인 실행 이미지를 가리키는데 쓰기 접근을 요청했다.ETXTBSY
pathname
이 현재 스왑 파일로 사용 중인 파일을 가리키는데O_TRUNC
플래그를 지정했다.ETXTBSY
pathname
이 현재 커널에서 (가령 모듈/펌웨어 적재를 위해) 읽는 중인 파일을 가리키는데 쓰기 접근을 요청했다.EWOULDBLOCK
O_NONBLOCK
플래그를 지정했는데 파일에 호환 불가능한 리스가 잡혀 있다. (fcntl(2) 참고.)
openat()
에서 추가로 다음 오류가 발생할 수 있다.
EBADF
dirfd
가 유효한 파일 디스크립터가 아니다.ENOTDIR
pathname
이 상대 경로명이고dirfd
가 디렉터리 아닌 파일을 가리키는 파일 디스크립터이다.
VERSIONS
리눅스 커널 2.6.16에서 openat()
이 추가되었다. glibc 버전 2.4에서 라이브러리 지원이 추가되었다.
CONFORMING TO
open()
, creat()
: SVr4, 4.3BSD, POSIX.1-2001, POSIX.1-2008.
openat()
: POSIX.1-2008.
openat2(2)는 리눅스 전용이다.
O_DIRECT
, O_NOATIME
, O_PATH
, O_TMPFILE
플래그는 리눅스 전용이다. 그 정의를 이용할 수 있으려면 _GNU_SOURCE
를 정의해야 한다.
O_CLOEXEC
, O_DIRECTORY
, O_NOFOLLOW
플래그는 POSIX.1-2001에는 명세돼 있지 않고 POSIX-1.2008에는 명세돼 있다. glibc 2.12부터 200809L과 같거나 그보다 큰 값으로 _POSIX_C_SOURCE
를 정의하거나 700과 같거나 그보다 큰 값으로 _XOPEN_SOURCE
를 정의하면 그 정의들을 이용할 수 있다.
feature_test_macros(7)에서 지적하듯 _POSIX_C_SOURCE
, _XOPEN_SOURCE
, _GNU_SOURCE
같은 기능 확인 매크로는 어떤 헤더 파일도 포함시키기 전에 정의돼 있어야 한다.
NOTES
리눅스에서는 파일을 열고 싶지만 꼭 읽기나 쓰기를 하려는 건 아닌 경우에 O_NONBLOCK
플래그를 쓸 때가 있다. 예를 들어 장치를 열어서 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
필드가 현재 시간으로 설정된다.
/proc/[pid]/fd
디렉터리의 파일들은 PID가 pid
인 프로세스의 열린 파일 디스크립터들을 보여 준다. /proc/[pid]/fdinfo
디렉터리의 파일들은 그 파일 디스크립터들에 대해 추가 정보를 보여 준다. 두 디렉터리에 대한 더 자세한 내용은 proc(5)을 보라.
리눅스 헤더 파일 <asm/fcntl.h>
에는 O_ASYNC
가 정의돼 있지 않다. 대신 (BSD에서 유래한) 같은 의미의 FASYNC
가 정의돼 있다.
열린 파일 기술 항목
열린 파일 기술 항목(open file description)은 POSIX에서 시스템 전역 열린 파일 테이블의 항목들을 나타내는 데 쓰는 용어다. 다른 맥락에서는 이 객체를 "열린 파일 객체", "파일 핸들", "열린 파일 테이블 항목", (커널 개발자 용어로) struct file
등으로 다양하게 부른다.
파일 디스크립터를 (dup(2) 등으로) 복제할 때 사본은 원래 파일 디스크립터와 같은 열린 파일 기술 항목을 가리키며, 따라서 두 파일 디스크립터가 파일 오프셋과 파일 상태 플래그를 공유한다. 그런 공유가 프로세스 사이에서도 이뤄질 수 있다. fork(2)를 통해 생성된 자식 프로세스는 부모 프로세스의 파일 디스크립터들의 사본을 물려받으며 그 사본은 같은 열린 파일 기술 항목을 가리킨다.
각 파일 open()
마다 새 열린 파일 기술 항목을 만든다. 즉 한 파일 노드에 대응하는 열린 파일 기술 항목이 여럿 있을 수 있다.
리눅스에서 kcmp(2) KCMP_FILE
동작을 사용해 (동일 프로세스의 또는 다른 두 프로세스의) 두 파일 디스크립터가 같은 열린 파일 기술 항목을 가리키는지 확인할 수 있다.
동기 I/O
POSIX.1-2008 "synchronized I/O" 옵션에서는 다양한 동기 I/O 형태를 명세하고 동작 방식을 제어하기 위한 open()
플래그 O_SYNC
, O_DSYNC
, O_RSYNC
를 명세한다. 구현에서 옵션을 지원하는지 여부와 상관없이 적어도 정규 파일에 대한 O_SYNC
사용은 지원해야 한다.
리눅스에서는 O_SYNC
와 O_DSYNC
를 구현하고 O_RSYNC
는 구현하지 않고 있다. glibc에서는 O_RSYNC
를 O_SYNC
와 같은 값을 가지도록 정의하는데, 이는 다소 부정확하다. (HP PA-RISC에서 리눅스 헤더 파일 <asm/fcntl.h>
에 O_RSYNC
가 정의돼 있지만 쓰이지는 않는다.)
O_SYNC
는 동기 I/O 파일 무결성 완료를 제공하는데, 쓰기 동작에서 데이터와 관련 메타데이터 모두를 기반 하드웨어로 플러시 한다는 뜻이다. O_DSYNC
는 동기 I/O 데이터 무결성 완료를 제공하는데, 쓰기 동작에서 데이터를 기반 하드웨어로 플러시 하되 메타데이터 갱신 사항은 이후 읽기 동작이 성공적으로 완료되는 데 필요한 것만 플러시 한다는 뜻이다. 데이터 무결성 완료 방식은 파일 무결성 완료 방식의 보장 수준까지는 필요치 않은 응용에서 디스크 동작 횟수를 줄여 줄 수 있다.
두 완료 방식의 차이를 이해하기 위해 두 가지 파일 메타데이터 조각, 즉 최근 수정 타임스탬프(st_mtime
)와 파일 길이를 생각해 보자. 최근 파일 수정 타임스탬프는 모든 쓰기 동작에서 갱신하지만 파일 길이는 파일 끝에 데이터를 추가하는 쓰기에서만 바꾸게 된다. 그리고 최근 수정 타임스탬프는 읽기의 성공적 완료를 보장하는 데 필요하지 않지만 파일 길이는 필요하다. 따라서 O_DSYNC
에서는 파일 길이 메타데이터 갱신을 플러시 하는 것만 보장한다. (반면 O_SYNC
에서는 최근 수정 타임스탬프 메타데이터도 항상 플러시 하게 된다.)
리눅스 2.6.33 전에서 리눅스는 open()
에 O_SYNC
플래그만 구현하고 있었다. 하지만 그 플래그 지정 시 대다수 파일 시스템에서 실제로는 동기 I/O 데이터 무결성 완료에 해당하는 동작을 제공했다. (즉 O_SYNC
가 실제로는 O_DSYNC
에 해당하는 방식으로 구현되었다.)
리눅스 2.6.33부터 제대로 된 O_SYNC
지원을 제공한다. 하지만 하위 바이너리 호환성을 보장하기 위해 O_DSYNC
는 이전 O_SYNC
와 같은 값으로 정의했고 O_SYNC
는 새로운 (두 비트짜리) 플래그 값으로 정의해서 O_DSYNC
플래그 값을 포함하게 했다. 이렇게 하면 새 헤더로 컴파일 한 응용들이 2.6.33 전 커널에서 적어도 O_DSYNC
동작은 얻게 된다.
C 라이브러리/커널 차이
버전 2.26부터 glibc의 open()
래퍼 함수가 커널의 open()
시스템 호출 대신 openat()
시스템 호출을 이용한다. 어떤 아키텍처에서는 glibc 버전 2.26 전에서도 그렇다.
NFS
NFS의 기반이 되는 프로토콜에 여러 부적절한 부분이 있어서 특히 O_SYNC
와 O_NDELAY
에 영향을 준다.
UID 매핑을 켠 NFS 파일 시스템에서 open()
이 파일 디스크립터를 반환하고서도 가령 read(2) 요청이 EACCES
로 거부될 수도 있다. 이는 클라이언트에서 open()
을 수행할 때 권한 검사는 하지만 UID 매핑은 읽기 및 쓰기 요청 때 서버에서 수행하기 때문이다.
FIFO
FIFO의 읽기 쪽 내지 쓰기 쪽을 여는 동작은 (다른 프로세스나 스레드가) 반대편도 열 때까지 블록 한다. 자세한 내용은 fifo(7) 참고.
파일 접근 모드
flags
에 지정할 수 있는 다른 값들과 달리 접근 모드 값인 O_RDONLY
, O_WRONLY
, O_RDWR
은 개별 비트를 나타내는 게 아니다. 대신 함께 flags
의 하위 두 비트를 규정하며, 각기 0, 1, 2로 정의돼 있다. 달리 말해 O_RDONLY | O_WRONLY
는 논리적 오류이며 전혀 O_RDWR
와 같은 의미가 아니다.
리눅스에서는 flags
에 비표준 접근 모드 3(이진수로 11)을 따로 두고 있는데, 파일에서 읽기 및 쓰기 권한을 검사하되 읽기 및 쓰기에 사용할 수 없는 파일 디스크립터를 반환하는 걸 뜻한다. 일부 리눅스 드라이버에서 장치별 ioctl(2) 동작에만 사용해야 하는 파일 디스크립터를 반환하게 하는 데 이 비표준 접근 모드를 쓴다.
openat()
및 기타 디렉터리 파일 디스크립터 API의 배경
openat()
과 디렉터리 파일 디스크립터 인자를 받는 여타 시스템 호출 및 라이브러리 함수들(즉 fexecveat(2), faccessat(2), fanotify_mark(2), fchmodat(2), fchownat(2), fspick(2), fstatat(2), futimesat(2), linkat(2), mkdirat(2), move_mount(2), mknodat(2), name_to_handle_at(2), open_tree(2), openat2(2), readlinkat(2), renameat(2), statx(2), symlinkat(2), unlinkat(2), utimensat(2), mkfifoat(3), scandirat(3))은 선행 인터페이스에 있는 두 가지 문제를 다룬다. 여기선 openat()
호출을 가지고 설명하지만 다른 인터페이스들의 근거도 비슷하다.
첫째로, 응용에서 openat()
을 쓰면 현재 작업 디렉터리 아닌 디렉터리의 파일을 open()
으로 열 때 발생할 수 있는 경쟁 조건을 피할 수 있다. 그 경쟁 조건은 open()
에 준 디렉터리 선두부의 어느 구성 요소가 open()
호출과 동시에 바뀔 수도 있다는 사실에서 온다. 예를 들어 dir1/dir2/xxx
라는 파일이 존재한다면 dir1/dir2/xxx.dep
라는 파일을 만들고 싶다고 해 보자. 문제는 존재 여부 검사 단계와 파일 생성 단계 사이에 (심볼릭 링크일 수도 있을) dir1
이나 dir2
가 다른 위치를 가리키게 변경될 수도 있다는 것이다. 대상 디렉터리에 대한 파일 디스크립터를 연 다음 그 파일 디스크립터를 (가령) fstatat(2)과 openat()
의 dirfd
인자로 지정한다면 그런 경쟁을 피할 수 있다. dirfd
파일 디스크립터 사용에는 다른 이득도 있다.
-
디렉터리 이름이 바뀌더라도 파일 디스크립터가 그 디렉터리를 안정적으로 참조한다.
-
열린 파일 디스크립터가 있으면 기반 파일 시스템의 마운트가 해제되는 게 방지된다. 파일 시스템 상에 프로세스의 현재 작업 디렉터리가 있는 것과 마찬가지다.
둘째로, openat()
을 쓰면 응용에서 파일 디스크립터(들)을 유지하며 스레드별 "현재 작업 디렉터리"를 구현할 수 있다. (/proc/self/fd/dirfd
를 이용하는 기법으로도 같은 기능성을 얻을 수 있지만 덜 효율적이다.)
이 API들에 쓸 dirfd
인자는 open()
이나 openat()
으로 (O_RDONLY
또는 O_PATH
플래그를 써서) 디렉터리를 열어서 얻을 수 있다. 또는 opendir(3)로 만든 디렉터리 스트림에 dirfd(3)를 적용해서 얻을 수도 있다.
이 API들에 dirfd
인자로 AT_FDCWD
를 주거나 경로명이 절대 경로이면 대응하는 전통적 API와 같은 방식으로 경로명 인자를 다룬다. 하지만 이 경우에도 여러 API에는 flags
인자가 있어서 대응하는 전통적 API에서 이용할 수 없는 기능들에 접근할 수 있다.
O_DIRECT
O_DIRECT
플래그 사용 시 사용자 공간 버퍼의 길이와 주소, 그리고 I/O의 파일 오프셋에 정렬 제약이 있을 수 있다. 리눅스의 정렬 제약은 파일 시스템과 커널 버전에 따라 다르며 아예 없을 수도 있다. 하지만 응용에서 어떤 파일이나 파일 시스템에 대해 그런 제약을 알아낼 수 있는 파일 시스템 독립적 인터페이스는 현재 없다. 어떤 파일 시스템에서는 이를 위한 자체 인터페이스를 제공하기도 하는데, 예를 들어 xfsctl(3)
의 XFS_IOC_DIOINFO
가 있다.
리눅스 2.4에서는 전송 크기, 사용자 버퍼의 정렬, 파일 오프셋이 모두 파일 시스템의 논리적 블록 크기의 배수여야 한다. 리눅스 2.6.0부터는 기반 저장소의 논리적 블록 크기(보통 512바이트)에 정렬된 것으로 충분하다. ioctl(2) BLKSSZGET
동작을 이용하거나 셸에서 다음 명령을 사용해 그 논리적 블록 크기를 알아낼 수 있다.
blockdev --getss
메모리 버퍼가 비공유 매핑(즉 mmap(2) MAP_PRIVATE
플래그로 만든 매핑. 힙과 정적 할당 버퍼에 할당된 메모리 포함)이라면 절대 O_DIRECT
I/O를 fork(2) 시스템 호출과 동시에 실행하지 말아야 한다. 그런 I/O가 있으면 비동기 I/O 인터페이스를 통해 제출된 것이든 프로세스의 다른 스레드에서 이뤄지는 것이든 fork(2) 호출 전에 완료돼야 한다. 그렇게 하지 않으면 부모 프로세스와 자식 프로세스에서 데이터 오염과 규정 안 된 동작이 발생할 수 있다. O_DIRECT
I/O를 위한 메모리 버퍼를 shmat(2)이나 MAP_SHARED
플래그를 쓴 mmap(2)으로 만든 경우에는 이 제약이 적용되지 않는다. 또는 메모리 버퍼에 madvise(2)로 MADV_DONTFORK
조언을 줘서 fork(2) 후 자식에서 사용 가능하지 않게 만든 경우에도 이 제약이 적용되지 않는다.
O_DIRECT
플래그는 SGI IRIX에서 도입되었으며 거기서의 정렬 제약은 리눅스 2.4와 비슷하다. IRIX에는 적절한 정렬 및 크기를 질의할 수 있는 fcntl(2) 호출도 있다. FreeBSD 4.x에서 같은 이름의 플래그를 도입했는데 정렬 제약은 없다.
리눅스에서 O_DIRECT
지원이 추가된 건 커널 버전 2.4.10에서였다. 그 전의 리눅스 커널들은 이 플래그를 그냥 무시한다. 일부 파일 시스템에서 이 플래그를 구현하고 있지 않을 수 있으며, 그 경우 플래그를 쓰면 open()
이 EINVAL
오류로 실패한다.
응용에서 같은 파일에 대해 O_DIRECT
I/O와 보통 I/O를 섞어 쓰는 걸 피하는 게 좋으며 같은 파일의 겹치는 바이트 영역에 대해선 특히 그렇다. 파일 시스템에서 일관성 문제를 올바르게 처리한다 하더라도 전체 I/O 스루풋이 어느 한 모드만 쓰는 경우보다 느릴 것이다. 마찬가지로 응용에서 같은 파일에 대해 파일 mmap(2)과 직접 I/O를 섞어 쓰는 걸 피하는 게 좋다.
NFS에서 O_DIRECT
의 동작 방식은 로컬 파일 시스템에서와 다르다. 구식 커널이나 특정 방식으로 구성한 커널에서는 그 조합을 지원하지 않을 수도 있다. NFS 프로토콜에서는 서버로 플래그를 전달하는 걸 지원하지 않으므로 O_DIRECT
I/O는 클라이언트에서의 페이지 캐시만을 건너뛰게 된다. 즉 서버에서는 여전히 I/O를 캐싱 할 수 있다. 클라이언트에서 서버에게 I/O를 동기적으로 만들어 달라고 요청해서 O_DIRECT
의 동기성 의미론을 유지한다. 그런데 어떤 서버는 이런 경우에, 특히 I/O 크기가 작을 때 성능이 나빠지게 된다. 또 어떤 서버는 클라이언트에게 I/O가 안정적 저장소에 도달했다고 거짓말을 하게 구성돼 있을 수도 있다. 그러면 성능 불이익은 피하지만 서버 전원 장애 시 데이터 무결성에 대한 위험을 얼마간 지게 된다. 리눅스 NFS 클라이언트에서는 O_DIRECT
I/O에 대해 어떤 정렬 제약도 두지 않는다.
요컨대 O_DIRECT
는 잠재적으로 강력한 도구이지만 주의해서 써야 한다. 응용에서 O_DIRECT
이용을 성능 개선 옵션으로 생각해서 기본적으로 비활성화해 두는 걸 권장한다.
BUGS
현재 open()
호출 시에 O_ASYNC
를 지정해서 시그널 주도 I/O를 켜는 게 불가능하다. 그 플래그를 켜려면 fcntl(2)를 사용하라.
커널에서 O_TMPFILE
기능을 지원하는지 여부를 알아내려 할 때 두 가지 다른 오류 코드 EISDIR
과 ENOENT
를 확인해야 한다.
flags
에 O_CREAT
과 O_DIRECTORY
가 같이 지정돼 있고 pathname
으로 지정한 파일이 존재하지 않을 때 open()
이 정규 파일을 만들게 된다. (즉 O_DIRECTORY
를 무시한다.)
SEE ALSO
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), openat2(2), read(2), socket(2), stat(2), umask(2), unlink(2), write(2), fopen(3), acl(5)
, fifo(7), inode(7), path_resolution(7), symlink(7)
2021-03-22