KAIST PINTOS

system call

움뭄무 2022. 1. 21. 20:09

process.c에 있는 코드들을 건드리기 전에 ../userprog/syscall.c에 있는 나머지 시스템콜 함수 부분을 마무리하기로 했다. 

system call handler

syscall_handler는 다음과 같이 구현했다. 

void
syscall_handler (struct intr_frame *f UNUSED) {
	// TODO: Your implementation goes here.

	uint64_t sysnum = f -> R.rax; // 스택에서 시스템콜 넘버 받아오기
	void *rsp = f -> rsp; // intr_frame의 스택 포인터
	uint64_t argv = f -> R.rsi; //load 마지막에서 rsi에 argv[[0] = 함수 이름 저장했었음
	uint64_t argc = f -> R.rdi; // load 마지막에서 rdi에 argument 개수 저장했었음

	if(!is_user_vaddr(rsp)){ // user 영역의 주소가 아니면
		exit(-1);
	}

	switch(sysnum){
		case SYS_HALT:
			sys_halt();
			break;

		case SYS_EXIT:
			exit(f -> R.rdi);
			break;
			
		case SYS_FORK:
			f -> R.rax = sys_fork(f -> R.rdi, f);
			break;

		case SYS_EXEC:
			f -> R.rax = sys_exec(f -> R.rdi);
			break;

		case SYS_WAIT:
			f -> R.rax = sys_wait(f -> R.rdi);
			break;

		case SYS_CREATE:
			f -> R.rax = sys_create(f -> R.rdi, f -> R.rsi);
			break;
		
		case SYS_REMOVE:
			f -> R.rax = sys_remove(f -> R.rdi);
			break;

		case SYS_OPEN:
			f -> R.rax = sys_open(f -> R.rdi);
			break;

		case SYS_FILESIZE:
			f -> R.rax = sys_filesize(f -> R.rdi);
			break;

		case SYS_WRITE:
			f -> R.rax = sys_write(f -> R.rdi, f -> R.rsi, f -> R.rdx);
			break;

		case SYS_SEEK:
			sys_seek(f -> R.rdi, f -> R.rsi);
			break;

		case SYS_TELL:
			f -> R.rax = sys_tell(f -> R.rdi);
			break;

		case SYS_CLOSE:
			sys_close(f -> R.rdi);
			break;

		default:
			thread_exit();
			break;
	}
}

 

system call

다음으로는 handler가 sysnum을 받아 호출하는 각각의 함수이다. 

sys_open(), sys_tell(), sys_seek()를 제외한 함수들은 지난 주에 미리 짜놨으니까 코드만 백업해놓고,

아직 구현하지 않은 세 함수만 다루려고 한다. 

 

▶ sys_halt()

void sys_halt (void){
	power_off();	
}

▶ exit()

void exit (int status){
	/*  process termination message 출력; printf ("%s: exit(%d)\n", ...);
	 Whenever a user process terminates,
	  because it called exit or for any other reason, print the process's name and exit code */
	printf("%s: exit(%d)\n", thread_name(), status);

	struct thread *curr = thread_current();
	curr -> is_exited = true;
	curr -> exit_status = status; // 프로세스 디스크립터에 exit status 저장
	thread_exit();

	return;
}

▶ fork()

다른 함수들과 다른 점이 있다면, sys_fork()는 다른 함수들과 다르게 syscall.h 파일에 선언하고 include 해주는 게 아니라 syscall.c에 로컬으로 선언했다는 것이다. 

왜?

pid_t sys_fork(const char *thread_name, struct intr_frame* f){
	lock_acquire(&syscall_lock);
	tid_t result = process_fork(thread_name, f);
	lock_release(&syscall_lock);

	return result;
}

▶ sys_exec()

int sys_exec (const char *cmd_line){
	if (!is_user_vaddr(cmd_line)) exit(-1);

	if (&cmd_line != NULL)
		return process_exec(cmd_line);

	return -1;
}

▶  sys_wait()

int sys_wait (pid_t pid){
	if (pid == NULL) return -1;
	int result = process_wait(pid);

	return result;
}

 

▶ sys_create()

bool sys_create (const char *file, unsigned initial_size){
	if (file == NULL) exit(-1);

	lock_acquire(&syscall_lock);
	bool result = filesys_create(file, initial_size);
	lock_release(&syscall_lock);

	return result;
}

 

▶ sys_remove

bool sys_remove (const char *file){
	if (file == NULL) return false;

	lock_acquire(&syscall_lock);
	bool result = filesys_open(file);
	lock_release(&syscall_lock);

	return result;
}

 

▶ sys_filesize()

int sys_filesize (int fd){
	struct file *f = thread_current() -> fd_table[fd];
	if (f == NULL) return -1;

	lock_acquire(&syscall_lock);
	int result = file_length(f);
	lock_release(&syscall_lock);
	
	return result;
}

