[PINTOS PROJECT 2] ARGUMENT PARSING

zeo·2021년 10월 12일
1
  1. process_exec()
int process_exec(void *f_name)
{
	char *file_name = f_name;
	bool success;
	struct thread *cur = thread_current(); 

	// 1. intr_frame 생성
	// intr_frame은 실행중인 프로세스의 register 정보, stack pointer, instruction counter를 저장하는 자료구조
	// interrupt나 systemcall 호출시 사용
    
	struct intr_frame _if; // intr_frame 내 실행 시 필요한 정보 담기
	_if.ds = _if.es = _if.ss = SEL_UDSEG;
	// ds : data segment, es : more data segment, ss : stack segment
	_if.cs = SEL_UCSEG;
	// cs : code segment
	_if.eflags = FLAG_IF | FLAG_MBS;
	// eflags : CPU flags

	// 2. process_cleanup() : curr->pml4 초기화
	// 새로운 실행 파일을 현재 스레드에 담기 전,
	// 먼저 현재 프로세스에 담긴 context 지워주기(=현재 프로세스에 할당된 page directory 지우기)
	process_cleanup(); //실행하던 프로세스 초기화하고, 실행하려는 파일로 덮어쓰기

	// Project 2-1. Pass args - parse
	// 3. argument parse -> *argv[30]에 저장
	char *argv[30]; 
	int argc = 0;

	char *token, *save_ptr;
	token = strtok_r(file_name, " ", &save_ptr); // " "을 기준으로 앞의 내용은 file_name에 저장하고, 
	// " " 뒤의 내용은 save_ptr에 저장
	while (token != NULL)
	{
		argv[argc] = token; // token에 있는 주소값을 argv[argc]에 넣기
		token = strtok_r(NULL, " ", &save_ptr); // 처음 이후의 strtok_r()에서는 첫인자로 NULL을 입력
		// 토큰 자체를 NULL 캐릭터로 옮겨 놓은 후, 캐릭터 라인 다음의 토큰의 선두의 포인터로 돌려줌
		// 토큰이 없어지면, NULL 포인터를 돌려줌
		argc++;
	}

	// 4. load() -> file 실행(_if.rsp -> 유저 스택 할당, _if.rip -> 스택 포인터 할당) 
	// rip는 현재 명령 실행 주소를 저장하는 레지스터
	success = load(file_name, &_if);

	// file_name은 프로그램 파일 이름을 받기 위해 만든 임시 변수이므로, 
	// load 종료 시 해당 메모리 반환해야함
	if (!success)
	{
		palloc_free_page(file_name);
		return -1;
	}

	// load 성공한 경우, load_userStack() 통해 user stack에 인자 저장
	// Project 2-1. Pass args - load arguments onto the user stack
	// 5. 인자들을 user stack에 넘기기
	void **rspp = &_if.rsp; //rsp : 현재 스택 주소(스택 맨 위쪽의 주소)
	load_userStack(argv, argc, rspp);
	_if.R.rdi = argc; //rdi : 목적지(destination)
	_if.R.rsi = (uint64_t)*rspp + sizeof(void *); // rsi : 출발지(source)

	palloc_free_page(file_name); 

	// load가 성공적으로 된 경우, context_switching 실시
	// 6. do_iret 실행(intr_frame 정보를 가지고 launch thread)
	do_iret(&_if);
	NOT_REACHED();
}
  1. load_userStack()
void load_userStack(char **argv, int argc, void **rspp)
{
	// 1. Save argument strings (character by character)
	// 각 인자 스트링을 스택에 한글자씩 기록
	for (int i = argc - 1; i >= 0; i--) // 인자 개수 기준 내림차순
	{
		int N = strlen(argv[i]); // 각 인자의 길이(argv[i]의 길이) 
		for (int j = N; j >= 0; j--)
		{
			char individual_character = argv[i][j]; // 각 인자의 각 요소 넣기 ('n', 'a', 'm', 'e')
			(*rspp)--; // 1 byte씩 내리기
			**(char **)rspp = individual_character; 
			//*(char *)(_if.rsp) = individual_character를 해주고 싶으니까
			// char * 형으로 캐스팅한 다음, * 포인터를 통해 해당 값에 접근
		}
		argv[i] = *(char **)rspp; // push this address too
		// 각 인자별 첫 글자의 스택 주소 저장(나중에 쓸 "name"의 첫 주소 저장)
	}
	// 2. Word-align padding
	// 인자들을 모두 저장한 후, 현재 스택 포인터(_if.rsp)의 값이 8배수가 되도록 맞춰주기
	// 64비트 이므로, 8바이트 단위로 끊어주기
	// rsp가 8의 배수가 되도록 설정

	// 마지막 인자 주소값을 8로 나눈 나머지만큼 밑으로 이동하여 0으로 초기화
	int pad = (int)*rspp % 8;
	for (int k = 0; k < pad; k++)
	{
		(*rspp)--;
		**(uint8_t **)rspp = (uint8_t)0; // 1 byte씩 아래로 이동하면서, 각 칸의 내용을 0으로 초기화
	}

	// 3. Pointers to the argument strings
	// 인자들이 stack에 저장된 주소를 stack에 기입하는 것
	size_t PTR_SIZE = sizeof(char *); // 캐릭터형 포인터 자료구조의 size는 8byte

	(*rspp) -= PTR_SIZE; // 캐릭터형 포인터 사이즈 만큼 빼주기
	**(char ***)rspp = (char *)0; // 해당 위치 값 0으로 초기화

	for (int i = argc - 1; i >= 0; i--) 
	{ 
		(*rspp) -= PTR_SIZE;
		**(char ***)rspp = argv[i]; // 해당 위치에 argv[i] 기입(i번째 arg의 주소)
	}

	// 4. Return address를 0으로 초기화(push a fake 'return address')
	(*rspp) -= PTR_SIZE;
	**(void ***)rspp = (void *)0;
}



0개의 댓글