[ KJ ] Week08
Krafton Jungle

[ KJ ] Week08

반응형

 

8~9주차 PintOS User Programs 프로젝트를 진행했습니다~

 

  • Project2 : User Programs
  • 유저 모드(User Mode) : 일반적인 응용 프로그램이 실행되는 환경

   - 제한된 메모리 공간과 시스템 자원에만 접근 가능

   - 운영 체제의 핵심 부분에 직접 접근 불가

   - 시스템의 안정성과 보안을 유지
   - 시스템 자원이나 서비스가 필요할 경우, 시스템 호출(System Call)을 통해 커널 모드로 전환하여 요청을 처리

  • 커널 모드(Kernel Mode) : 운영 체제의 핵심 부분과 하드웨어를 직접 제어하는 코드가 실행되는 환경

   - 시스템의 메모리 및 하드웨어를 직접 제어 가능

   - 오류가 발생할 경우 시스템의 안정성에 큰 영향

   - 메모리, 프로세스, 파일 시스템, 입출력 시스템, 네트워킹, 드라이버 등이 커널 모드에서 실행

  • 시스템 호출 : 유저 프로그램이 파일을 열기, 네트워크 통신, 프로세스 생성 및 관리 등의 시스템 자원을 필요로 할 때, 이러한 요청은 시스템 호출

   - 유저 모드에서 커널 모드로의 명시적인 전환

   - 운영 체제가 시스템의 안정성, 보안, 자원 관리를 유지

  • 물리 주소와 가상 주소

   > 유저 가상 메모리 : 가상 주소 0부터 KERN_BASE(0x8004000000)까지의 범위

   > 커널 가상 메모리 : 가상 주소 공간(유저 가상 메모리)의 나머지를 차지

 

1. Argument Passing

  • 목표 : 유저 프로그램이 실행되기 전 레지스터에 올라가 있는 초기 함수에 인자를 넣어줘야 한다.
  • 구현 : f_name을 받아 " "을 기준으로 자를 수 있는 문자열의 정보를 스택에 저장한다. 스택의 첫 주소값 USER_STACK(0x47480000)을 기준으로 포인터를 8씩 이동하여, 위치한 주소값에 문자열을 저장한다. 전체 길이가 8의 배수가 아니면 스택 포인터를 조정하여 8의 배수가 되도록 한다. 이후 시스템 콜 작업을 수행한다.
int process_exec(void *f_name)
{
	char *file_name = f_name;
	bool success;

	/* We cannot use the intr_frame in the thread structure.
	 * This is because when current thread rescheduled,
	 * it stores the execution information to the member. */
	struct intr_frame _if;
	_if.ds = _if.es = _if.ss = SEL_UDSEG;
	_if.cs = SEL_UCSEG;
	_if.eflags = FLAG_IF | FLAG_MBS;

	char *token, *save_ptr;
	int elem_num = 1;  // 토큰 요소 개수
	int total_len = 0; // 전체 문자 길이

	// 토큰 요소 개수 세기
	for (int n = 0; file_name[n] != '\0'; n++)
	{
		if (file_name[n] == ' ')
		{
			elem_num++;
		}
	}

	// 토큰 저장할 포인터 배열 선언
	char *file_stok[elem_num]; // 마지막에 NULL 포인터 미포함
	int64_t file_stok_addresses[elem_num]; // 토큰 시작 주소값 저장 배열

	// 토큰 포인터 배열에 저장
	for (int i = 0; i < elem_num; i++)
	{
		token = strtok_r((i == 0) ? file_name : NULL, " ", &save_ptr);
		file_stok[i] = token;
	}

	/* We first kill the current context */
	process_cleanup(); //

	/* And then load the binary */
	// 파일 정보를 읽음
	success = load(file_stok[0], &_if);

	for (int j = elem_num - 1; j >= 0; j--)
	{
		// 다음 주소를 가리키도록 내림
		_if.rsp -= strlen(file_stok[j]) + 1;
		total_len += strlen(file_stok[j]) + 1;
		memcpy((void *)_if.rsp, file_stok[j], strlen(file_stok[j]) + 1);
		file_stok_addresses[j] = _if.rsp;
	}

	if (total_len % 8 != 0)
	{
		_if.rsp -= 8 - (total_len % 8);
		memset((void *)_if.rsp, 0, 8 - (total_len % 8));
	}

	_if.rsp -= 8;
	memset((void *)_if.rsp, 0, 8);

	for (int n = elem_num - 1; n >= 0; n--)
	{
		_if.rsp -= 8;
		memcpy((void *)_if.rsp, &file_stok_addresses[n], 8);
	}

	// return address
	_if.rsp -= 8;
	memset((void *)_if.rsp, 0, 8);

	// RDI: 4 | RSI: 0x4747ffc0
	// 스택 시작점
	_if.R.rsi = _if.rsp + 8;
	// 토큰 요소 개수
	_if.R.rdi = elem_num;

	/* _if.rsp를 시작 주소로 하여 메모리 덤프를 생성. 메모리 덤프의 크기는 16진수로 */
	hex_dump(_if.rsp, (const void *)_if.rsp, USER_STACK - (uint64_t)_if.rsp, true);

	/* If load failed, quit. */
	// 주소 공간 초기화
	palloc_free_page(file_name);

	// 파일 읽기 실패
	if (!success)
		return -1;

	/* Start switched process. */
	// 시스템 호출이나 인터럽트 처리 후
	// 저장된 상태 정보를 복원해
	// 원래의 사용자 모드 프로세스 또는 스레드로 제어를 되돌리는 컨텍스트 스위칭 함수
	do_iret(&_if);
	NOT_REACHED();
}

 

 

2. System Call

  • 목표 : 유저 프로그램은 요청받은 시스템 콜에 대해 처리한다.
  • 구현 : 각각의 시스템 콜의 반환 값을 레지스터의 R.rax에 저장해야 한다.
  • 테스트 결과

 

< 추성결 >

- 현재 process_exec()의 thread_current()에서 문제가 발생하는데, load() 함수에서 argument_parsing을 진행하는 과정에서 문제가 발생한 것 같지만 정확한 원인이 파악이 불가하다.

- make check를 실행해서 디버깅을 할 때마다 문제가 발생하는 테스트 케이스가 달라서 디버깅이 어려웠다.

- 메인 프로세스에서 작업하기 위해 유저 프로그램을 fork() 해서 만든 프로세스를 exec() 하면 해당 프로세스가 끝날 때까지 부모 프로세스가 wait() 하는 과정의 흐름이 어려웠다.

- fork(), exec(), wait() 코드 작성할 때, 부모 프로세스와 자식 프로세스를 구분하는 것이 어려웠고, 많이 헷갈려서 작업이 오래 걸렸다.

 

< 김수빈 >

- 스택 포인터의 주소 값의 시작이 어딘지 찾는 것이 어려웠다.

- 구조체 내의 각 변수 값이 어떤 것을 의미하는지 알기 어려웠다.

 

 

 

반응형

'Krafton Jungle' 카테고리의 다른 글

[ KJ ] Week07  (0) 2024.03.11
[ KJ ] Week06  (0) 2024.02.29
[ KJ ] Week05  (0) 2024.02.23
[ KJ ] Week04  (0) 2024.02.07
[ KJ ] Week03  (1) 2024.02.01