[PintOS] Project2: System Calls - Ⅰ

김상호·2022년 6월 24일
0

Development Log

목록 보기
34/45

System Calls

과제 목표

  • 이번 과제에서는 시스템 콜 핸들러 및 시스템 콜을 구현하는 것이다. 여기서 시스템 콜이란 사용자가 커널 영역에 접근하고 싶을 때, 원하는 목적을 대신해서 작업하는 프로그래밍 인터페이스이다. 그렇기 때문에 시스템 콜은 커널 모드에서 실행되고, 작업 후 사용자 모드로 복귀한다. pintos에서는 이를 시스템 콜 핸드러를 통해 시스템 콜을 호출한다.

    시스템 콜을 호출할 때, 원하는 기능에 해당하는 시스템 콜 번호를 rax에 담는다. 그리고 시스템 콜 핸들러는 rax의 숫자로 시스템 콜을 호출하고, 해당 콜의 반환값을 다시 rax에 담아서 intr frame(인터럽트 프레임)에 저장한다.

구현

halt();
case SYS_HALT:
	halt();
	break;
void halt(void) {
	// * power_off()를 사용하여 pintos 종료
	power_off();
}
  • 주석에서 설명하고 있듯이 power_off()함수를 호출하면서 pintos를 종료시키는 시스템 콜이다


exit();
case SYS_EXIT:
	exit(f->R.rdi);
	break;
void exit(int status) {
	/*
	* 실행중인 스레드 구조체를 가져옴
	* 프로세스 종료 메시지 출력
	* 출력 양식: "프로세스 이름: exit(종료상태)"
	* thread 종료
	*/ 
	struct thread *cur = thread_current();
	cur->exit_status = status;
	printf("%s: exit(%d)\n", cur->name, status);
	thread_exit();
}
  • 현재 실행 중인 프로세스를 종료시키는 시스템 콜이다. 프로세스를 완전히 종료시키기 전에 thread 구조체에 int형인 exit_status에 인자로 받은 status를 저장한다. 그리고 thread_exit()를 호출하여 thread를 완전히 종료시킨다.

※ 곧 사라질 thread의 exit_status에 status를 저장하는 것은 만약 현재 프로세스가 자식 프로세스여서 부모 프로세스가 wait 상태일 경우, 자식 프로세스가 사라지면서 종료 상태(exit_status)를 부모에게 알려주기 위함이다. 정상적 종료라면 0을 저장한다.

create();
case SYS_CREATE:
	f->R.rax = create(f->R.rdi, f->R.rsi);
	break;
bool create (const char *file, unsigned initial_size) {
	/* 
	* 파일 이름과 크기에 해당하는 파일 생성
	* 파일 생성 성공 시 true 반환, 실패 시 false 반환
	*/
	check_address(file);
	return filesys_create(file, initial_size);
}
  • file을 만드는 시스템 콜이다. 인자로 받은 file 이름과 크기(initial_size)에 해당하는 파일을 생선한다. 파일 생성에 성공하면 true를 반환하고 실패하면 false를 반환한다. filesys_create가 실질적으로 작업을 수행하므로 크게 볼 것은 없다. (파일을 만들고 바로 열지 않는다. 여는 것은 open() 시트템 콜을 사용해서 따로 실행한다.)

  • create를 실행하기 전에 해당 file이 유저 영역에 있는 file인지 확인해야한다. 이는 check_address()로 확인할 수 있다.

remove();
case SYS_REMOVE:
	f->R.rax = remove(f->R.rdi);
	break;
bool remove (const char *file) {
	/* 
	* 파일 이름에 해당하는 파일을 제거
	* 파일 제거 성공 시 true 반환, 실패 시 false 반환
	*/
	check_address(file);
	return filesys_remove(file);
}
  • 파일 이름에 해당하는 파일을 제거한다. 파일 제거 성공 시 true 반환, 실패 시 false 반환한다.

open();
case SYS_OPEN:
	f->R.rax = open(f->R.rdi);
	break;
int open (const char *file) {
	check_address(file);
	struct thread *cur = thread_current();
	struct file *fd = filesys_open(file);
	if (fd) {
		for (int i = 2; i < 128; i++) {
			if (!cur->fdt[i]) {
				cur->fdt[i] = fd;
				cur->next_fd = i + 1;
				return i;
			}
		}
		file_close(fd);
	}
	return -1;
}
  • open()함수는 파일을 열 때 사용하는 시스템 콜이다. 성공 시 fd를 생성하고 반환, 실패 시 -1을 반환한다.

