chapter 13 - The Virtual Filesystem
파일시스템(File System)
저장장치 내에서 데이터를 읽고 쓰기 위해 미리 정해진 약속
파일 저장 및 검색을 용이하도록 유지/관리하는 방법
파일을 어떻게 관리할 것인가에 대한 메커니즘과 정책
파일 + 디렉토리 구조
EXT2, EXT3, FAT, FAT32, NTFS, JFFS, JFFS, JFFS2, ISO 9660
- filesystem 관련 인터페이스를 user-space program에 제공하고 file을 구현하는 kernel subsystem.
- program은 VFS를 통해 different filesystem, different media 간에도 standard Unix system call을 이용해 read와 write가 가능하다.
- 표준 유닉스 파일시스템과 관련한 모든 시스템 콜을 처리하는 kernel software layer
Common Filesystem Interface
- VFS는 open(), read(), write() 같은 system call이 file system이나 physical medium의 종류와 상관 없이 동작하게 해주는 역할을 한다.
- 현대 OS(linux와 같은)가 추상화된 가상 인터페이스를 통해 파일시스템에 접근하기 때문에 이런 상호작용과 generic access가 가능하게 되었다.
Filesystem Abstraction Layer
- 모든 filesystem이 지원하는 basic conceptual interfaces와 data structure를 정의하는 방식으로 동작한다.
- VFS는 하나의 기준 같은것. filesystem을 작성할 때에는 VFS가 기대하는 abstract interface와 data structure를 제공하도록 작성한다. 이를 이용해 커널은 filesystem을 쉽게 다룰 수 있다.
ret = write(fd, bud, len);
- File discriptor
fd
가 지정하는 file의 현재 위치에buf
포인터가 가리키는len
byte의 정보를 기록함. - 이
write
는fd
가 있는 filesystem의 실제 파일 기록 방식을 정하는sys_write()
generic system call을 부른다. - 즉,
write()
(user space) ->sys_write()
(VFS) ->filesystem's write method
(filesystem)->physical media
로 data가 흐른다.
- File discriptor
Unix Filesystems
- Filesystem : data를 특정 구조체에 담는 계층적인 storage.
4가지 file 관련 abstraction을 제공한다 : files, directory entries, inodes, mount points
files, directories, associated control information이 들어있다.
creation, deletion, mounting 동작을 한다.
Unix에서 filesystem은 namespace라는 global 계층의 특정 지점에 mount 된다. 이 namespace는 process마다 한개씩 있다. 모든 filesystem은 하나의 tree 구조를 이루게 된다.
File : byte가 정렬된 문자열. 첫 번째 byte는 파일의 시작을 표시하며 마지막 byte는 파일의 끝을 표시한다.
Directory : 관련 file들을 모아두는 폴더. directory를 중첩해 경로를 구성할 수 있다.
Directory entry : 경로의 각 부분
/home/wolfman/butter
- directory entry : root directory /, home directory, wolfman directory, butter파일 > dentry라고 부름.
File metadata : 접근권한, 크기, 소유자, 생성 시간 등의 파일 관련 정보.
- 파일과 별도로 존재하는 inode(index node) 라는 자료구조에 저장한다.
Super block : filesystem 전체에 대한 정보를 담고있다.
Filesystem metadata : file metadata + superblock
VFS Objects and Their Data Structures
- VFS는 객체지향적이다! C 구조체로 표현된 객체가 common file model을 표현한다.
- VFS의 4가지 primary object type
- superblock object
- inode object
- dentry object
- file object
- 디렉토리는 파일로 취급하기 때문에 디렉토리 객체는 따로 존재하지 않는다.
- 이 primary object에는 operation object가 들어있다. operation object에는 kernel이 primary object에 대해 호출하는 function이 들어있다.
- superblock_operations : write_inode(), sync_fs() … filesystem에 대해 kernel이 호출하는 함수.
- inode_operations : create(), link()… file에 대해 kernel이 호출하는 함수.
- dentry_operations : d_compare(), d_delete()… directory entry에 대해 kernel이 호출하는 함수.
- file_operations : read(), write()… open된 파일에 대해 process가 호출하는 함수.
- VFS가 사용하는 구조체
- file_system_type : 등록된 filesystem을 표현.
- vfsmount : mount 지점 표현.
- fs_struct : 프로세스가 사용하는 filesystem과 파일을 표시. 프로세스별로 있다.
- file
The Superblock Object
- 각 filesystem별로 구현하며, filesystem을 기술하는 정보를 저장.
filesystem super block, filesystem control block에 대응된다.
super_block 구조체
$include/linux/fs.h struct super_block
super_block 생성, 초기화
$fs/super.c sget() -> sget_userns() -> alloc_super static struct super_block *alloc_super( struct file_system_type *type, int flags, struct user_namespace *user_ns)
Superblock Operations
- super_block struct에서의
struct super_operations s_op;
- super block operator table을 가르키는 pointer.
$include/linux/fs.h
struct super_operations
- 이 구조체의 각 항목은 superblock struct에 동작하는 함수를 가리키는 포인터 이다.
- filesystem이 superblock에 대한 어떤 operation을 해야 할 경우, superblock object의 pointer를 이용해 필요한 함수를 찾아간다.
sb->s_op->write_super(sb);
- ilesystem의 superblock(sb)->superblock operator table(s_op)->원하는 function.
- 이 함수들은 모두 process context에서 VFS가 호출한다. dirty_inode() 함수 제외한 나머지는 모두 sleep가능.
- dirty_inode()는 inode가 변경되었을 경우 journal을 갱신해야하기 때문에 중간에 sleep하면 안된다.
The Inode Object
- Kernel이 file이나 directory를 관리하는 데 필요한 모든 정보를 담고 있다. file과 directory 모두 한 개의 inode를 가진다.
- 파일이 생성될 때 만들어진다.
$include/linux/fs.h
struct inode
- What is not in an inode?
- File Name: There is no file name entry in the inode. Instead, another directory entry runs parallel the inode.The reason for separating out the file name from the other metadata is to maintain links to files. Thus, one can have various file names point to the same inode.
- Parent Directory: This is for the same reason file names are not included in the inode. Multiple directories can have directory entries that point to the same file. Therefore, there should not be one single parent directory in the inode.
- Processes that have file open: The processes would be implemented as a linked list. This leads to bad performance and security issues.
Inode Operations
i->i_op->truncate(i)
$include/linux/fs.h
struct inode_operations
The Dentry Object
- VFS는 directory 관련 작업을 하면서 필요할 때에 경로 이름을 나타내는 문자열로부터 동적으로 dentry object를 만든다.
- 디렉토리 entry를 메모리에서 읽으면, VFS에 의해서 dentry structure 의 dentry 객체가 바뀐다.
- 프로세스가 찾은 경로의 각각의 component 로 구성된 dentry객체는 커널에 의해 생성된다.
- dentry객체는 inode와 유사한 구성요소와 연결한다.
/tmp/test
경로명을 찾을 때- 커널은 / 디렉토리를 위한 dentry 객체를 생성한다.
- 두번째 / 디렉토리의 tmp entry 를 위한 dentry 객체를 생성한다.
- 세번째 /tmp 디렉토리의 test entry 를 위한 dentry 객체를 생성한다.
- dentry 객체는 디스크에 대응하는 이미지가 없다.
- dentry구조체에는 변경된 객체를 나타내는 field가 없다.
- dentry객체는 dentry_cache라고 불리는 slab allocator cache에 저장된다.
- dentry객체는 kmem_cache_alloc()와 kmem_cache_free()에 의해 생성과 소멸된다.
// dentry 구조체
$include/linux/dcache.h
struct dentry
// dentry object는 slab allocator cache인 dentry_cache에 저장된다
$mm/slab.c
22 * The memory is organized in caches, one cache for each object type.
23 * (e.g. inode_cache, dentry_cache, buffer_head, vm_area_struct)
24 * Each cache consists out of many slabs (they are small (usually one
25 * page long) and always contiguous), and each slab contains multiple
26 * initialized objects.
// dentry object 할당
$fs/dcache.c
struct dentry *__d_alloc() {
...
dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
}
Dentry State
- Used
- d_inode 항목이 해당 inode를 가리킴(valid inode)
- d_count 값이 양수(object를 사용하는 user가 1명 이상 있음)
- VFS가 사용중이고, 유효한 데이터를 가리키고 있기 때문에 discard될 수 없다.
- Unused
- valid inode
- d_count 값이 0(현재 VFS가 사용하고있지 않음)
- 다시 필요할 때를 대비하여 cache 등에 보관된다. 메모리 확보가 필요할 경우 discard 될 수 있다.
- Negative
- d_inode가 NULL(유효한 inode가 없음)
- 향후 파일명 해석을 빠르게 하기위하여 보관 하기는 한다.(파일이 존재하지 않는다는것 확인할 경우 등)
The Dentry Cache
- Kernel은 dentry object를 dcache 라고 부르는 덴트리 캐시에 저장한다.
- 디스크에서 디렉토리 엔트리를 읽어서 이에 대응 하는 덴트리 객체를 생성하는 일은 비경제적이다.
- 사용을 끝낸 다음에도 다시 사용할 것 같은 덴트리 객체를 계속 메모리에 유지하는 것이 도움이 됨
- dentry cache를 이용하여 덴트리 객체를 캐싱한다.
- 예를 들어, 사용자는 파일을 편집하고, 파일을 컴파일한 다음 다시 편집하고, 인쇄하거나 복사하고, 복사한 파일을 편집한다.
이러한 경우 동일한 파일에 반복하여 접근하게된다. - 덴트리 캐쉬는 3부분으로 이루어져 있다.
- 상태가 used인 덴트리들의 리스트
- inode객체의 필드 I_dentry를 통하여 inode들과 연결된다.
- inode는 다수의 링크를 가질수 있으므로 다수의 dentry object가 존재 할수 있다. 따라서 리스트를 사용한다.
- 최근에 가장 적게 사용된 리스트
- 상태가 unused와 negative인 dentry object에 대한 이중 연결 리스트
- 최신항목이 head에 가깝도록 삽입시간을 기준으로 정렬한다.
- 커널이 메모리를 회수하기 위해 항목을 제거하는 경우 리스트의 꼬리부터 제거한다.
- 해시테이블(hash table)과 해시함수
- 주어진 경로와 관련된 dentry object를 빠르게 연관시키기 위한 dentry_hashtable 배열을 사용하여 해시 테이블을 구현
- 배열의 각 항목은 해시 값이 같은 dentry list를 가리키는 포인터이다.
- 배열의 크기는 시스템의 메모리 용량에 따라 다르다.
- dentry object의 d_hash 필드는 해시 값이 같은 리스트의 인접 항목에 대한 포인터를 포함한다.
fs/dcache.c -> d_hash
- 해시테이블 참조는 d_lookup()을 통하여 수행한다.
fs/dcache.c -> d_looup
⇒ 캐시를 검색하여 일치하는 dentry object가 발견될 경우 해당항목을 반환, 그렇지 않을 경우 직접 변환한 후 dcache에 dentry object를 추가한다.
- dcache_lock (spinlock)l
Dentry Operations
$include/linux/dcache.h
struct dentry_operations
The File Object
- process에 의해 열려있는 파일을 표현하는데 사용하는 object.
- opened file을 메모리 상에 나타낸 것.
- 여러 개의 프로세스가 동시에 하나의 파일을 열고 조작할 수 있으므로 같은 파일에 대하여 여러 개의 파일 객체가 존재할 수 있다.
즉, file object는 opened file에 대한 procecss의 view를 표현해 주는 것.
$include/linux/fs.h
struct file
- file ->
f_dentry
(해당 dentry 객체) -> inode
File Operations
$include/linux/fs.h
struct file_operations
Data Structures Associated with Filesystems
$include/linux/fs.h
struct file_system_type
linux는 수많은 filesystem을 지원하기 때문에, 각 filesystem의 capabilities와 behavior 를 표현하기 위한 구조체가 필요하다.
이 구조체는 filesystem 별로 하나씩 있다.
$include/linux/fs.h struct file_system_type
- filesystem이 실제로 mount된 순간에 vfsmount 구조체가 생성된다.
- 파일 시스템의 특정한 인스턴스, 즉 마운트 포인트(mount point)를 나타내는데 사용된다.
- vsmount의 linked list를 통해 filesystem과 모든 mount 위치에 대한 관계를 추적할 수 있다.
Data Structures Associated with a Process
$include/linux/fdtable.h
struct file_struct
- 각각의 모든 프로세스가 사용중인 파일과 file descriptor에 대한 정보가 들어있다.
$include/linux/fs_struct.h
struct fs_struct
- 현재 프로세스의 현재 작업 directory(pwd) 및 root directory 정보가 들어있다.
$include/linux/mnt_namespace.h
struct mnt_namespace
- 프로세스별로 독자적인 filesystem 체계를 가질 수 있게 한다.