커맨드 라인에 입력된 명령어를 실행하는 것이 Argument Passing 과제의 목표이다.
예를 들어 ls -l .c라는 명령어가 입력 되었을 때, 파일명 .c와 -l와 같은 다른 인자를 스택에 저장하여 프로세스를 생성하고 실행하도록 해야 한다.
먼저 run 옵션(응용 프로그램 실행)일 경우 run_task() 함수를 호출한다.
인자 함수 process_execute(argv)를 통해 유저 프로세스를 생성한 후 process_wait() 함수를 사용하여 자식 프로세스가 종료될 때까지 대기한다.
process_execute() 함수를 통해 프로세스(쓰레드)를 생성하는 함수를 호출하고 tid를 반환한다.
thread_create() 함수를 호출하여 쓰레드를 생성한 후 생성된 쓰레드를 ready list에 추가한다.
startprocess() 함수에 파일명 file_name을 입력하여 프로그램을 메모리에 적재한 후 프로그램을 시작한다.
프로그램을 실행하는 과정에서 load() 함수를 통해 프로그램을 메모리에 적재한다.
프로그램을 메모리에 적재하는 것을 성공하지 못하면 thread_exit()을 통해 쓰레드를 종료하고, 성공하면 유저 프로그램을 시작한다.
main() 함수에서 시작하여 커맨드 라인에 입력된 명령어가 run 옵션(응용 프로그램 실행)일 경우 run_action() 함수를 호출한다.
run_task() 함수를 호출하여 유저 프로세스가 실행될 수 있도록 프로세스 생성을 시작(process_execute())하고 자식 프로세스가 종료될 때까지 대기(process_wait())한다.
process_waite() 함수는 자식 프로세스가 종료될 때까지 대기한다.
프로세스(쓰레드) 생성 함수 process_execute()을 호출하고 tid를 반환한다.
thread_create() 쓰레드를 생성한 후 ready list에 추가한다.
process_execute() 프로그램 이름을 파싱한 후 커맨드 라인에서 프로세스 이름을 확인해야 한다.
start_process() 인터럽트 프레임을 초기화하고 load() 메모리를 할당 받아 사용자 프로그램을 적재하기 전에 Argument Passing 기능을 추가하여 커맨드 라인을 Parsing하여 인자를 확인해야 한다.
load() 사용자 프로그램을 메모리에 적재한 후 argument_stack() 인자들을 스택에 삽입해야 한다.
쓰레드의 페이지 디렉토리에 유저 프로세스의 페이지 테이블을 생성한다.
ELF 파일의 헤더 정보를 읽어서 저장한다.
배치 정보를 읽어 저장한다.
load_segment() 함수를 통해 배치 정보를 통해 파일을 메모리에 적재한다.
스택 포인터가 가리키는 스택을 초기화한다.
문자열을 파싱하고 유저 스택에 인자 값이 저장될 수 있도록 기존의 process_execute() 함수와 start_process() 함수를 수정한다.
문자열 토큰화 함수를 이용해 커맨드 라인의 첫 번째 토큰을 thread_create() 함수에 쓰레드 이름으로 전달한다.
argument_stack() 함수를 이용해 스택에 토큰들을 저장한다.
process_execute() 함수를 수정한다.
thread_create() 함수를 호출하여 프로그램을 실행할 쓰레드를 생성한다.
쓰레드 이름 (file_name), 쓰레드 우선순위 (PRI_DEFAULT), 생성된 쓰레드가 실행할 함수를 가리키는 포인터 (start_process), start_process() 함수를 수행할 때 사용하는 인자 값 (fn_copy)를 인자로 입력한다.
start_process() 함수를 수정하여 실행 파일 로드 기능을 추가한다.
strtok_r() 함수를 이용해 인자들을 토큰화하여 토큰의 개수를 계산한다.
실행 파일 이름을 load() 함수의 첫 번째 인자로 전달한다.
파싱된 토큰을 유저 스택에 저장하는 argument_stack() 함수를 추가한다.
argument_stack() 함수는 프로그램 이름과 인자가 저장되어 있는 메모리 공간 (parse), 인자의 개수 (count), 스택 포인터를 가리키는 주소 (esp)를 인자로 입력한다.
argument_stack() 함수를 호출할 시 인자 값을 스택에 오른쪽에서 왼쪽 순으로 저장한다.
Return Address는 Caller(함수를 호출하는 부분)의 다음 수행 명령어 주소를 의미한다.
Callee(호출 받은 함수)의 리턴 값은 eax 레지스터에 저장된다.
기존 PintOS 시스템에서는 유저 스택에 프로그램 인자 값이 삽입되지 않기 때문에 유저 프로그램을 실행할 시 유저 스택은 비어 있는 상태다.
따라서 인자들을 스택에 삽입하는 기능을 추가해야 한다.
예를 들어 '$pintos run 'echo x'라는 커맨드 라인을 입력할 시, 유저 스택의 상단에는 'echo'와 'x'라는 인자가 저장되고 중단에는 각 인자들의 주소값 argv[0], argv[1], *argv[2]가 아래에서 부터 위로 저장된다. 하단에는 인자의 갯수 argc와 중단에 저장된 주소값들의 주소값 **argv가 저장되고 마지막으로 함수를 호출하는 부분의 다음 수행 명령어 주소인 return address가 저장된다.
esp: 스택 포인터를 가리키는 주소값 (void **esp); 스택 포인터는 스택 주소를 감소시키면서 (높은 메모리 주소에서 낮은 메모리 주소로 이동하면서) 인자를 스택에 삽입한다.
parse: 프로그램 이름은 이름과 인자가 저장되어 있는 메모리 공간 (char **parse)
argument_stack() 함수는 프로그램 이름과 인자가 저장되어 있는 메모리 공간 parse, 인자의 개수 count, 스택 포인터를 가리키는 주소값 esp를 인자로 입력한다.
유저 프로그램이 실행되기 전에 argument_stack() 함수를 호출하여 스택에 인자를 저장한다. (start_process() 함수에 포함)
인터럽트 프레임(if_)에 저장된 유저 프로그램 컨텍스트를 유저 스택으로 복사한 후 'intr_exit' 위치로 실행 흐름을 변경한다.
유저 스택에 저장된 유저 프로그램 컨텍스트를 각 레지스터에 세팅한다.
'$pintos -v -- run 'echo x''가 입력되었을 때,
00 00 00 00 거짓 return address, 02 00 00 00 인자의 갯수 argc, ec ff ff bf 인자의 값, 65 63 68 6f argv[0] echo의 주소값, 78 argv[1]의 주소값이 출력된다.
process_execute() (const char *file_name): 프로그램을 실행 할 프로세스 생성
startprocess() (void *file_name): 프로그램을 메모리에 적재하고 응용 프로그램 실행
argument_stack() (char parse, int count, void esp): 함수 호출 규약에 따라 유저 스택에 프로그램 이름과 인자들을 저장
안녕하세요, could you please share the slides?Thank u very much:)