운영체제 기본/Linux/파일 시스템 콜/openat과 경로 탐색

관리자 (토론 | 기여)님의 2026년 11월 1일 (일) 02:18 판 (관리자님이 운영체제 기본/Linux의 파일 시스템 콜/openat과 경로 탐색 문서를 운영체제 기본/Linux/파일 시스템 콜/openat과 경로 탐색 문서로 이동했습니다: Linux 각론 제목을 계층형 경로로 통일)

openat()은 파일 이름을 열린 파일 객체로 바꾸고, 그 객체를 가리키는 fd를 반환한다. Linux v6.12의 기존 일반 파일을 여는 경로를 먼저 살펴본다.

경로의 시작점

int fd = openat(AT_FDCWD, "/home/ldmsys/a.txt", O_RDONLY);

절대 경로는 프로세스의 루트 디렉터리에서 시작하므로 위 호출의 dirfd는 경로의 시작점을 바꾸지 않는다. 상대 경로라면 AT_FDCWD는 현재 작업 디렉터리를, 디렉터리 fd는 그 열린 디렉터리를 기준으로 삼는다. 예를 들어 /home/ldmsys를 연 디렉터리 fd에 상대 이름 "a.txt"를 전달할 수 있다. 시작 디렉터리, 마운트와 경로 구성요소는 path_init() 및 경로 탐색 함수에서 처리된다.[1]

파일 디스크립터의 예약과 설치

openat 시스템 콜은 do_sys_open()에서 인자를 정리한 뒤 do_sys_openat2()로 이어진다. 이 내부 함수를 사용한다고 해서 사용자 프로그램이 openat2 시스템 콜을 호출한 것은 아니다. 두 시스템 콜이 공통 열기 구현을 공유하는 것이다.[2]

openat()
  -> do_sys_open()
  -> do_sys_openat2()
       build_open_flags()       플래그와 접근 모드 정리
       getname()                사용자 경로 문자열 확보
       get_unused_fd_flags()    빈 fd 슬롯 예약
       do_filp_open()           경로 탐색과 파일 열기
       fd_install()             fd -> struct file 연결 설치
  -> fd 반환

fd 예약과 파일 열기는 별개 단계다. do_filp_open()이 실패하면 put_unused_fd()로 예약을 돌려준다. 성공한 뒤에만 fd_install()로 포인터를 설치하므로, 반환된 fd에는 준비된 열린 파일 객체가 연결된다. 일반적인 성공 경로에서는 사용 가능한 가장 낮은 fd를 얻는다.[3]

이름 탐색과 파일 열기

do_filp_open()은 path_openat()에서 열린 파일 객체를 할당하고 경로를 탐색한다. 기존 일반 파일에서는 link_path_walk()와 open_last_lookups()로 구성요소를 처리한 뒤, do_open()에서 최종 대상과 접근 권한 등을 확인하고 vfs_open()으로 넘긴다. 이름 탐색은 dentry 캐시를 사용할 수 있고, 필요한 경우 파일시스템의 조회 연산을 호출한다. 따라서 openat()마다 디렉터리의 모든 데이터를 디스크에서 다시 읽는 것은 아니다.[1]

/home/ldmsys/a.txt
  -> 시작점과 마운트 결정
  -> home -> ldmsys -> a.txt 이름 탐색
  -> 대상 dentry / inode 확보
  -> 열기 권한과 플래그 검사
  -> struct file 준비
  -> fd 테이블에 설치

do_dentry_open()은 f_inode, f_mapping 등을 연결하고 inode의 i_fop에서 파일 연산표를 얻는다. 등록된 .open 함수가 있으면 이를 호출해 파일시스템이나 드라이버의 추가 상태를 준비한다. 일반 ext4 파일과 procfs 파일이 같은 열기 인터페이스를 사용하면서 서로 다른 읽기 연산을 갖게 되는 연결 지점이다.[2]

디렉터리 엔트리에서 inode와 데이터 블록으로 이어지는 구체적인 ext4 구조는 ext4의 파일 읽기에 설명되어 있다. 경로 탐색이 끝난 뒤에는 fd가 열린 객체를 붙잡고 있으므로, 후속 읽기와 쓰기는 이름 대신 그 객체를 사용한다.

생성과 열기 플래그

기존 파일을 읽기 전용으로 여는 경로에 플래그가 더해지면 열기 과정의 작업도 달라진다.

  • O_CREAT: 마지막 이름이 없으면 파일 생성 경로로 이어진다. 생성 모드는 요청한 mode와 umask, 적용되는 ACL 등의 규칙에 따라 결정된다.
  • O_EXCL | O_CREAT: 같은 이름이 이미 있으면 실패한다.
  • O_TRUNC: 일반 파일을 열면서 길이를 0으로 줄이며 쓰기 권한 검사를 요구한다. 보통 O_WRONLY 또는 O_RDWR와 함께 사용한다. 단순한 fd 생성만이 아니라 파일 내용에 영향을 주는 요청이다.
  • O_CLOEXEC: exec 시 이 fd가 닫히도록 fd 테이블의 close-on-exec 비트를 함께 설정한다. 파일의 읽기·쓰기 위치와는 별개의 디스크립터 속성이다.

생성과 마지막 구성요소의 열기는 파일시스템의 atomic_open 등으로 결합될 수도 있다. 기본 읽기 경로를 이해할 때는 이름 탐색, 열린 파일 준비, fd 설치의 역할을 구분하면 된다.[1][2][3]

오류와 반환값

대상이 없으면 ENOENT, 권한이 부족하면 EACCES, 프로세스가 더 이상 fd를 할당할 수 없으면 EMFILE 같은 오류가 발생한다. 커널은 음수 오류 코드를 반환하며, 일반적인 C 라이브러리 래퍼는 이를 -1과 errno로 전달한다. 열기에 성공하지 못했다면 유효한 새 fd도 얻지 못한 것이다.[4]

참고 문헌

  1. ↑ 1.0 1.1 1.2 Linux v6.12 fs/namei.c, path_init, link_path_walk, open_last_lookups, do_open, path_openat, do_filp_open.
  2. ↑ 2.0 2.1 2.2 Linux v6.12 fs/open.c, do_sys_open, do_sys_openat2, openat, do_dentry_open.
  3. ↑ 3.0 3.1 Linux v6.12 fs/file.c, alloc_fd, get_unused_fd_flags, put_unused_fd, fd_install.
  4. ↑ Linux man-pages, open(2).