미새문지

pintOS - project2(Userprog) syscall.c 본문

크래프톤 정글/pintOS

pintOS - project2(Userprog) syscall.c

문미새 2024. 3. 29. 22:18
728x90

syscall.c

 

< macro >

더보기
#define MSR_STAR 0xc0000081         /* 세그먼트 셀렉터 MSR, 64비트 모드로 전환할 때 사용되는 셀렉터를 저장 */
#define MSR_LSTAR 0xc0000082        /* 롱 모드 SYSCALL 목표 주소를 저장하는 MSR */
#define MSR_SYSCALL_MASK 0xc0000084 /* 실행 시 EFLAGS 레지스터에 적용될 마스크를 저장한다. */

MSR_STAR (0xc0000081) : 시스템 콜이나 리턴 시 CS와 SS 세그먼트 레지스터를 로드하는데 사용

MSR_LSTAR 0xc0000082 : 64비트에서 syscall 명령어가 실행될 때, CPU는 이 레지스터에 저장된 주소로 점프MSR_SYSCALL_MASK 0xc0000084 : syscall이 실행될 때 일부 EFLAGS 비트를 클리어하는 데 사용된다.

 

< find_file_descriptor >

더보기
// 파일 디스크립터(fd)에 해당하는 구조체를 찾아 반환한다.
struct file_descriptor *find_file_descriptor(int fd) {
	// 리스트 포인터 fd_table에 현재 스레드의 fd_table을 저장한다. 
	struct list *fd_table = &thread_current()->fd_table;
    
    // fd_table이 없거나 fd가 1보다 작으면 종료
	ASSERT(fd_table != NULL);
	ASSERT(fd > 1);
    
    // fd_table이 비어있으면 NULL반환
	if (list_empty(fd_table)) 
		return NULL;
    
	struct file_descriptor *file_descriptor;
	struct list_elem *e = list_begin(fd_table); // fd_table의 시작 노드를 가져온다.
	ASSERT(e != NULL);
    
    // fd_table의 꼬리까지 순회
	while (e != list_tail(fd_table)) {
    	// file_descriptor에서 e와 같은 fd_elem을 가져온다.
		file_descriptor = list_entry(e, struct file_descriptor, fd_elem);
        
        // file_descriptor의 fd가 인자로 받아온 fd와 동일하면 file_descriptor를 반환
		if (file_descriptor->fd == fd) {
			return file_descriptor;
		}
		e = list_next(e); // e에 리스트의 다음 노드를 저장
	}
	return NULL;
}

 

< syscall_init >

더보기
// 시스템 호출을 초기화
void
syscall_init (void) {
	/* MSR_STAR 레지스터에 사용자 모드와 커널 모드 사이에서 전환될 때 사용되는 
    	세그먼트 셀렉터 값을 설정한다. */
	write_msr(MSR_STAR, ((uint64_t)SEL_UCSEG - 0x10) << 48  |
			((uint64_t)SEL_KCSEG) << 32);
	/* MSR_LSTAR 레지스터에 syscall_entry 함수의 주소를 설정한다. 
    	(시스템 호출이 발생했을 때 실행될 함수를 지정하는 역할) */
	write_msr(MSR_LSTAR, (uint64_t) syscall_entry);

	/* The interrupt service rountine should not serve any interrupts
	 * until the syscall_entry swaps the userland stack to the kernel
	 * mode stack. Therefore, we masked the FLAG_FL. 
	 * 인터럽트 서비스 반올림은 인터럽트 서비스를 제공해서는 안 됩니다
	 * syscall_entry가 userland 스택을 커널 모드 스택으로 스왑할 때까지. 따라서 FLAG_FL을 마스킹했습니다.*/
	write_msr(MSR_SYSCALL_MASK,
			FLAG_IF | FLAG_TF | FLAG_DF | FLAG_IOPL | FLAG_AC | FLAG_NT);

	// 파일 시스템에 대한 접근을 동기화하기 위해 lock을 초기화한다.
	lock_init(&filesys_lock);
}

 

< syscall_handler >