filesize();
case SYS_FILESIZE:
	f->R.rax = filesize(f->R.rdi);
	break;
int filesize (int fd) {
	struct file *file = thread_current()->fdt[fd];
	if (file)
		return file_length(file);
	return -1;
}
  • filesize()함수는 파일의 크기를 알려주는 시스템 콜이다

read();
case SYS_READ:
	f->R.rax = read(f->R.rdi, f->R.rsi, f->R.rdx);
	break; 
int read (int fd, void *buffer, unsigned size) {
	check_address(buffer);
	if (fd == 1) {
		return -1;
	}

	if (fd == 0) {
		lock_acquire(&filesys_lock);
		int byte = input_getc();
		lock_release(&filesys_lock);
		return byte;
	}
	struct file *file = thread_current()->fdt[fd];
	if (file) {
		lock_acquire(&filesys_lock);
		int read_byte = file_read(file, buffer, size);
		lock_release(&filesys_lock);
		return read_byte;
	}
	return -1;
}
  • buffer 파라미터는 읽은 데이터를 저장할 버퍼의 주소값이고, size는 읽을 데이터의 크기이다.
  • fd값이 0일 때는 표준입력이기 때문에 input_getc() 함수를 이용하여 키보드의 데이터를 읽어 byte에 저장한다.

write();
case SYS_WRITE:      
	f->R.rax = write(f->R.rdi, f->R.rsi, f->R.rdx);
	break;
int write (int fd UNUSED, const void *buffer, unsigned size) {
	check_address(buffer);

	if (fd == 0) // STDIN일때 -1
		return -1;

	if (fd == 1) {
		lock_acquire(&filesys_lock);
		putbuf(buffer, size);
		lock_release(&filesys_lock);
		return size;
	}

	struct file *file = thread_current()->fdt[fd];
	if (file) {
		lock_acquire(&filesys_lock);
		int write_byte = file_write(file, buffer, size);
		lock_release(&filesys_lock);
		return write_byte;
	}
}
  • wirte() 함수는 열린 파일의 데이터를 기록하는 시스템 콜이다. 역시 fd값이 1일 때는 표준 출력이기 때문에 1일 시 putbuf() 함수를 사용하여 버퍼에 저장된 데이터를 화면에 출력한다.

seek();
case SYS_SEEK:
	seek(f->R.rdi, f->R.rsi);
	break;
void seek (int fd, unsigned position) {
	struct file *curfile = thread_current()->fdt[fd];
	if (curfile)
		file_seek(curfile, position);
}
  • seek()함수는 열린 파일의 위치(offset)를 이동하는 시스템 콜이다.

tell();
case SYS_TELL:
	f->R.rax = tell(f->R.rdi);
	break;
unsigned tell (int fd) {
	struct file *curfile = thread_current()->fdt[fd];
	if (curfile)
		return file_tell(curfile);
}
  • tell()함수는 열린 파일의 위치를 알려주는 시스템 콜이다.

close();
case SYS_CLOSE:
	close(f->R.rdi);
	break;
void close (int fd) {
	struct file * file = thread_current()->fdt[fd];
	if (file) {
		lock_acquire(&filesys_lock);
		thread_current()->fdt[fd] = NULL;
		file_close(file);
		lock_release(&filesys_lock);
	}
}
  • close()함수는 열린 파일을 닫는 시스템 콜이다. 파일을 닫고 fd를 제거한다.

check_addres();
void check_address(void *addr) {
	struct thread *cur = thread_current();
	if (addr == NULL || is_kernel_vaddr(addr) || pml4_get_page(cur->pml4, addr) == NULL)
		exit(-1);
}
  • Check_Address() 는 해당 주소 값이 주소 영역에 있는 주소 값인지 확인하는 함수이다. PintOS에서는 시스템 콜이 접근할 수 있는 주소를 0cx0000000 ~ 0x8048000 (이 이상은 커널 영억이다.) 으로 제한하기 때문에 유저 영억을 벗어난 영역일 경우 비정상 접근이라고 판단해 exit(-1)로서 프로세스를 종료한다.

PintOS Project2 GIthub 주소 PintOS

0개의 댓글