구현 목표
f_name을 통째로file_name으로 바꿔주고 있다.- [[strtok_r]] 를 사용해 토큰별로 쪼개준다 (공백 여러개 있을 때도 처리해야 함)
- 그렇게 쪼개진 토큰을 list에 담아
intr_frame의register에 세팅해준다.
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;
/* We first kill the current context */
process_cleanup ();
/* And then load the binary */
// f_name을 그대로 load에 넣어줘 file_load에 실패하고 있다.
success = load (file_name, &_if);
/* If load failed, quit. */
palloc_free_page (file_name);
if (!success)
return -1;
/* Start switched process. */
do_iret (&_if);
NOT_REACHED ();
}
수정해야 할 함수
process_exec
string token으로 분할
char *token_arr[128];
char *save_ptr, *token;
int count = 0;
for (token = strtok_r(file_name, " ", &save_ptr);
token != NULL;
token = strtok_r(NULL, " ", &save_ptr))
token_arr[count++] = token;
/* And then load the binary
* 2. 디스크에서 해당 바이너리 파일을 메모리로 로드한다 -> load() */
success = load (file_name, &_if);
token을 저장할token_arr를 선언해준다. 배열의 크기는 깃북을 참고함buffer역할을 해줄save_ptr과token을 선언, 인자의 개수를 저장할count를 초기화해준다.- 분할 후
token_arr에 담아주고,file_name으로load
추가해야 할 함수
argument_stack
전체 코드
static void
argument_stack(char *parse[], int count, struct intr_frame *_if) {
for (int i = count - 1; i >= 0; i--) {
size_t len = strlen(parse[i]) + 1;
_if->rsp -= len;
memcpy(_if->rsp, parse[i], len);
parse[i] = _if->rsp;
}
uint8_t padding = _if->rsp % 8;
if (padding) {
memset(_if->rsp -= (sizeof(uint8_t) * padding), 0, sizeof(uint8_t) * padding);
}
_if->rsp -= sizeof(uintptr_t);
memset(_if->rsp, 0, sizeof(uintptr_t));
for (int i = count - 1; i >= 0; i--) {
_if->rsp -= sizeof(uintptr_t);
_if->rsp = memcpy(_if->rsp, &parse[i], sizeof(uintptr_t));
}
_if->R.rsi = _if->rsp;
_if->R.rdi = count;
_if->rsp -= sizeof(uintptr_t);
memset(_if->rsp, 0, sizeof(uintptr_t));
}
개요


args-single라는 file을 load하고, onearg인자를 전달하는 테스트를 진행할것이다.
우리가 원하는 동작은 위 그림과 같이 전달받은 인자, 파일의 이름을 [[stack]]에 적재하는 것인데, 이를 코드를 따라가며 한줄 한줄 정리해보자.
인수의 메모리 배치 & 8byte 정렬

for (int i = count - 1; i >= 0; i--) {
size_t len = strlen(parse[i]) + 1;
_if->rsp -= len;
memcpy(_if->rsp, parse[i], len);
parse[i] = _if->rsp;
}
uint8_t padding = _if->rsp % 8;
if (padding) {
memset(_if->rsp -= (sizeof(uint8_t) * padding), 0, sizeof(uint8_t) * padding);
}
- rsp 주소를
token의 사이즈 + 1(null 종단문자)만큼 감소시키면서stack에 데이터를 할당한다. - 해당
token이 저장되어 있는 주소를buffer로 사용될 parse에 저장시켜둔다. - 이 과정이 종료되면 추가로 할당해줘야 할
padding의 크기를 구하고 할당해준다.
인자의 마지막을 알려주는 null pointer sentinel

_if->rsp -= sizeof(uintptr_t);
memset(_if->rsp, 0, sizeof(uintptr_t));
그 다음, 스택에 NULL 포인터를 추가합니다. 이는 인수 목록의 끝을 표시하기 위해 사용됩니다.
인수 포인터 배열

for (int i = count - 1; i >= 0; i--) {
_if->rsp -= sizeof(uintptr_t);
_if->rsp = memcpy(_if->rsp, &parse[i], sizeof(uintptr_t));
}
그 후, 각 인수의 주소를 스택에 저장합니다. 이를 통해 인수 포인터 배열을 구성합니다.
레지스터 설정

_if->R.rsi = _if->rsp;
_if->R.rdi = count;
마지막으로, 레지스터에 인수 포인터 배열의 시작 주소와 인수 개수를 설정합니다. 이는 프로그램 시작 시 argc와 argv를 설정하는 역할을 합니다.

_if->rsp -= sizeof(uintptr_t);
memset(_if->rsp, 0, sizeof(uintptr_t));
마지막으로, 스택에 NULL 포인터를 추가하여 함수가 종료됩니다.
요약
이 과정은 스택에 명령줄 인수들을 저장하고, 인수 배열을 구성하여, 프로그램이 실행될 때 인수들을 올바르게 접근하고 사용할 수 있게 합니다. 스택에 저장된 주소들을 통해 프로그램은 인수들을 올바르게 참조하고 처리할 수 있습니다. 이는 명령줄 인수 전달 규약에 따른 표준적인 처리 방식입니다.
'Krafton_Jungle > pintOS' 카테고리의 다른 글
| Project 2 User program - argument_stack (0) | 2024.05.30 |
|---|---|
| Project 1 Advanced Scheduler (2) | 2024.05.19 |
| Project 1 Priority donation (0) | 2024.05.17 |
| Project 1 Alarm Clock (0) | 2024.05.11 |
| ERROR - mac 핀토스 환경설정 에러 27 of 27 tests failed. (5) | 2024.05.10 |