본문으로 이동
  • 현재 모든 문서는 작성 중입니다. 보시는 데 불편함이 있더라도 양해 부탁드립니다.

운영체제 기본/Linux의 파일시스템/VFS와 파일 객체

~ldmsys
관리자 (토론 | 기여)님의 2026년 10월 31일 (토) 08:11 판 (VFS의 경로·dentry·inode·열린 파일과 ext4/procfs 연산 연결 설명)
(차이) ← 이전 판 | 최신판 (차이) | 다음 판 → (차이)

파일시스템 구현 목차

기준은 Linux v6.12다. read() 시스템 호출 경로는 VFS의 파일 객체와 연산 인터페이스를 통해 실제 파일시스템 구현으로 연결된다.

공통 인터페이스와 구현의 분리

프로그램은 ext4의 일반 파일도, /proc/uptime도 경로로 열고 반환된 파일 디스크립터로 읽는다. 그러나 앞의 파일은 저장된 내용을 읽는 대상이고, 뒤의 파일은 부팅 이후 경과 시간과 CPU 유휴 시간을 텍스트로 보여 주는 대상이다. 두 파일에 같은 시스템 콜을 사용할 수 있다고 해서 내부의 데이터 생성 방식까지 같아지는 것은 아니다.

VFS(Virtual File System)는 파일시스템마다 별도의 시스템 콜을 만들지 않고도 이들을 함께 사용할 수 있게 하는 커널 계층이다. 공통 객체와 연산 인터페이스를 정의하고, 실제 대상에 맞는 파일시스템 구현을 호출한다. VFS는 이름으로 대상을 찾고 그 대상의 연산으로 연결하는 공통 경로를 제공한다.[1]

경로에서 파일 객체의 대상을 찾는다

/home/user/note를 열려면 커널은 경로의 구성 요소를 차례로 해석해야 한다. 이때 dentry는 부모 디렉터리 아래의 이름과 대상을 연결하는 메모리 객체다. 이미 캐시된 연결을 사용할 수 없다면 해당 디렉터리의 inode->i_op->lookup() 같은 파일시스템 연산으로 대상을 확인한다. 찾아낸 inode는 파일의 종류, 권한 등과 연산 정보를 담는 객체이며, 파일 이름 자체와는 구분된다. 여러 이름이 같은 inode를 가리킬 수도 있다.[1][2]

경로 탐색에는 dentry만 아니라 마운트 정보도 필요하다. 예를 들어 /proc에 procfs가 마운트되어 있으면 그 지점을 지나면서 탐색 대상 파일시스템이 바뀐다. 그래서 커널의 struct path는 마운트와 dentry를 함께 가리킨다. super_block은 이 과정에서 만나는 파일시스템 인스턴스의 공통 상태를 나타내며, inode도 자신이 속한 super_block을 참조한다. 하나의 경로 공간에 연결된다는 것은 모든 경로가 같은 디스크나 같은 파일시스템에 속한다는 뜻이 아니다.[2][3]

open은 대상에 열린 상태와 연산을 연결한다

대상을 찾는 것만으로 읽기 준비가 끝나지는 않는다. open() 과정에서 마련하는 struct file은 파일 자체를 나타내는 inode와 달리, 그 파일을 열어 둔 상태를 나타낸다. 여기에는 경로인 f_path, 현재 위치인 f_pos, 상태 플래그인 f_flags, 사용할 연산표인 f_op 등이 연결된다. 같은 파일을 별도로 열면 inode는 같더라도 열린 상태와 읽기 위치는 따로 유지할 수 있다.[3]

일반적인 열기 경로에서 do_dentry_open()은 다음과 같이 inode에 등록된 연산표를 가져온다. 이어서 해당 연산표의 open 콜백이 있으면 호출하여 구현별 준비를 수행한다.[4]

f->f_op = fops_get(inode->i_fop);

열기에 성공하면 fd_install()이 프로세스의 파일 디스크립터 테이블에 이 file을 연결한다. 프로그램에 반환되는 정수 fd는 inode 번호가 아니라 이 테이블에서 열린 파일을 찾는 번호다. 이후 읽을 때마다 최초의 경로 문자열을 다시 해석하지 않고, 이미 연결된 file을 통해 대상과 연산을 찾는다.[4]

read는 연결된 구현으로 분기한다

경로 탐색: 이름 + 마운트 -> dentry -> inode
열기 이후: fd -> fdtable -> struct file -> f_op -> 읽기 구현

read()의 커널 경로는 fd로 file을 얻고 vfs_read()에 전달한다. 이 함수는 읽기 가능 여부와 요청 범위 등을 검사한 다음 file->f_op->read가 있으면 호출한다. 그렇지 않고 read_iter가 있으면 new_sync_read()가 버퍼를 iterator 형태로 준비하여 그 콜백을 호출한다. 그러므로 VFS에서 읽기를 제공한다고 해서 각 파일시스템이 반드시 같은 이름의 read 함수를 구현해야 하는 것은 아니다.[5]

ext4 일반 파일은 ext4_file_operations.read_iter에 ext4_file_read_iter()를 등록한다. 보통의 버퍼드 읽기는 여기서 generic_file_read_iter()로 이어진다. 반면 /proc/uptime의 inode에는 proc_iter_file_ops가 연결된다. 그 read_iter 콜백인 proc_reg_read_iter()는 엔트리의 proc_ops.proc_read_iter에 등록된 seq_read_iter()로 이어진다. 이 읽기는 디스크 내용을 가져오는 대신, 등록된 uptime_proc_show()를 통해 커널 상태를 텍스트로 만든다.[6][7]

즉 공통인 부분은 파일 객체를 찾고 연산을 호출하는 계약이다. 디스크 접근 여부와 데이터 생성 방식은 그 뒤의 구현이 결정한다. 페이지 캐시도 모든 VFS 읽기에 강제되는 단계는 아니다. ext4 내부에서도 버퍼드 읽기와 직접 I/O, DAX 경로가 구분된다.[6] 구체적인 흐름은 ext4의 파일 읽기, procfs의 파일 읽기, 페이지 캐시에서 이어서 살펴본다.

참고 문헌

  1. ↑ 1.0 1.1 Linux kernel documentation, Overview of the Linux Virtual File System, Introduction, Directory Entry Cache, The File Object.
  2. ↑ 2.0 2.1 Linux v6.12, fs/namei.c, lookup_fast(), __lookup_slow(), step_into(), handle_mounts().
  3. ↑ 3.0 3.1 Linux v6.12, include/linux/fs.h, struct inode, struct file, struct super_block.
  4. ↑ 4.0 4.1 Linux v6.12, fs/open.c, do_dentry_open(), do_sys_openat2().
  5. ↑ Linux v6.12, fs/read_write.c, ksys_read(), vfs_read(), new_sync_read().
  6. ↑ 6.0 6.1 Linux v6.12, fs/ext4/file.c, ext4_file_operations, ext4_file_read_iter().
  7. ↑ Linux v6.12, fs/proc/inode.c, proc_get_inode(), proc_iter_file_ops, proc_reg_read_iter(); fs/proc/generic.c, proc_single_ops, proc_create_single_data(); fs/proc/uptime.c, proc_uptime_init(), uptime_proc_show().