현재 핀토스 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 |