[운체] 오늘의 삽질 - 0722

방법이있지·2025년 7월 22일

  • cf. valid_pointer는 앞선 0721 글의 [구현 2-2]에서 만들어 둔, 포인터 확인용 함수임에 유의.
// 커널쪽: userprog/syscall.c
// [구현 2-2] 포인터가 valid한지 확인하는 함수를 만든다.
bool valid_pointer (void *p){
	// 널 주소-/ 커널 영역의 주소 / 매핑되지 않은 페이지
	if (p == NULL || is_kernel_vaddr(p) || pml4_get_page(thread_current() -> pml4, p) == NULL){
		thread_current() -> exit_code = -1;
		thread_exit();
	};
}

halt

[구현 3] halt 구현: 핀토스를 종료한다.

  • include/threads/init.h에 선언된 power_off() 함수를 사용하면 끝. 참 쉽죠?
// 클라이언트쪽: lib/user/syscall.c
void halt (void);
// 커널쪽: userprog/syscall.c
#include "threads/init.h"

case SYS_HALT:					
  /* Halt the operating system. */
  // [구현 3] 핀토스를 종료. 매우 쉽죠?
  power_off();
  break;

반쪽자리 write

[구현 4-1] write로 콘솔 출력하기 구현

  • write는 콘솔 창에 출력하는 경우와, 파일에 출력하는 경우에 따라 구현방식이 달라짐
    • fd == 1이면 콘솔 창에 출력됨
  • 콘솔 창에 출력하는 부분을 구현해야 이후 테스트 케이스를 보는 게 편해져서, 이걸 먼저 구현했음
// 클라이언트쪽: lib/user/syscall.c
// fd == 1인 경우, buffer의 내용을 size만큼 콘솔창에 출력한다.
int write (int fd, const void *buffer, unsigned size);
  • 첫 인자 fdf -> R.rdi, 둘째 인자 bufferf -> R.rsi, 셋째 인자 sizef -> R.rdx에 위치함에 유의할 것
  • putbuf 함수를 이용해, buffer의 내용을 size만큼 출력하게끔 구현
    • putbufpintos/include/lib/kernel/stdio.h에 존재하는데, #include <stdio.h>에 이미 포함됨. 추가로 인클루드할 필요 없음.
// 커널쪽: userprog/syscall.c
case SYS_WRITE:
	/* Write to a file. */
	// [구현 4] 콘솔 창에다 출력하는 부분만 일단 우선 구현 가능.
	valid_pointer(f -> R.rsi);

	if ((int)(f -> R.rdi) == 1){
		putbuf((char *)(f -> R.rsi), (size_t)(f -> R.rdx));
	}
	break;
  • 나머지 write는 나중에 구현해보아요들레이요

exit

[구현 5] exit로 프로세스 terminate하기

// 클라이언트쪽: lib/user/syscall.c
void exit (int status);
  • thread_exit()를 호출해서 종료시키면 되는데, termination message를 출력해야 함
    • termination message에 인자인 status를 포함시켜야 함
  • thread_exit()process_exit()을 호출하고, process_exit()에서 termination message 출력이 이루어짐
  • 문제는 process_exit은 인자를 받지 않아, 바로 status 값을 전달할 순 없음
// include/threads/thread.h
// 구조체 내 다른 멤버들은 생략.
struct thread {
	#ifdef USERPROG			
		int exit_code; /* exit 시 보여줄 코드 번호
	#endif
}
  • 따라서 struct threadint exit_code 멤버를 두어, 앞선 status를 종료하는 용도로 사용
// 커널쪽: userprog/syscall.c
case SYS_EXIT:
	/* Terminate this process. */
	// [구현 5-1] thread 내 exit_code 멤버에 status 저장
	thread_current() -> exit_code = (int)(f -> R.rdi);
	thread_exit();
	break;
  • 현재 쓰레드의 exit_code 멤버에 status (첫 인자이므로 rdi)를 저장하고
// 커널쪽: userprog/process.c
void process_exit (void) {
	// [구현 5-2] exit 및 exit 메시지 띄우기 구현
	struct thread *curr = thread_current ();
	printf("%s: exit(%d)\n", curr -> name, curr -> exit_code);

	process_cleanup ();
}
  • process_exit에 앞서 저장한 exit_code 멤버를 이용하여, termination 메시지를 출력시키기

exec

[구현 6] exec로 새로운 프로그램 실행

int exec (const char *file); 
  • file에는 "echo x y z"와 같은 커맨드 라인이 주어짐. 실행 성공 시 반환값이 없음, 실패 시 -1을 반환.
  • 앞서 argument passing 과정에서 [구현 1-3]에서 열심히 만들어 둔 process_exec 함수를 사용하면 됨.
// userprog/process.c 내 process_exec 함수
int process_exec (void *f_name) {
	char *file_name = f_name;
    //조금 많이 생략
	palloc_free_page (file_name);
}
  • 이때, process_exec에서 매개변수로 받은 f_name 주소에선 palloc_free_page로 메모리 할당 해제가 이루어짐.
  • 그 뜻은 매개변수 f_namepalloc으로 할당된 페이지의 주소여야 한다는 사실....
case SYS_EXEC:
	/* Switch current process. */
	// [구현 6] 새로운 프로세스 실행
	valid_pointer(f -> R.rdi);
	char *f_name = palloc_get_page(0);
	strlcpy(f_name, f->R.rdi, PGSIZE);
	f -> R.rax = process_exec(f_name);
	thread_exit();
	break;
  • 따라서 palloc_get_page로 페이지를 할당한 뒤, 해당 페이지에 f_name(첫 인자이므로 rdi)를 복사해 줌
  • 해당 값을 process_exec에 인자로 보내주고, 반환값은 rax 레지스터에 저장
  • 일반적으로는 반환되지 않지만, 실패하는 경우 반환이 이루어짐. 이 경우 -1만 리턴하고 바로 반환해야 함. 이를 위해 thread_exit()가 실행시켜 줌.
profile
뭔가 만드는 걸 좋아하는 개발자 지망생입니다. 프로야구단 LG 트윈스를 응원하고 있습니다.

2개의 댓글

comment-user-thumbnail
2025년 7월 22일

생일이셨나요? 축하드려요!

1개의 답글