WIL Pintos Project 2 User Programs

김지수·2022년 11월 28일
0

SW사관학교정글5기

목록 보기
9/13

Pintos Project 2 User Programs

1. Argument Passing (인자 전달)

/bin/ls -l foo bar 와 같은 명령이 주어졌을 때, 인자를 어떻게 다루어야할지 생각해보기!
1. Break the command into words: /bin/ls, -l, foo, bar.
2. Place the words at the top of the stack. Order doesn't matter, because they will be referenced through pointers.
3. Push the address of each string plus a null pointer sentinel, on the stack, in right-to-left order. These are the elements of argv. The null pointer sentinel ensures that argv[argc] is a null pointer, as required by the C standard. The order ensures that argv[0] is at the lowest virtual address. Word-aligned accesses are faster than unaligned accesses, so for best performance round the stack pointer down to a multiple of 8 before the first push.
4. Point %rsi to argv (the address of argv[0]) and set %rdi to argc.
5. Finally, push a fake "return address": although the entry function will never return, its stack frame must have the same structure as any other.

  • 위와 같이 /bin/ls -l foo bar 와 같은 명령어가 들어왔을 때, 위의 사진처럼 스택에 쌓여 내려가게 끔 단계적으로 코드를 짜보았다.

우선 arg[0]에 온전한 file_name을 저장해주기 위해 arg[0]만 먼저 저장해주고, while을 문을 통해 그 뒤에 인자들을 " " << 빈칸을 기준으로 parsing하여 args[]에 저장을 해준다.

    /* PROJECT 2: ARGUMENT PASSING */
    char *save_ptr, *f_name;
    char *tmp, *args[64];
    int argc = 1;

    args[0] = strtok_r(file_name, " ", &save_ptr);
    while((tmp = strtok_r(NULL, " ", &save_ptr)) != NULL) {
        args[argc] = tmp;
        argc++;
    }

	/* Open executable file. */
	file = filesys_open (file_name);
	if (file == NULL) {
		printf ("load: %s: open failed\n", file_name);
		goto done;
	}

  • 우선 stack_pointer라는 인자에 interrupt frame의 rsp포인터의 주소값을 지정해준다
  • 이전에 parsing되어 저장되었던 args에서 argc만큼 반복하여 유저 스택 부분에 memory copy를 진행해준다.
  • 각각의 args가 저장되어 있는 유저 스택의 주소를 address[i]에 저장해준다.
    /* PROJECT 2: ARGUMENT PASSING */
    uintptr_t stack_pointer = (if_->rsp);

    /* 4단계: 문자열 넣기 */
    size_t sum = 0;
    char *address[64];

    for(int i = argc-1; i >= 0; i--) {
        uintptr_t len = strlen(args[i]) + 1;   // '\0' 포함
        sum += len;
        address[i] = (stack_pointer - sum);
        memcpy((stack_pointer - sum), args[i], len);
    }

  • word align을 8byte단위로 해주기 위해 8로 나누었을 때 나머지 값이 존재할 경우 그 만큼을 '\0' 으로 채워준다.
    /* 3단계: word align (8byte) 단위로 주소 맞춰주기 */
    uintptr_t word_align;
    int align;
    //  = (argc % 2 == 0) ? 8 : 16;
    align = 8;
    word_align = ((stack_pointer - sum) % align);
    sum += word_align;
    memset((stack_pointer - sum), '\0', word_align);

  • 각각의 args가 저장되어 있는 유저 스택의 주소를 저장해주었던 address[i]의 값을 메모리에 다시 써준다.
    /* 2단계: 문자열 주소값 넣어주기 */
    sum += 8;
    memset((stack_pointer - sum), '\0', 8); // argv[4] '\0'
    for(int i = argc-1; i >= 0; i--) {
        sum += 8;
        memcpy((stack_pointer - sum), (&address[i]), 8);
    }

  • 실제 return이 일어나지 않을 address 값에 대해 '\0'으로 채워준다.
    /* 1단계: 가짜 return address 넣어주기 */
    sum += 8;
    memset((stack_pointer - sum), '\0', 8);

  • 값을 memory copy를 진행해주고, interrupt frame 값들을 최신화 시켜준다.
    /* 0단계: RDI = argc, 
             RSI = 가짜 return address 이전 주소 */
    if_->rsp -= sum;            // 저장된 스택 주소를 내려주는 작업을 마지막에 해주었다.
    if_->R.rdi = argc;
    if_->R.rsi = if_->rsp + 8;


2. System Call

  • void halt (void);
    Terminates Pintos by calling power_off() (declared in src/include/threads/init.h). This should be seldom used, because you lose some information about possible deadlock situations, etc.
  • halt() system call을 하게 되면 power_off()를 실행하게 된다.
void halt_handler(struct intr_frame *f) {
    power_off();
}

  • void exit (int status);
    Terminates the current user program, returning status to the kernel. If the process's parent waits for it (see below), this is the status that will be returned. Conventionally, a status of 0 indicates success and nonzero values indicate errors.
  • status에 first argument 값을 넣어주고, running하고 있는 thread의 process_status를 status로 최신화 시켜주고 thread_exit을 진행한다.
void exit_handler(struct intr_frame *f) {
    int status = F_ARG1;
    struct thread *curr = thread_current();

    thread_current()->process_status = status;
    printf("%s: exit(%d)\n",curr->name, curr->process_status);
    thread_exit ();
}

  • pid_t fork (const char *thread_name);
    Create new process which is the clone of current process with the name THREAD_NAME. You don't need to clone the value of the registers except %RBX, %RSP, %RBP, and %R12 - %R15, which are callee-saved registers. Must return pid of the child process, otherwise shouldn't be a valid pid. In child process, the return value should be 0. The child should have DUPLICATED resources including file descriptor and virtual memory space. Parent process should never return from the fork until it knows whether the child process successfully cloned. That is, if the child process fail to duplicate the resource, the fork () call of parent should return the TID_ERROR.
    The template utilizes the pml4_for_each() in threads/mmu.c to copy entire user memory space, including corresponding pagetable structures, but you need to fill missing parts of passed pte_for_each_func (See virtual address).

.... 추후 업로드 예정

0개의 댓글