[PintOS] Project 2 - User Programs 2

Quro·2024년 5월 24일
0

OS

목록 보기
5/6
post-thumbnail

첫 번째 과제 Command Line Parsing 과 Argument Passing

  • pintOS는 프로그램과 인자를 구분하지 못하는 구조이다.
    ex) ls -a (pintOS 는 'ls-a'를 하나의 프로그램명으로 인식)

  • 프로그램 이름과 인자를 구분하여 스택에 저장, 인자를 응용 프로그램에 전달하는 기능을 구현하자.


과제를 해결하기 위해서는?

  • 응용 프로그램 실행 흐름을 추적하여 프로그램 파싱 시점을 파악
  • 문자열 파싱을 담당하는 함수 이용
  • 함수 호출 규약에 따른 인자 전달 메커니즘 이해 및 이를 저장하는 인터페이스 구현

함수 호출 규약이란?

유닉스 64비트 x86-64 구현에 있는 몇 가지 중요한 호출 규약들은 다음과 같다.

유저-레벨 어플리케이션은 %rdi, %rsi, %rdx, %rcx, %r8, %r9 시퀀스들을 전달하기 위해 정수 레지스터를 사용합니다.
호출자는 다음 인스트럭션의 주소(리턴 어드레스)를 스택에 푸시하고, 피호출자의 첫번째 인스트럭션으로 점프합니다. CALL 이라는 x86-64 인스트럭션 하나가 이 두 가지를 모두 수행합니다.
피호출자가 실행됩니다.
만약 피호출자가 리턴 값을 가지고 있다면, 리턴 값은 레지스터 RAX에 저장됩니다.
피호출자는 x86-64 인스트럭션인 RET 를 사용해서, 스택에 받았던 리턴 어드레스를 pop하고 그 주소가 가리키는 곳으로 점프함으로써 리턴됩니다.

우리는 userprog 디렉토리의 process.c 파일을 수정할 것이다.

커널은 유저 프로그램이 실행되기 전에, 레지스터에 올라가 있는 초기 함수를 위한 인자를 반드시 넣어줘야 한다. (이 인자들은 일반적인 호출 규약과 동일한 방식으로 전달)

👉 /bin/ls -l foo bar 와 같은 명령이 주어졌을 때, 인자들을 어떻게 다뤄야 하는지 생각해보자.

  1. 명령어를 띄어쓰기 단위로 쪼갠다 -> /bin/lslfoobar
  2. 이 단어들을 스택에 넣는다. 순서는 상관 X (포인터에 의해 참조될 예정이기 때문)
  3. 각 문자열의 주소 + 경계조건을 위한 널포인터를 스택에 오른쪽→왼쪽 순서로 푸시
    **이들은 argv의 원소가 된다.
    널포인터 경계는 argv[argc] 가 널포인터라는 사실을 보장해준다.
    그리고 이 순서는 argv[0]이 가장 낮은 가상 주소를 가진다는 사실을 보장해준다.
    또한 word 크기에 정렬된 접근이 정렬되지 않은 접근보다 빠르므로, 최고의 성능을 위해서는 스택에 첫 푸시가 발생하기 전에 스택포인터를 8의 배수로 반올림 해준다.

// userprog/process.c/argument_stack()

...

int padding = (int)*rsp % 8;
    for (int i = 0; i < padding; i++)
    {
        (*rsp)--;
        **(uint8_t **)rsp = 0;        // rsp 직전까지 값 채움
    }
    
 ...
  1. %rsiargv 주소(argv[0]의 주소)를 가리키게 하고, %rdiargc 로 설정한다.
  2. 마지막으로 가짜 “리턴 주소”를 푸시 : entry 함수는 절대 리턴되지 않겠지만, 해당 스택 프레임은 다른 스택 프레임들과 같은 구조를 가져야 하기 때문에.
👉 아래의 표는 스택과 관련 레지스터들이 유저 프로그램이 시작되기 직전에 어떤 상태인지를 보여준다 (스택은 아래 방향으로 커진다)

Argument Passing(인자 전달) 구현

구현 할 때 크게 두 부분으로 나눠서 구현한다

  • 받은 문자열을 parsing후 argv, argc에 저장
  • stack에 인자 넣기

유저 프로그램을 실행시키는 전체적인 흐름은 다음과 같다

Todo

  • command line을 parsing해서 스레드의 이름을 식별한다.
  • 해당 파일 이름을 갖는 프로그램을 찾는다.
  • user stack에 인자를 push한다.

유저 스택에 파싱된 토큰을 저장하는 함수 구현

void argument_stack(char **parse ,int count ,void **esp)
  • parse : 프로그램 이름과 인자가 저장되어 있는 메모리 공간
  • count : 인자의 개수
  • esp : 스택 포인터를 가리키는 주소

코드의 흐름

init.c → int main(void) → run_actions(argv) → run_task(char **argv) → process_create_initd(task) → thread_create (file_name, PRI_DEFAULT, initd, fn_copy) → initd→process_exec → load, do_iret

profile
개발합니다

0개의 댓글