본문 바로가기
OS/Pintos P.J_2

[Project 2_User Programs]_Argument Passing(인수전달)

by Success T.H.I.E.F 2022. 1. 4.

현재 핀토스 process_exec()파일을 보면 새로운 프로세스에게 인수전달을 하지 않는다고 한다.

따라서 process_exec()를 수정하여 위 기능을 구현해야 한다.

 

단순히 파일이름을 인수로 사용하는 대신 공백을 기준으로 단어를 나누어 전달해야 한다.

첫번째는 프로그램의 이름, 두번째 단어부터는 첫번째 인수 이다.

예를들어 process_exec("grep foo bar") 라고 한다면 파일명은 grep, foo와 bar는 각각 첫번째 두번째 인자가 되도록 파싱해 주어야 한다. 

strtok_r()을 이용 원하는 방식으로 인수 문자열을 구문 분석할 수 있다.

 

한양대 핀토스 pdf자료를 토대로 본 part2의 흐름과 구현해야하는 부분

응용 프로그램 실행의 흐름
추가해줄부분

우리 과제 부분에서 start_process()는 process_exec()부분으로 /usergrog/process.c 안에 있다.

/* userprog/process.c */
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. */
	//intr_frame에 실행할 때 필요한 정보들을 담아준다
	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에 담긴 context를 지워준다.
	지워준다는 것은 현재 process에 할당된 page directory를 지운다는 것 */
	process_cleanup(); 

	/* And then load the binary */
	/* file_name을 현재 프로세스에 load한다.
	 만약 load에 성공하면 1을 반환하고 아니면 0을 반환 */
	success = load(file_name, &_if);
	hex_dump(_if.rsp, _if.rsp, USER_STACK - _if.rsp, true); 
	/* If load failed, quit. */

	//file_name은 우리가 프로그램 파일 이름을 받기 위해 만든 임시 변수이므로  load가 끝났다면 해당 메모리를 반환해야한다.
	palloc_free_page(file_name);
	if (!success)
		return -1;

	/* Start switched process. */
	/*  만약 load가 실행했다면 context switching 을 실시 */
	do_iret(&_if);
	NOT_REACHED();
}

 

/* userprog/process.c */
/* Loads an ELF executable from FILE_NAME into the current thread.
 * Stores the executable's entry point into *RIP
 * and its initial stack pointer into *RSP.
 * Returns true if successful, false otherwise. */
static bool
load(const char *file_name, struct intr_frame *if_)
{   
	...

	/*----------PROJECT2: User Programs - Argument Passing-------------*/
	/* [file_name] Parsing : file_name을 '\0'으로 구분하여 argv에 저장한다. */
	char *argv[128]; // string literal 주소값들의 배열
	int argc = 0;

	char *token, *save_ptr;

	for (token = strtok_r(file_name, " ", &save_ptr); token != NULL; token = strtok_r(NULL, " ", &save_ptr)){
		argv[argc] = (char *)token;
		argc++;
	}
	/*-----------------------------------------------------------------*/
	
    ...
    
    
	/* Set up stack. */
	if (!setup_stack(if_))
		goto done;

	/* Start address. */
	if_->rip = ehdr.e_entry;

	/* TODO: Your code goes here.
	 * TODO: Implement argument passing (see project2/argument_passing.html). */
    /*----------PROJECT2: User Programs - Argument Passing-------------*/ 
	argument_stack(argv, argc, if_);
    /*-----------------------------------------------------------------*/
	// hex_dump(if_->rsp, if_->rsp, USER_STACK - if_->rsp, true);
	success = true;

done:
	/* We arrive here whether the load is successful or not. */
	file_close(file);
	return success;
}

void argument_stack(char **argv, const int argc, struct intr_frame *if_){
	uintptr_t rsp = if_->rsp;
	/* 프로그램명 및 인자(문자열) push */
	for (int i = argc - 1; i >= 0; i--){//argv[4]는 sentinel null pointer
		rsp -= strlen(argv[i]) + 1;// rsp를 이동시켜 공간을 확보, '\0'을 위해 추가
		memcpy((char *)rsp, argv[i], strlen(argv[i]) + 1); // 확보된 공간에 문자열 추가
		argv[i] = (char *)rsp;// 스택에 추가된 문자열의 주소값을 보관 (argv 재활용)
	}


	while (rsp % 8 != 0)
	{ //word-align을 실행한다. 64bit이기 때문에 8byte단위로 끊어준다. rsp가 8의 배수가 될 때까지 rsp의 위치를 내려준다.
		rsp--;
		// rsp는 그냥 interger이기 때문에, 먼저 1byte 주소값으로 casting을 해준 뒤 역변환을 통해 그 byte 자리에 0을 넣음
		*(char *)rsp = (char)0;
	}

	/* 프로그램명 및 인자 주소들 push */
	// 포인터의 크기 계산
	size_t PTR_SIZE = sizeof(char *);

	// argv[argc] 위치에 0 삽입
	rsp -= PTR_SIZE;
	*(char **)rsp = (char *)0; // 여기서는 8 bytes
	// argv[0] ~ argv[argc-1]에 각 문자열의 주소값 저장
	for (int j = argc - 1; j >= 0; j--){
		rsp -= PTR_SIZE;
		// 여기서 *(char *) 로 처리하면 에러 발생! 왜? 8byte가 아닌 1byte로 처리되기 때문
		// rsp부터 sizeof(char *) 크기 만큼, 즉 주소값 크기 만큼의 자리에 argv[j]를 넣겠다는 의미
		// 여기서 rsp는 (char *)에 대한 주소값이므로, *(char **)
		*(char **)rsp = argv[j];
		// printf("  at %p, %p (%p)\n", (char *)rsp, *(char **)rsp, (char *) argv[j]);
	}

	// fake return address
	rsp -= PTR_SIZE;
	*(char **)rsp = (char *)0; // 여기서도 8 bytes

	if_->rsp = rsp;
	/* argc (문자열의 개수 저장) push */
	if_->R.rdi = argc;
	/* argv (문자열을 가리키는 주소들의 배열을 가리킴) push*/
	if_->R.rsi = rsp + PTR_SIZE; // fake return address 위치를 빼주어야 함
	// set %rsi -> &argv[0], %rdi -> argc
}

라인 11~21에서  parsing 되지 않고 들어온 filename을 배열 argv 에 strtok_r을 이용하여 쪼개어 담아주고있다. strtok_r의 첫번째 인자는 null일경우 token을 자동으로 저장되었던 위치로 이동시킨다.

이렇게 분할한 하여 담은 argv를 이용 argument_stack 함수만들어 인수를 전달한다.

 

/*터미널에 아래 복붙하여 결과 확인*/
pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/args-single:/bin/ls -- -q   -f run '/bin/ls -l foo bar'

실행결과 이다 핀토스 가이드에 나와있는 예제대로 잘 들어가 있음을 확인해 볼 수있다.

 

'OS > Pintos P.J_2' 카테고리의 다른 글

[Project 2_User Programs]_System Calls  (0) 2022.01.09
[Project 2_User Programs]_Intro  (0) 2022.01.01