▶ sys_read()

int sys_read (int fd, void *buffer, unsigned size){
	//0: stdin, 1: stdout, 2:stderr, 3~: file
	int result = -1;
	lock_acquire(&syscall_lock);

	if (fd < 0 || fd == 1){
		lock_release(&syscall_lock);
		return result;
	} //예외 처리

	if (fd == 0){
		result = 0;

		for (int i = 0; i < size; i++){
			result++; // 전체 길이
			if (input_getc() == '/n') break;
		}

		lock_release(&syscall_lock);
		return result; // 읽은 길이만큼 반환
	}

	else{
		struct file *f = thread_current() -> fd_table[fd];
		if (f == NULL){
			lock_release(&syscall_lock);
			exit(-1);
		} 

		result = file_read(f, buffer, size);
		lock_release(&syscall_lock);
		return result;
	}

	lock_release(&syscall_lock);
	return result;
}

 

▶ sys_write()

int sys_write (int fd, const void *buffer, unsigned size){
	if (!is_user_vaddr(buffer)) exit(-1);

	int result = -1;

	if (fd < 0) return result;

	if (fd == 1){
		putbuf(buffer, size);
		result = size;
	}

	else{
		lock_acquire(&syscall_lock);
		// struct file *f = get_file(fd);
		struct file *f = thread_current() -> fd_table[fd];

		if (f == NULL){
			lock_release(&syscall_lock);
			return 0;
		}

		result = file_write(f, buffer, size);
	}
	
	lock_release(&syscall_lock);
	return result;

}

▶ sys_seek()

 

void sys_seek (int fd, unsigned position){
	struct file *f = thread_current() -> fd_table[fd];
	if (f == NULL) return;

	lock_acquire(&syscall_lock);
	file_seek(f, position);
	lock_release(&syscall_lock);

	return;
}

▶ sys_open()

 

먼저 깃북에 나와있는 설명을 보자. 

Opens the file called file. Returns a nonnegative integer handle called a "file descriptor" (fd), or -1
 if the file could not be opened. File descriptors numbered 0 and 1 are reserved for the console: fd 0 (STDIN_FILENO) is standard input, fd 1 (STDOUT_FILENO) is standard output. The open system call will never return either of these file descriptors, which are valid as system call arguments only as explicitly described below. Each process has an independent set of file descriptors. File descriptors are inherited by child processes. When a single file is opened more than once, whether by a single process or different processes, each open returns a new file descriptor. Different file descriptors for a single file are closed independently in separate calls to close and they do not share a file position. 
You should follow the linux scheme, which returns integer starting from zero, to do the extra.

흐름을 짜보자.

0. syscall_lock을 획득한다.

1. 파일을 연다. 연 파일이 NULL 일 경우, 락 release한 뒤 -1 반환

2. 연 파일을 fd_table에 넣어줘야한다. fd테이블의 index 번째에 넣어주고, index를 증가시켜준다. (파일의 최대 개수는 128개로 제한되어있다.)

 

일단은 허술하게라도 필수적인 기능만 넣어준 것 같은데, 나중에 전반적으로 수정할 때 한 번 더 손 볼 예정이다. 

int sys_open (const char *file){
	if (file == NULL)
		exit(-1);
	
	lock_acquire(&syscall_lock);
	struct file *f = filesys_open(file);
	if (f == NULL){
		lock_release(&syscall_lock);
		return -1;
	}

	struct thread *curr = thread_current();
	if (curr -> fd_table == NULL || curr -> index > 129){
		file_close(file);
		lock_release(&syscall_lock);
		return -1;
	}
	curr -> fd_table[curr -> index] = file;
	curr -> index++;
	
	lock_release(&syscall_lock);
	return curr -> index - 1;
}

 

▶ sys_tell()

: reports current position in a file

Q. fd 값이 0, 1, 2같은 게 들어간다면?

unsigned sys_tell (int fd){
	struct file *f = thread_current() -> fd_table[fd];
	if (f == NULL)
		return -1;
	
	lock_acquire(&syscall_lock);
	int result = file_tell(fd);
	lock_release(&syscall_lock);

	return result;
}

 

▶ sys_close()

void sys_close (int fd){
	if (fd < 2 || fd >= thread_current() -> index)
		return;

	struct file *f = thread_current() -> fd_table[fd];
	if (f == NULL)
		return;
	
	file_close(f);
	thread_current() -> fd_table[fd] == NULL;
	
	return;
}

 

 

이후 수정 사항

sys_exec(), sys_open(), sys_read(), sys_write(), sys_fork() 함수 시작 부분에 user 영역의 주소인지 확인해주는 코드 삽입