먼저 구현하기 쉬운 친구들부터서술하겠다.
Syscall handler
/* The main system call interface */
void syscall_handler(struct intr_frame *f UNUSED)
{
int syscall_n = f->R.rax; /* 시스템 콜 넘버 */
#ifdef VM
thread_current()->rsp = f->rsp;
#endif
switch (syscall_n)
{
case SYS_HALT:
halt();
break;
case SYS_EXIT:
exit(f->R.rdi);
break;
case SYS_FORK:
thread_current()->parent_if = f;
f->R.rax = fork(f->R.rdi);
break;
case SYS_EXEC:
f->R.rax = exec(f->R.rdi);
break;
case SYS_WAIT:
f->R.rax = wait(f->R.rdi);
break;
case SYS_CREATE:
f->R.rax = create(f->R.rdi, f->R.rsi);
break;
case SYS_REMOVE:
f->R.rax = remove(f->R.rdi);
break;
case SYS_OPEN:
f->R.rax = open(f->R.rdi);
break;
case SYS_FILESIZE:
f->R.rax = filesize(f->R.rdi);
break;
case SYS_READ:
f->R.rax = read(f->R.rdi, f->R.rsi, f->R.rdx);
break;
case SYS_WRITE:
f->R.rax = write(f->R.rdi, f->R.rsi, f->R.rdx);
break;
case SYS_SEEK:
seek(f->R.rdi, f->R.rsi);
break;
case SYS_TELL:
f->R.rax = tell(f->R.rdi);
break;
case SYS_CLOSE:
close(f->R.rdi);
break;
}
}
syscall handler의 역할은 userprog에서 syscall이 발생했을때 stack agument에서 세팅해준 레지스터의 값을 가지고 system call을 실행해주는 역할을 수행해준다.
여기서 rax에는 syscall의 종류가 저장되어있고, rdi, rsi, rdx에는 각각 매개변수가 포함되어 있다.
마지막으로 return값이 있는 syscall같은 경우, rax에 값을 반환해주는 모습을 볼 수 있다.
syscall.c
먼저 구현하기 쉬운 코드들부터 살펴보도록 하겠다.
halt
void
halt (void) {
power_off();
}
진짜 이게 끝임
seek, tell, filesize
// process.c
struct file
*process_get_file(int fd) {
if (fd < FDT_PAGES || fd > FDT_COUNT_LIMIT)
return NULL;
return thread_current()->fdt[fd];
}
void
process_close_file(int fd) {
if (fd < FDT_PAGES || fd > FDT_COUNT_LIMIT || fd == NULL)
return NULL;
struct thread *cur = thread_current();
struct file *open_file = process_get_file(fd);
if (open_file == NULL) return NULL;
cur->fdt[fd] = NULL;
file_close(open_file);
}
// syscall.c
void
seek (int fd, unsigned position) {
struct file *open_file = process_get_file(fd);
if (fd < FDT_PAGES || fd > FDT_COUNT_LIMIT)
return;
if (open_file)
file_seek(open_file, position);
}
unsigned
tell (int fd) {
struct file *open_file = process_get_file(fd);
if (fd < FDT_PAGES || fd > FDT_COUNT_LIMIT)
return;
if (open_file)
return file_tell(open_file);
return NULL;
}
int
filesize (int fd) {
struct file *open_file = process_get_file(fd);
if (open_file)
return file_length(open_file);
return -1;
}
void
close (int fd) {
struct file *open_file = process_get_file(fd);
if (open_file == NULL)
return;
process_close_file(fd);
}
입력받은 fd
의 값이 유효한지 if
문을 통해 검증하고,
현재 진행중인 process
의 fdt(file descripter table)
에서 파일을 찾고 pintos
에서 제공해주는 함수를 호출하면 해결된다.close
함수는 조금 다른데 process_get_file
함수를 통해 받아온 file
을 닫아주는 함수를 따로 작성해야 한다.
check_addr, create, remove
void
check_addr(char *addr) {
struct thread *cur = thread_current();
if (addr == NULL || !is_user_vaddr(addr) || pml4_get_page(cur->pml4, addr) == NULL) exit(-1);
}
bool
create (const char *file, unsigned initial_size) {
check_addr(file);
return filesys_create(file, initial_size);
}
bool
remove (const char *file) {
check_addr(file);
return filesys_remove(file);
}
check_addr은 입력받은 명령어가 kernel영역을 침범하는지, 유효한 값을 입력받았는지 검사하는 함수이다.
create, remove 함수는 명령어의 유효성 체크를 진행하고 pintos에서 제공해주는 filesys 함수를 호출하면 해결된다.
exit
void
exit (int status) {
struct thread *cur = thread_current();
cur->exit_status = status;
printf("%s: exit(%d)\n", cur->name, status);
thread_exit();
}
현재 진행중인 thread
를 입력받은 status
로 업데이트 해주고 thread_exit
를 호출해준다.
여기서 저 양식으로 printf
를 찍어주지 않으면 test
통과가 안되니깐 조심
read, write
struct lock filesys_lock;
void
syscall_init (void) {
write_msr(MSR_STAR, ((uint64_t)SEL_UCSEG - 0x10) << 48 |
((uint64_t)SEL_KCSEG) << 32);
.
.
.
.
lock_init(&filesys_lock);
}
설명에 들어가기 앞서 먼저 read
, write
함수에 사용될 lock
을 init
해줘야 한다.
int
read (int fd, void *buffer, unsigned size) {
int count;
check_addr(buffer);
unsigned char *buff = buffer;
lock_acquire(&filesys_lock);
if (fd == 0) {
char key;
for(int i = 0; i < size; i++) {
key = input_getc();
*buff++ = key;
if (key == '\0') {
count = i;
break;
}
}
} else {
struct file *open_file = process_get_file(fd);
count = file_read(open_file, buffer, size);
}
lock_release(&filesys_lock);
return count;
}
먼저 read
함수에 대해 설명하자면, fd
가 0, 즉 표준 입력이면 문자를 하나씩 읽어와 버퍼에 저장한다.
마지막으로 읽은 문자가 \0, null 종단문자
라면 루프를 종료하고 지금까지 읽은 문자의 개수를 count
에 저장한다.
파일 디스크립터가 0이 아닌경우 (file)이라면 열린 파일의 구조체를 가저온다.
그 후 file_read
함수를 호출해 count
를 return
해준다.
여기서 lock
을 걸어주는 이유는 파일에 접근하는 동안 동시 접근을 막기 위해 락을 걸어준다.
fork, wait
tid_t fork(const char *thread_name) {
return process_fork(thread_name, NULL);
}
int wait (int pid) {
return process_wait(pid);
}
fork
함수와 wait
함수 자체는 별거 없다.syscall
의 요청을 받고 해당 요청을 수행해주는 함수를 호출하면 그만
물론 나중에 우리가 직접 함수의 로직을 짜줘야 한다.
exec
int
exec(const char *cmd_line) {
check_addr(cmd_line);
char *fn_copy;
fn_copy = palloc_get_page (PAL_ZERO);
if (fn_copy == NULL)
exit(-1);
strlcpy (fn_copy, cmd_line, PGSIZE);
if (process_exec(fn_copy) == -1)
exit(-1);
}
exec
함수는 fn_copy = palloc_get_page(PAL_ZERO)
를 호출하여 페이지를 할당하고, 할당된 페이지를 0으로 초기화한다.
그리고 process_exec
함수에 file_name
인 fn_copy
를 매개변수로 호출하면 완료
다음 포스팅에서는 본격적으로 process를 제어하는 로직을 살펴보겠다.
'Krafton_Jungle > pintOS' 카테고리의 다른 글
Project 2 User program - argument_stack (0) | 2024.05.23 |
---|---|
Project 1 Advanced Scheduler (2) | 2024.05.19 |
Project 1 Priority donation (0) | 2024.05.17 |
Project 1 Alarm Clock (0) | 2024.05.11 |
ERROR - mac 핀토스 환경설정 에러 27 of 27 tests failed. (5) | 2024.05.10 |