[Operating System] File System API
2020. 6. 2. 17:12ㆍOperating System
- 파일이란 읽고 쓸 수 있는 영구적인 byte array이다.
- 각각의 파일은 inode number라는 low-level 이름을 가지고 있다. (inode 하나가 file 하나를 가리킨다.)
File Name
- inode number
- 하나의 FS에서 각각의 inode number는 유일하다. (다른 FS끼리는 같은 넘버가 사용될 수 있음)
- inode number는 삭제된 뒤에는 재사용 가능하다.
- path
- inode #는 유저에게 친숙하지 않기 때문에 string file name을 따로 정의하고 inode와 매핑시킨다.
- path-inode 간의 매핑에 대한 정보는 root 파일(inode 2번)에 저장된다.
- 따라서 특정 파일이나 directory를 read할 때 root inode부터 읽어야한다.
- 만약
/etc/.bashrc
를 읽는다고 하면, 총 6번의 read가 발생한다. (각각 inode와 data block)
- file descriptor
- 0: stdin, 2: stdout, 3:stderr
Directory
- 디렉토리 역시 low-level name을 가지는 파일이다.
- 디렉토리의 엔트리는 파일이나 다른 디렉토리를 가리킨다.
API
mkdir
,readdir
,rmdir
- file create는
open()
syscall을 통해서 이루어진다.open()
은 fd를 리턴함.- open 된 파일은 current offset 정보를 가진다.(파일 내에서 어디서부터 읽고 쓸지를 정하기 위하여)
File R/W API Strategy
1. inode를 통한 R/W
read(int inode, void *buf, size_t nbyte)
- 단점
- 이름이 기억하기 어렵다.
- inode #를 통해 파일에 대한 정보를 파악하기가 어렵다.
2. path를 통한 R/W
pread(char *path, void *buf, off_t offset, size_t nbyte)
- 단점
- Root 디렉토리부터 타겟 파일까지 순회하면서 inode와 data 블록을 읽어야하기 때문에 오버헤드가 크다.
- fd를 사용하면 오버헤드를 줄일 수 있다.
- Root 디렉토리부터 타겟 파일까지 순회하면서 inode와 data 블록을 읽어야하기 때문에 오버헤드가 크다.
3. File Descriptor를 통한 R/W (Best)
- 처음 딱 한번만 fd를 얻기 위해서 path를 통해
open()
int fd = open(char* path, int flag, mode_t mode)
- 그 다음부터는 fd를 통해 R/W
read(int fd, void *buf, size_t nbytes)
close(fd)
를 통해서 fd를 지울 수 있다.- path의 파일을 찾아 순회하는 과정을 딱 한번만 수행한다. (파일을 open 할 때)
- 파일을 open한 뒤에는 inode를 fd 구조체에 넣고(메모리에 캐싱) 이 fd를 통해서 파일에 R/W를 수행한다. (offset 역시 트래킹된다.)
- 각각의 프로세스마다 fd table이 존재하며, open files을 가리키는 포인터를 담고 있다.
struct file {
struct inode *ip;
uint off;
}
기타 API
파일 삭제
- 명시적으로 파일을 삭제하는 syscall은 존재하지 않으며, 파일의 inode에 대한 참조가 없을 때 Garbage Collect(GC) 된다.
- inode에 대한 참조는 path나 fd를 통해서 이루어진다.
- path는
unlink()
가 호출되었을 때 지워짐unlink()
는 디렉토리 내부에 file이 존재하지 않을 때만 가능하다.
- fd는
close()
가 호출되었을 때 지워짐
- path는
- inode에 대한 참조는 path나 fd를 통해서 이루어진다.
rm -rf
를 했을 때는 파일의 가장 밑부터 파일을 하나하나 지우면서 올라간다.
Hard Link
link(old_pathname, new_pathname)
ln file file2
: file, file2가 같은 데이터를 참조하게 됨- 기존 파일에 대한 새로운 참조가 하나 더 생긴다. 따라서 inode와 data block은 모두 그대로이고, 그 데이터를 가리키는 이름이 두 개가 될 뿐이다.
rm file
을 하면 해당 파일에 대한 ref cnt가 1 감소하며, ref cnt가 0이 되면 GC된다.link()
시에 ref cnt 1 증가unlink()
시에 ref cnt 1 감소stat()
을 통해 ref cnt를 확인할 수 있다.
- directory나 다른 FS의 파일에 대해서는 link가 불가능하다.
Soft Link (Symbolic Link)
ln -s file file2
:-s
옵션을 주면된다.- hard link는 inode를 통해서 데이터에 접근하는 반면, soft link는 절대 경로를 통해 순회를 하며 데이터에 접근하기 때문에 path가 깊어질 수록 link file의 크기도 증가한다.
- ref cnt가 없다.
- 디렉토리에도 link가 가능하다.
FS 관련 API
- FS 생성:
mkfs
- bitmap, inode table과 같은 기본적인 metadata를 initialize하고, 비어있는 root directory를 생성한다.
mount
: 특정 디렉토리를 mounting point로 삼아서 새로운 파일 시스템을 붙여넣는다. mounting point는 새로운 FS의 root 역할을 하게된다.
Memory - Disk Sync
fsync()
: write한 데이터는 곧장 disk에 write 되지 않고 일단 메모리에 버퍼링된다. 만약 disk에 바로 write를 해야하는 상황이라면fsync()
를 통해 buffer flush를 할 수 있다.- 참고) Disk driver에서 자체적으로 optimize를 하는 경우엔 OS에서
fsync
를 했더라도 disk write가 실제로는 이루어지지 않을 수 있다.
- 참고) Disk driver에서 자체적으로 optimize를 하는 경우엔 OS에서
Rename
mv foo bar
rename(char *old, char *new)
: atomicity가 보장되는 함수- Atomicity를 위해서, new file link 생성 $\to$ link 교체 $\to$ old link 삭제
Old link 삭제를 먼저 하면 안 되는 이유
다음과 같은 순서로 파일 renaming이 이루어진다고 해보자.
1. Old link 삭제
2. New link를 생성하여 연결
만약 1과 2 사이에서 컴퓨터가 꺼져버린다면 해당 파일 데이터에 대한 참조가 완전히 날아가버리는 문제가 생긴다. 따라서 renaming을 비롯하여 파일의 데이터를 업데이트할 때는 먼저 새로운 파일 링크를 만들어서 연결을 하고 그 뒤에 old link를 제거해야 crash가 발생하더라도 업데이트 이전의 온전한 데이터 또는 업데이트 이후의 온전한 데이터 둘 중 하나의 상태를 보존할 수 있다.
멀티 프로세스에서의 파일 concurrency
flock()
: 여러개의 프로세스에서 같은 파일을 동시에 수정하는 것을 막기 위한 lock
'Operating System' 카테고리의 다른 글
[Operating System] FFS (Fast File System) (0) | 2020.06.03 |
---|---|
[Operating System] File System Implementation (0) | 2020.06.02 |
[Operating System] RAID (Redundant Array of Inexpensive Disk) (0) | 2020.06.02 |
[Operating System] I/O Devices (0) | 2020.06.02 |
[Operating System] Scheduling (0) | 2020.01.16 |