더보기
// 시스템 호출을 받기 위한 핸들러
void
syscall_handler (struct intr_frame *f) {
	struct thread *t = thread_current();
	t->tf = *f;
	
	// 유효하지 않은 주소라면 isnotvaddr을 출력하고 스레드 종료
	if(!is_user_vaddr(f->rsp)){
		printf("isnotvaddr\n");
		thread_exit();
	}
    
	// 주소가 커널 주소에 속하거나 음수인 경우 smaller를 출력하고 스레드 종료
	else if(f->rsp > KERN_BASE || f->rsp < 0){
		printf("smaller\n");
		thread_exit();
	}
    
	// 다음 명령어의 주소를 계산 후, 위의 두 주소검사를 수행해 걸린다면 third condition을 출력하고 스레드 종료
	int addr = (f->rsp + 8);
	if (!is_user_vaddr(addr) || (addr > KERN_BASE || addr<0)) {
		printf ("third condition\n");
		thread_exit();
	}
	
    /* 받아온 인자대로 시스템 호출을 올바르게 수행하기 위해 switch문 실행.
    받아온 인자에 맞게 각 시스템 호출을 올바른 매개변수를 전달해 실행.
    f->R.rax 값에 담는 것은 void를 제외한 return값이 존재하는 함수만 */
	switch(f->R.rax){
	case SYS_HALT:
		halt();
		break;		

	case SYS_EXIT:
		exit(f->R.rdi);
		break;
	
	case SYS_FORK:
		f->R.rax = fork(f->R.rdi, f);
		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;

	default:
		break;
	}
}

 

< halt >

더보기
// 핀토스 종료
void halt(void)
{
	// 강제 종료
	power_off();
}

 

< exit >

더보기
// 현재 유저 프로그램 종료 (status를 반환함)
void exit(int status)
{
	// 현재 스레드의 종료 상태에 인자로 받아온 status로 변경
	thread_current()->exit_status = status;
    // 스레드 종료
	thread_exit();
}

 

< fork >

더보기
// 프로세스를 복제하는 함수
tid_t fork (const char *thread_name, struct intr_frame *f){
	// 인자로 받아온 스레드 이름을 process_fork에 전달한 값을 반환
	return process_fork(thread_name, f);
}

 

< exec >

더보기
// 현재 프로세스를 file로 바꿈
int exec (const char *file){
	// pml4_get_page : 주어진 가상 주소에 해당하는 페이지를 현재 스레드의 페이지 테이블에서 찾는다.
    /* 이 함수가 NULL을 반환하거나 file이 NULL이거나, file이 유효한 사용자 가상 주소가 아니면 -1을 반환 후 종료 */ */
	if(pml4_get_page(thread_current()->pml4, file) == NULL || file == NULL || !is_user_vaddr(file)) 
		exit(-1);

	char* file_in_kernel;
	file_in_kernel = palloc_get_page(PAL_ZERO); // PAL_ZERO는 할당된 페이지를 0으로 초기화한다는 뜻

	// 할당된 페이지가 없으면 exit함수에 -1을 보냄으로써 종료
	if (file_in_kernel == NULL)
		exit(-1);
       
    // 인자로 받은 file을 최대 PGSIZE만큼 file_in_kernel에 복사한다.
	strlcpy(file_in_kernel, file, PGSIZE);
	
    // process_exec에 페이지를 보내서 -1이 반환되면 exec함수도 -1을 반환
	if (process_exec(file_in_kernel) == -1)
		return -1;	
}

 

< wait >

더보기
// 자식 프로세스 tid가 끝날때까지 기다림 & 자식프로세스의 status를 반환함
int wait (tid_t t)
{		
	// process_wait에 thread_id인 tid를 매개로 보내 반환된 값을 반환
	return process_wait(t); 
}

 

< create >

더보기
// file이라는 파일을 만들고, 성공시 true 반환
// 파일 생성은 해당 파일을 열지는 않음. 열기 위해서는 open을 사용
// 메인 쓰레드의 fd_table 리스트에 할당
bool create (const char *file, unsigned initial_size) 
{
	// 가상메모리 주소에 해당하는 물리메모리 주소를 확인하고, 커널의 가상메모리 주소를 반환함
	if(pml4_get_page(thread_current()->pml4, file) == NULL || file == NULL || !is_user_vaddr(file) || *file == '\0') 
		exit(-1);
	
    // filesys_create로 file과 file크기를 보내서 생성 여부를 success에 저장
    // 파일의 동시성 문제를 막기 위해 lock을 사용하여 현재 파일만 관리할 수 있게 함
	lock_acquire(&filesys_lock);
	bool success = filesys_create(file, initial_size);
	lock_release(&filesys_lock);
    
    // 파일의 생성 여부를 반환
	return success;
}

 

<  remove >

더보기
// file이라는 파일을 삭제
// 성공시 true 반환
bool remove (const char *file)
{
	// 가상메모리 주소에 해당하는 물리메모리 주소를 확인하고, 커널의 가상메모리 주소를 반환함
	if(pml4_get_page(thread_current()->pml4, file) == NULL || file == NULL || !is_user_vaddr(file) || *file == '\0') 
		exit(-1);
        
    // create처럼 file이 제거되었는지의 성공 여부를 success에 담고 동시성 문제를 위해 lock을 사용
	lock_acquire(&filesys_lock);
	bool success =  filesys_remove(file);
	lock_release(&filesys_lock);

	// file 제거 여부를 반환
	return success;
}

 

