리눅스 운영체제는 여러 파일시스템을 지원한다. (ext2, ext3, ext4, ZFS, RasierFS, XFS, msdos, ntfs, ... )
시스템 내에 파티션 별로 각기 다른 파일시스템을 사용할 때 사용자 태스크에서 파일 열기, 읽기, 쓰기, 닫기 와 같은 행동을 수행한다고 가정하면, 사용자 프로세스, 즉 태스크들은 해당 파일이 어느 파티션에 위치하는지 식별하고, 각 파일시스템에 맞는 시스템 콜을 이용하여 파일시스템에 접근해야 한다.
각 파일시스템 별 호출 함수를 직접 호출해서 사용한다는 것은 불편한다.(ext2_create(), ext2_link(), ext2_redpage(), ...., msdos_create(), msdos_readpage(), ....)
그래서 리눅스는 파일시스템과 사용자 태스크 사이에 가상적인(Virtual) 계층을 도입하였다.
사용자 계층에서는 일반화된 open(), read(), write(), close() 등의 단일화된 함수를 통해 파일시스템에 접근할 수 있다.
중간 계층에서는 파일 이름을 확인하고 파일시스템이 어떤 것인지 식별한 후 해당 파일 시스템의 적절한 함수를 호출하여 결과값을 반환해준다.
이것이 VFS(Virtual File System) 개념이다.
VFS는 open(), read(), write(), close()와 같은 시스템 호출이 파일시스템이나 물리적 매체의 종류와 상관없이 동작하게 해주는 역할을 한다.
VFS 주요 객체
슈퍼블록(superblock) 객체 - 마운트된 파일 시스템을 표현
아이노드(inode) 객체 - 파일을 표현
덴트리(dentry) 객체 - 경로를 구성하는 요소인 디렉토리 항목을 표현
파일(file) 객체 - 프로세스가 사용하는 열린 파일을 표현
위 객체들에는 동작 객체가 들어있다. (커널이 객체에 대해 호출하는 함수)
super_opertaions - write_inode(), synsnc_fs() 등과 같이 특정 파일 시스템에 대해 커널이 호출하는 함수
inode_operations - create(), link() 등과 같이 특정 파일에 대해 커널이 호출하는 함수
dentry_operations - d_compare(), d_delete() 등과 같이 특정 디렉토리 항목에 대해 커널이 호출하는 함수
file_operations - read(), write() 등과 같이 열린 파일에 대해 프로세스가 호출하는 함수
사용자 프로세스에서 특정 파일을 open() 하는 상황을 가정해보자.
사용자는 파일 이름을 인자로 open() 호출 시 내부적으로 sys_open() 시스템 호출을 요청한다.
VFS는 파일시스템 내부(ex. ext4)의 open 함수를 호출하고 파일시스템은 내부 함수를 이용해, 필요한 정보를 아이노드 객체에 채워서 반환한다.
VFS는 아이노드 객체를 덴트리 객체에 연결시켜서 사용자의 태스크 구조와 연결해준다.
그렇다면 open() 함수를 호출하는 경우 operation은 어떻게 흘러가는지 알아보자.
사용자 레이어에서 open() 함수 호출 시 시스템 콜 sys_open()이 호출되고
VFS 레이어의 file 구조체의 f_op는 open을 가리킨다. (file->f_op->open)
실제 열려는 파일의 유형에 따라 적합한 파일 연산으로 등록된다.
fifo_open(), blkdev_open(), chrdev_open(), sock_no_open(), 등
파일 유형에 맞는 파일 연산을 f_op 변수에 등록하고 나면, file->f_op->open() 함수를 호출한다.
open()외 read(), write(), close() 등과 같은 연산도 동일한 흐름으로 동작.
리눅스 커널 - 파일 시스템
파일 시스템 개요
Ext 파일시스템
/fs/ext4/ext4.h
가상 파일 시스템 (VFS)