pintos project 2: USER PROG - Argument passing

Jifrozen·2024년 10월 6일

정글

목록 보기
2/4
post-thumbnail

Argument passing

해당 그림은 Pintos 운영체제에서 프로세스 실행 흐름을 설명하는 다이어그램이다.
1. 명령어 입력 (Test Program Execution)
커맨드 라인에 run echo x 라는 명령어가 입력됩니다.
2. main 함수 (프로세스 생성 시작)
3. run_actions() 함수는 명령줄에서 지정된 작업들을 순차적으로 실행
run 명령어 action 발견하면 2번째 인자 - echo 를 검사하고 run_task
4. run_task() 명령을 실행하는 역할 명령어 배열(argv)의 두 번째 요소를 이용해 실행할 작업을 지정
process_wait(process_create_initd(task)); 호출
5. tid_t process_execute()
process_create_initd(task) > thread_create > initd > process_exec
6. process_exec(void *f_name)
현재 실행중인 프로세스를 주어진 실행 파일로 교체하여 새로운 프로그램을 실행하는 역할을 기존 프로세스의 메모리 공간을 정리하고, 새로운 실행파일을 메모리에 로드한 후, 필요한 인자들을 스택에 설정
7. do_iret()을 통해 유저 모드로 전환 스택에 저장된 인자들이 argc와 argv 형태로 main() 함수에 전달
process_exec()에서 프로세스가 완전 교체되었고, 로드된 프로그램의 진입점이 main함수로 지정되었기 때문에 새로운 프로그램은 main함수 실행 시 명령줄 인자를 사용해 원하는 동작

여기서 스택에 인자를 저장하는 과정이 없기 때문에 추가해줘야한다.
명령어를 받아서 스택에 넣어주는 방법은 다음과 같다.

1. Argument parsing

명령줄에 입력된 파일 이름이랑 인자들을 파싱해서 각 단어를 분리해야한다.
strtok_r은 공백을 기준으로 파싱하고, 각 토큰은 parse 배열에 저장한다.

    char *parse[64];
    char *token, *save_ptr;
    int count = 0;
    for (token = strtok_r(file_name, " ", &save_ptr); token != NULL; token = strtok_r(NULL, " ", &save_ptr))
        parse[count++] = token;

2. Argument passing

명령줄 인자를 유저 스택에 삽입하는 로직입니다.
스택은 아래와 같이 저장되어야한다.
rsp (스택포인터)는 아래로 확장한다.
따라서 rsp의 포인터를 감소시켜주고 데이터를 넣어줘야한다.
1. 처음에는 명령어 인자들을 넣어준다.
2. 다음은 명령어 인자들의 word-align을 위해 패딩을 넣어준다.
현재 64비트 운영체제이기 때문에 8바이트를 맞춰주는 로직이 필요하다,
3. 빈문자열을 삽입한다.
4. 각 인자들이 저장된 주소를 넣어준다.
5. return 값을 넣어준다.
| Address | Name | Data | Type |
| --- | --- | --- | --- |
| 0x4747fffc | argv[3][...] | 'bar\0' | char[4] |
| 0x4747fff8 | argv[2][...] | 'foo\0' | char[4] |
| 0x4747fff5 | argv[1][...] | '-l\0' | char[3] |
| 0x4747ffed | argv[0][...] | '/bin/ls\0' | char[8] |
| 0x4747ffe8 | word-align | 0 | uint8_t[] |
| 0x4747ffe0 | argv[4] | 0 | char |
| 0x4747ffd8 | argv[3] | 0x4747fffc | char
|
| 0x4747ffd0 | argv[2] | 0x4747fff8 | char |
| 0x4747ffc8 | argv[1] | 0x4747fff5 | char
|
| 0x4747ffc0 | argv[0] | 0x4747ffed | char |
| 0x4747ffb8 | return address | 0 | void (
)() |

  argument_stack(parse, count, &_if.rsp);
    _if.R.rdi = count;
    _if.R.rsi = (char *)_if.rsp + 8;
  1. 명령어 인자 삽입
void argument_stack(char **parse, int count, void **rsp) // 주소를 전달받았으므로 이중 포인터 사용
{
    // 프로그램 이름, 인자 문자열 push
    for (int i = count - 1; i > -1; i--)
    {
        for (int j = strlen(parse[i]); j > -1; j--)
        {
            (*rsp)--;                      // 스택 주소 감소
            **(char **)rsp = parse[i][j]; // 주소에 문자 저장
        }
        parse[i] = *(char **)rsp; // parse[i]에 현재 rsp의 값 저장해둠(지금 저장한 인자가 시작하는 주소값)
    }

스택 포인터의 현재 주소가 8바이트 정렬이 되도록 패딩을 추가
64비트 환경이기 때문에 8바이트 정렬


    // 정렬 패딩 push
    int padding = (int)*rsp % 8;
    for (int i = 0; i < padding; i++)
    {
        (*rsp)--;
        **(uint8_t **)rsp = 0; // rsp 직전까지 값 채움
    }

// 주소 넣어주기


    // 인자 문자열 종료를 나타내는 0 push
    (*rsp) -= 8;
    **(char ***)rsp = 0; // char* 타입의 0 추가

    // 각 인자 문자열의 주소 push
    for (int i = count - 1; i > -1; i--)
    {
        (*rsp) -= 8; // 다음 주소로 이동
        **(char ***)rsp = parse[i]; // char* 타입의 주소 추가
    }

return 주소를 갱신
여기선 초기화이기 때문에 0으로 설정

    // return address push
    (*rsp) -= 8;
    **(void ***)rsp = 0; // void* 타입의 0 추가
}

0개의 댓글