< open >

더보기
// file이라는 파일을 연다
// fd반환
int open (const char *file) 
{
	// 메모리 누수를 위해 파일 생성을 126개로 제한했다.(multi-oom 테스트를 통과하기 위한 제한 조건)
    // 현재 스레드의 마지막 생성 fd가 126이면 스레드를 종료한다.
	if (thread_current()->last_created_fd == 126) {
		exit(126);
	}

	// 가상메모리 주소에 해당하는 물리메모리 주소를 확인하고, 커널의 가상메모리 주소를 반환함
	if(pml4_get_page(thread_current()->pml4, file) == NULL || file == NULL || !is_user_vaddr(file)) 
		exit(-1);
        
    // 동시성 문제를 위해 lock으로 실행 코드를 감싸준다.
	lock_acquire(&filesys_lock);
    
	struct file *open_file = filesys_open(file); // file을 인자로 filesys_open함수를 사용해 변수에 file을 저장한다.
	int fd = -1; // 첫 fd를 -1로 초기화
    
    // 파일이 열리지 않았다면 lock을 해제하고 -1(오류)인 fd를 반환
	if(open_file == NULL){
		lock_release(&filesys_lock);
		return fd;
	}
    
	fd = process_add_file(open_file); // 오픈한 파일을 fd_table에 추가한다.
	
    // fd가 -1이라면(process_add_file에서 file을 추가 못했다면 fd의 처음값인 -1이 저장되있다.) 파일 닫기
    if (fd == -1) 
		file_close(open_file);
	lock_release(&filesys_lock); // 얻어논 lock을 다시 해제
	return fd; // fd_table에 저장된 fd를 반환
}

 

< filesize >

더보기
// 파일 크기를 반환
int filesize (int fd)
{
	// 받아온 인자 fd를 매개로 find_file_descriptor에서 반환받은 파일 디스크립터를 저장
	struct file_descriptor *file_desc = find_file_descriptor(fd);
    
    // 받아오지 못했다면 -1을 반환(에러)
	if(file_desc == NULL)
		return -1;
    
    // 받아왔다면 file_length에 파일디스크립터의 파일을 보내 크기를 가져와 반환
	return file_length(file_desc->file);
}

 

< read >

더보기
// fd에 맞는 파일을 읽어온다.
int read (int fd, void *buffer, unsigned size)
{
	if(pml4_get_page(thread_current()->pml4, buffer) == NULL || buffer == NULL || !is_user_vaddr(buffer) || fd < 0)
		exit(-1);

	struct thread *curr = thread_current();
	struct list_elem *start;
	off_t buff_size;

	// fd가 0이면(표준 입력 상태) input_getc()를 실행
    // input_getc : 입력 버퍼에서 입력된 키를 찾는 함수, 버퍼가 비어있으면 입력될 때까지 대기
	if (fd == 0)
	{
		return input_getc();
	}
    // fd가 0보다 작거나 없거나 1일 때(에러거나 표준 출력일 때) 스레드 종료
	else if (fd < 0 || fd == NULL || fd == 1)
	{
		exit(-1);
	}
	// bad-fd는 page-fault를 일으키기 때문에 page-fault를 처리하는 함수에서 확인
    // 즉, 올바르게 가져온 파일 fd만 이 조건문에 타게 된다.
	else
	{
    	// fd_table 처음부터 끝까지 돌며 read_fd에 파일디스크립터에서 fd 에 맞는 요소를 가져오기
		for (start = list_begin(&curr->fd_table); start != list_end(&curr->fd_table); start = list_next(start))
		{
			struct file_descriptor *read_fd = list_entry(start, struct file_descriptor, fd_elem);
            
            // 가져온 fd 가 인자로 받은 fd 와 동일하면
			if (read_fd->fd == fd)
			{
            	// buff_size에 file_read로 읽어온 바이트 수를 저장해 반환한다.
				lock_acquire(&filesys_lock);
				buff_size = file_read(read_fd->file, buffer, size);
				lock_release(&filesys_lock);
			}
		}
	}
	return buff_size;
}

 

< write >

