리눅스 커널에서 VFS 객체 처리 및 기본 I/O 함수 구조

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_writevfs_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()는 디스크립터를 해제하고 관련 자원을 회수한다.

태그: linux-kernel VFS filesystem mount Superblock

8월 4일 01:20에 게시됨