ꕥ
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 |