더보기
// 선언한 버퍼에서 데이터를 읽어와 지정 대상에 쓰는 역할
int write (int fd, const void *buffer, unsigned size)
{
	if(pml4_get_page(thread_current()->pml4, buffer) == NULL || buffer == NULL || !is_user_vaddr(buffer) || fd < 0)
		exit(-1);

	struct thread *curr = thread_current();
	struct list_elem *start;
    // fd == 1이라는 의미는 표준 출력을 의미함. 따라서 화면에 입력된 데이터를 출력하는 함수 pufbuf를 호출.
	// putbuf 함수는 buffer에 입력된 데이터를 size만큼 화면에 출력하는 함수.
	// 이후 버퍼의 크기 -> size를 반환한다.
    // fd == 0 과 fd == 1은 표준 입출력을 의미하는 파일 식별자이기 때문에 해당되는 파일이 존재하지 않는다.
	// 따라서 정상적인 write가 이루어지지 않는다. fd == 1이면 write 함수의 반환값은 0임.
	if (fd == 1)
	{
		putbuf(buffer, size);
		return size;
	}
	else if (fd < 0 || fd == NULL)
	{
		exit(-1);
	}
	for (start = list_begin(&curr->fd_table); start != list_end(&curr->fd_table); start = list_next(start))
	{
		struct file_descriptor *write_fd = list_entry(start, struct file_descriptor, fd_elem);
        // 작성할 fd가 인자 fd와 동일할 시 작성할 파일 바이트수를 반환한다.
		if (write_fd->fd == fd)
		{
			lock_acquire(&filesys_lock);
            // 이 부분 빼고는 read와 코드가 다른게 별로 없다.
			off_t write_size = file_write(write_fd->file, buffer, size);
			lock_release(&filesys_lock);
			return write_size;
		}
	}
}

 

< seek >

더보기
// 열려있는 fd에서 다음에 읽고 쓸 바이트 위치를 pos 위치로 변경해준다.
// 기존에 작성되있던 함수
void seek (int fd, unsigned position)
{

	struct thread *curr = thread_current();
	struct list_elem *start;

	// fd_table 리스트를 돌며 가져온 fd가 인자fd와 같으면 file_seek함수를 실행해 새 pos값을 반환
	for (start = list_begin(&curr->fd_table); start != list_end(&curr->fd_table); start = list_next(start))
	{
		struct file_descriptor *seek_fd = list_entry(start, struct file_descriptor, fd_elem);
		if (seek_fd->fd == fd)
		{
			return file_seek(seek_fd->file, position);
		}
	}
}

 

< tell >

더보기
// 열려있는 fd에서 다음에 읽고 쓸 바이트 위치를 알려준다.
// tell함수도 이미 구현되어 있다.
unsigned tell (int fd)
{
	struct thread *curr = thread_current();
	struct list_elem *start;

	// fd_table 리스트를 돌며 가져온 fd가 인자fd와 같으면 file_tell을 실행해 위치값을 반환
	for (start = list_begin(&curr->fd_table); start != list_end(&curr->fd_table); start = list_next(start))
	{
		struct file_descriptor *tell_fd = list_entry(start, struct file_descriptor, fd_elem);
		if (tell_fd->fd == fd)
		{
			return file_tell(tell_fd->file);
		}
	}
}

 

< close >

더보기
// 열려있는 fd를 닫아준다
void close (int fd) {

	struct thread *curr = thread_current();
	struct list_elem *start;
    
    // 현재 스레드의 fd_table을 순회하며 닫을 fd와 인자 fd가 같으면 파일을 닫고 리스트에서 삭제한다.
	for (start = list_begin(&curr->fd_table); start != list_end(&curr->fd_table); start = list_next(start))
	{
		struct file_descriptor *close_fd = list_entry(start, struct file_descriptor, fd_elem);
		if (close_fd->fd == fd)
		{
			file_close(close_fd->file);
			list_remove(&close_fd->fd_elem);
		}
	}
	return;
}

 

< process_add_file >

더보기
// 인자로 받은 f를 실행중인 스레드의 fd_table에 추가한다.
int process_add_file(struct file *f)
{
	struct thread *curr = thread_current();
	struct file_descriptor *new_fd = malloc(sizeof(struct file_descriptor));

	// 현재 스레드의 fd가 126 이상이면 -1 반환(multi-oom, 메모리 누수를 막기 위함)
	if (curr->last_created_fd >= 126) {
		return -1;
	}

	curr->last_created_fd += 1; // 현재 스레드의 fd값을 1 증가
	new_fd->fd = curr->last_created_fd; // fd에 현재 마지막 fd를 넣기
	new_fd->file = f; // 파일 디스크립터의 file에 인자 f를 넣기
	list_push_back(&curr->fd_table, &new_fd->fd_elem); // fd_table에 마지막fd의 요소를 넣기

	// 파일 디스크립터의 fd를 반환
	return new_fd->fd;
}
728x90