PintOS 파일시스템 및 manipulation에 대한 글입니다.
File manipulation
Open
Remove
Denying Writes to Executables
open()
시스템 콜을 호출했을 때 리턴되는 fd만 커널에 넘겨주고, 실제 파일 링크를 가져오는 일은 커널에서 함.파일시스템 구조
Open File Table의 존재 이유
1. 가상화 및 abstraction
- 모든 프로세스는 하나의 머신과도 같으며, 자신이 독립적으로 자원을 쓴다고 착각해야 함.
- 각각의 프로세스는 독립적인 file discriptor table이 있지만, 컴퓨터 관리 측면에선 ‘System-wide’한 오픈 테이블이 있어야 함
- 이는 로컬변수와 전역변수의 존재 의미와도 같으며, VM에서 user virtual address는 per-process, kernel virtual address는 global(system wide)인 것도 같은 맥락
- 커널 차원의 Open File Table 역할
- 파일마다 여러 프로세스에 의해 참조된(open) 횟수를 count하여, 0이되면 알아서 해제
- vnode table 메타데이터를 찾아가기 위한 중간다리
2. 유연성
- 파일 작업 간소화
- 프로세스 및 Linux 커널 내부의 파일 객체로부터 파일 경로를 분리
- 동기화 용이
- fd를 사용하면 프로세스에서 동일한 파일을 다른 목적 및 위치로 여러 번 열 수 있음
- 프로세스가 동일한 파일에 대해 여러 개의 독립적인 참조를 가질 수 있으므로 동시에 액세스 가능
- 프로세스가 읽기 및 쓰기 작업을 동시에 수행하거나 동일한 파일 내에서 여러 위치를 유지해야 하는 경우 유용
- 예를 들어, 한 fd가 쓰기 작업을 수행하여 파일 위치를 앞당기면 동일한 파일 객체를 참조하는 다른 fd들도 업데이트된 위치를 볼 수 있음
- 메모리 관리
- 여러 파일 기술자가 동일한 파일 객체를 참조할 경우,
- 서로 다른 fd가 기본 파일 관련 데이터 구조를 복제하지 않고도 동일한 파일 객체를 공유 가능
open()
int open (const char file) {
struct file *fileobj = filesys_open(file); // 열 파일 객체 정보를 받기
...
int fd = add_file_to_fd_table(fileobj);
...
return fd;
}
struct file *
file_open (struct inode *inode) {
struct file *file = calloc (1, sizeof *file);
if (inode != NULL && file != NULL) {
file->inode = inode;
file->pos = 0;
file->deny_write = false;
return file;
} else {
inode_close (inode);
free (file);
return NULL;
}
}
dir_lookup (const struct dir *dir, const char *name,
struct inode **inode) {
...
if (lookup (dir, name, &e, NULL)) // 성공시 inode에 할당
*inode = inode_open (e.inode_sector);
...
}
lookup()
: 디렉토리 첫주소에서 오프셋만큼 이동해 파일 위치를 찾음fd_idx++
(인덱스 늘리기)fdt[cur->fd_idx] = file
int add_file_to_fd_table(struct file *file) {
struct thread *cur = thread_current();
struct file **fdt = cur->fd_table;
while (cur->fd_idx < FDCOUNT_LIMIT && fdt[cur->fd_idx]) {
cur->fd_idx++;
}
if (cur->fd_idx >= FDCOUNT_LIMIT)
return -1; // 빈공간이 없으면 에러처리
fdt[cur->fd_idx] = file;
return cur->fd_idx;
}
파일 구조체
/* An open file. */
struct file {
struct inode *inode; /* File's inode. */
off_t pos; /* Current position. */
bool deny_write; /* Has file_deny_write() been called? */
};
inode 구조체
/* In-memory inode. */
struct inode {
struct list_elem elem; /* Element in inode list. */
disk_sector_t sector; /* Sector number of disk location. */
int open_cnt; /* Number of openers. */
bool removed; /* True if deleted, false otherwise. */
int deny_write_cnt; /* 0: writes ok, >0: deny writes. */
struct inode_disk data; /* Inode content. */
};
/* On-disk inode.
* Must be exactly DISK_SECTOR_SIZE bytes long. */
struct inode_disk {
disk_sector_t start; /* First data sector. */
off_t length; /* File size in bytes. */
unsigned magic; /* Magic number. */
uint32_t unused[125]; /* Not used. */
};
return inode->data.length
: 파일 사이즈 리턴
디렉토리 구조체
/* A directory. */
struct dir {
struct inode *inode; /* Backing store. */
off_t pos; /* Current position. */
};
/* A single directory entry. */
struct dir_entry {
disk_sector_t inode_sector; /* Sector number of header. */
char name[NAME_MAX + 1]; /* Null terminated file name. */
bool in_use; /* In use or free? */
};
disk_sector_t
contains information about the sector number on disk where the inode is stored, struct inode_disk
stores the actual contents of the inode on disk.
생각 정리
- OS도 결국은 자료구조다. hard link로 연결된-
- fd는 ‘링크’로서 실제 하드디스크 파일을 가리키고 있고. 전부 주소로서 접근한다.
- 즉, 파일명(우리가 입력하는)과 그 파일 실체를 연결하는 건 하드링크라는 것. 이는 directory entry라고도 한다.
- 하드링크는 프로세스에서 파일을 열 때마다 생기지만, 같은 파일을 가리킬 수 있고.
- 경우에 따라 동시접근에 따른 inconsistency를 방지하기 위해
- deny_write
- read/write시 lock, semaphore를 이용한다.
- remove() 는 실제 하드디스크 파일을 삭제하는 게 아니라 ‘링크’를 삭제하는 것이다.
- 파일 이름과 파일 데이터 사이의 링크를 제거하며, 파일 데이터는 디스크에 남아있음.
- Open File Table은 최근 액세스된 파일들을 추적하는, 모든 프로세스에 의해 접근되는 테이블이며 reference count가 0이 되면 테이블에서 지운다.
- 결국 이러한 directory-based 파일시스템에선 ‘파일명’과 ‘path’를 분리하여 유저프로그램이 fd(식별자)만으로 여러 파일에 읽고쓰게 해주며,
- 다이렉트 접근이 아닌 제한적 접근으로 프로세스로부터 메모리 영역을 보호하기도 한다.
file_deny_write
: 메모리에 프로그램 적재 시, 프로그램 파일에 쓰기 권한 제거file_allow_write
: 프로그램이 종료되었을 때, 프로그램 파일에 쓰기 권한 부여