VFS 객체 관리 및 파일 시스템 등록
파일 시스템 등록 절차: 리눅스 커널은 모듈 형태나 정적으로 빌드된 파일 시스템을 동적으로 인식하기 위해 register_filesystem 함수를 사용한다. 이 함수는 전역 리스트에 파일 시스템 타입을 추가하며, 중복 등록을 방지하기 위해 이름 기반 검사를 수행한다. 현재 시스템에 등록된 모든 파일 시스템은 /proc/filesystems를 통해 확인 가능하다.
static struct file_system_type myfs_type = {
.owner = THIS_MODULE,
.name = "myfs",
.mount = myfs_mount_handler,
.kill_sb = kill_block_super,
.flags = FS_REQUIRES_DEV,
};
int register_filesystem(struct file_system_type *fst) {
int err = 0;
struct file_system_type **cursor;
if (!fst || fst->next)
return -EBUSY;
write_lock(&fs_registry_lock);
cursor = locate_fs_by_name(fst->name, strlen(fst->name));
if (*cursor) {
err = -EBUSY;
} else {
*cursor = fst;
}
write_unlock(&fs_registry_lock);
return err;
}
static struct file_system_type** locate_fs_by_name(const char *name, unsigned len) {
struct file_system_type **pos;
for (pos = &global_fs_list; *pos; pos = &(*pos)->next) {
if (strncmp((*pos)->name, name, len) == 0 && !(*pos)->name[len])
break;
}
return pos;
}
마운트와 언마운트: 파일 시스템을 디렉터리 트리에 통합하는 과정은 단순한 등록보다 훨씬 복잡하다. mount 시스템 호출은 새로운 파일 시스템의 루트를 지정된 마운트 포인트에 연결하며, 이때 기존 디렉터리 내용은 일시적으로 가려진다. 이 관계는 struct mount 구조체로 추상화되며, 계층적 마운트 구조를 표현하기 위해 부모-자식 링크가 포함된다.
struct mount {
struct hlist_node hash_node;
struct mount *parent_fs;
struct dentry *mountpoint_dentry;
struct vfsmount vfs_mount;
struct list_head child_mounts;
struct list_head sibling_link;
const char *device_name;
struct list_head global_mount_list;
struct mnt_namespace *namespace_ref;
int unique_id;
};
슈퍼블록 관리: 마운트 과정에서 가장 먼저 초기화되는 것은 슈퍼블록이다. 이 구조체는 파일 시스템의 메타데이터를 담고 있으며, 블록 크기, 최대 파일 크기, 루트 디렉터리 항목 등을 포함한다. 또한 s_op 필드를 통해 파일 시스템별로 구현된 연산 함수들을 가리킨다.
struct super_block {
struct list_head global_list;
dev_t device_id;
unsigned long block_size;
loff_t max_file_size;
struct file_system_type *type;
const struct super_operations *ops;
struct dentry *root_dentry;
struct block_device *backing_dev;
void *private_data;
struct list_lru unused_inodes;
struct list_head active_inodes;
};
슈퍼블록 연산 인터페이스는 다음과 같이 정의된다:
struct super_operations {
struct inode *(*create_inode)(struct super_block *);
void (*mark_dirty)(struct inode *, int);
int (*sync_inode)(struct inode *, struct writeback_control *);
void (*release_super)(struct super_block *);
int (*sync_fs)(struct super_block *, int);
int (*show_stats)(struct seq_file *, struct dentry *);
};
공유 서브트리와 전파 유형: 네임스페이스 간 마운트 이벤트 전파를 제어하기 위해 공유 서브트리 개념이 도입되었다. 각 마운트 포인트는 다음 네 가지 전파 유형 중 하나를 가질 수 있다: SHARED(이벤트 전파), PRIVATE(전파 없음), SLAVE(단방향 전파), UNBINDABLE(바인딩 불가).
표준 VFS 함수: VFS 계층은 다양한 파일 시스템에 대해 일관된 인터페이스를 제공하며, vfs_read, vfs_write 등의 함수를 통해 실제 파일 시스템 구현과 분리된 추상화를 유지한다. 데이터 접근 시 페이지 캐시를 우선 조회하고, 캐시 미스 시 블록 장치에 직접 I/O 요청을 전달한다.
시스템 호출 흐름: 사용자 공간의 read()/write() 호출은 커널 내부에서 VFS 계층을 거쳐 특정 파일 시스템의 구현으로 전달된다. 예를 들어, sys_write → vfs_write → 파일 시스템별 write_iter 순으로 실행된다.
파일 열기/닫기:
int fd = open("/path/to/file", O_RDWR | O_CREAT, 0644);
if (fd != -1) {
write(fd, buffer, size);
close(fd);
}
open()은 경로와 플래그를 받아 파일 디스크립터를 반환하며, O_CREAT 사용 시 세 번째 인자로 권한 모드를 지정할 수 있다. close()는 디스크립터를 해제하고 관련 자원을 회수한다.