x86-64 기반 Pint OS 프로젝트 2의 System Calls를 구현하기 위해 공부한 개념을 정리한다.
파일 시스템 콜
파일 조작을 위한 인터페이스를 제공한다. Pint OS - Project 2(System Calls)에서 구현해야 할 파일 관련 시스템 콜은 다음과 같다.
read
정의
int filesize (int fd, void *buffer, unsigned size);
열린 파일 'fd'에서 'size' 만큼의 바이트를 읽고 'buffer'에 저장한다. 실제로 읽은 바이트 크기를 반환하고, 실패했다면 -1을 반환한다. fd가 표준 입력(0)일 때는 input_getc() 함수를 사용해서 키보드 입력을 받는다.
락
include/userprog/syscall.h
struct lock filesys_lock;
userprog/syscall.c
void syscall_init (void) { ... lock_init (&filesys_lock); }
파일 작업을 처리하기 위한 전역변수 락(filesys_lock)을 생성한다.
read ()
int
read (int fd, void *buffer, unsigned size) {
check_address (buffer);
lock_acquire (&filesys_lock);
char *ptr = (char *)buffer;
int bytes_read = 0;
if (fd == STDIN_FILENO) {
for (int i = 0; i < size; i++) {
*ptr++ = input_getc ();
bytes_read++;
}
lock_release (&filesys_lock);
}
else {
if (fd < 2) {
lock_release (&filesys_lock);
return -1;
}
struct file *file = process_get_file (fd);
if (file == NULL) {
lock_release (&filesys_lock);
return -1;
}
bytes_read = file_read (file, buffer, size);
lock_release (&filesys_lock);
}
return bytes_read;
}
- 인자로 받은 buffer의 유효성을 체크한다.
- filesys_lock을 잠근다.
- fd가 표준 입력이라면, input_getc 함수로 키보드 입력을 받는다.
- 아니라면 현재 프로세스에서 fd에 해당하는 file 구조체를 가져와서 file 라이브러리의 file_read 함수를 사용해서 바이트를 읽는다.
- filesys_lock을 풀어준다.
process_get_file ()
struct file *
process_get_file (int fd) {
struct thread *curr = thread_current ();
struct file **fdt = curr->fdt;
if (fd < 2 || fd >= FDT_COUNT_LIMIT)
return NULL;
return fdt[fd];
}
- 현재 프로세스의 파일 스크립터 세트에서 fd에 해당하는 file 구조체를 가져온다.
write
정의
int write (int fd, const void *buffer, unsigned size);
buffer의 size 만큼의 바이트 데이터를 fd에 write한다. fd가 STDOUT_FILENO(표준 출력)인 경우, putbuf() 함수를 사용해서 콘솔에 출력한다. 그 외의 경우에는 process_get_file(fd) 함수를 호출해서 fd에 해당하는 file을 호출해서 file에서 데이터를 읽는다. 파일에 접근할 때는 filesys_lock을 잠가준다.
write ()
int
write (int fd, const void *buffer, unsigned size) {
check_address (buffer);
int bytes_write = 0;
if (fd == STDOUT_FILENO) {
putbuf (buffer, size);
bytes_write = size;
}
else {
if (fd < 2)
return -1;
struct file *file = process_get_file (fd);
if (file == NULL)
return -1;
lock_acquire (&filesys_lock);
bytes_write = file_write (file, buffer, size);
lock_release (&filesys_lock);
}
return bytes_write;
}
- 인자로 받은 buffer의 유효성을 체크한다.
- fd가 STUOUT_FILENO인 경우, 콘솔에 출력한다.
- 그 외의 경우, file_write()를 호출해서 파일에 작성한다.
- 이 때, filesys_lock을 잠가주어야 한다.
seek
정의
void seek (int fd, unsigned position);
fd에서 다음에 읽거나 쓸 바이트의 위치(position)를 지정한다. file 라이브러리에 구현된 함수를 사용하면 간단하다.
seek ()
void
seek (int fd, unsigned position) {
struct file *file = process_get_file (fd);
if (file == NULL)
return;
file_seek (file, position);
}
tell
정의
unsigned tell (int fd);
fd에서 다음에 읽거나 쓸 바이트의 위치를 반환한다. file 라이브러리에 구현된 함수를 사용하면 간단하다.
tell ()
unsigned
tell (int fd) {
struct file *file = process_get_file (fd);
if (file == NULL)
return;
return file_tell (file);
}
close
정의
void close (int fd);
fd를 닫는다. 프로세스를 끄거나 강제 종료할 때 close를 호출함으로써 fd를 명시적으로 닫아주어야 한다.
close ()
void
close (int fd) {
if (fd < 2)
return;
struct file *file = process_get_file (fd);
if (file == NULL)
return;
file_close (file);
process_close_file (fd);
- fd가 0(표준 출력)과 1(표준 입력)인 경우, 닫아선 안 된다.
- fd에 해당하는 file 구조체를 가져온다.
- file 라이브러리의 file_close() 함수를 사용해서 닫는다.
- process_close_file() 함수를 호출해서 현재 스레드의 file descriptor set에서 제거한다.
process_close_file ()
void
process_close_file (int fd) {
struct thread *curr = thread_current ();
struct file **fdt = curr->fdt;
if (fd < 2 || fd >= FDT_COUNT_LIMIT)
return NULL;
fdt[fd] = NULL;
}
- 현재 스레드의 file descriptor set에서 fd에 할당된 값을 제거한다(NULL).
'프로젝트 > Pint OS' 카테고리의 다른 글
[Pint OS] Lazy loading(2) (0) | 2023.06.19 |
---|---|
[Pint OS] Lazy loading(1) (0) | 2023.06.18 |
[Pint OS] 에러: multi-oom (0) | 2023.06.17 |
[Pint OS] System Calls (5) (1) | 2023.06.14 |
[Pint OS] System Calls (4) (0) | 2023.06.11 |