Unix, Linux에서 프로그램을 실행 할때는 보통 프로그램 이름 인자1 인자2 인자3 인자4
와 같은 실행 형태를 갖춘다. 그럼 이런 형태는 어떻게 이름으로 구분하고 인자를 구분하는가? 이제부터 그렇게 나누는 과정을 직접 다루고 완성해보자.
해당 글에서 이어진다.
2025.05.20 - [구현하기] - PintOS P2 #1 : User Program 서론
PintOS P2 #1 : User Program 서론
막막함의 강도가 남다르게 올라가는 요 근래의 주이지만, 나도 그렇게 호락호락 한 사람은 아니다. 글씨 몇 자와 pass/FAIL 하나 때문에 사활을 걸고 임하기에 몰입이란 단어를 온 신경으로 증명하
hyeonistic.tistory.com
나는 기존에 P1 #1에서 전체적인 PintOS의 부팅 과정을 보았다.
이제 이 전체 맥락에서 User Program 전용 옵션이 켜지면 내용이 조금은 달라진다.
2025.05.12 - [구현하기] - PintOS #1 : 진행 흐름도 파악하기
PintOS #1 : 진행 흐름도 파악하기
정글의 커리큘럼으로 진행으로 진행 중인 PintOS의 테스트 데이터가 어떻게 입력되고 작동되는지에 대해 논해보겠다. 우리는 64bit의 KAIST PintOS에 대해 다루고 있다. 이 논함은 정규 커리큘럼은 아
hyeonistic.tistory.com
pintos-kaist/threads/init.c
#ifdef USERPROG
exception_init ();
syscall_init ();
#endif
이제부터 문제가 생기면 exception 오류를 받을 수 있고, 그리고 유저 프로그램이 접근 할 수 있게 하는 syscall component에 대한 초기화를 완료했다. 본격적으로 User Program을 실행 준비하는 일련의 과정인 것이다.
기존 코드에는 run_task를 통해 곧바로 테스트 케이스에 들어가는 내용을 보았다. 하지만, 이젠 조금 달라졌다.
#ifdef USERPROG
if (thread_tests){
run_test (task);
} else {
process_wait (process_create_initd (task));
}
...
이제 새로운 함수 process_create_initd(..) 와 process_wait(...)이 나타났다.
- process_create_initd :
- 입력 받은 내용을 바탕으로 User Program을 실행 할 수 있는 쓰레드를 생성한다. 그리고 파싱의 작업을 비롯한 스택 설정 작업도 여기에 이루어지도록 해야한다.
- 우린 이 내용을 이 글에서 다룬다.
- process_wait :
- 우측에 대한 코드가 끝내기 전까지 기다려야한..다.. 근데, 지금 구현하기엔 할게 너무 많다. 공식 문서에서도 완전한 작동 전까지는 무한 루프를 걸어두도록 언급하고 있다.
pintos-kaist/userprog/process.c
tid_t
process_create_initd (const char *file_name) {
char *fn_copy;
tid_t tid;
/* Make a copy of FILE_NAME.
* Otherwise there's a race between the caller and load(). */
fn_copy = palloc_get_page (0);
if (fn_copy == NULL)
return TID_ERROR;
strlcpy (fn_copy, file_name, PGSIZE);
/* Create a new thread to execute FILE_NAME. */
tid = thread_create (file_name, PRI_DEFAULT, initd, fn_copy);
if (tid == TID_ERROR)
palloc_free_page (fn_copy);
return tid;
}
앞에서 task를 file_name이라는 매개변수로 가져와서 fn_copy에 복사해준다음, thread_create의 thread name란에 그대로 부여한다. 여기서 문제는, file_name은 엄연히 task라서 file_name만을 담고 있지 않다. 즉, 내용을 적절히 잘라야한다. 파싱은 여기서 시작하던가, 여기에서만 하던가 해야한다. 나는 관련 작성을 한 글에 할거라서 다음 글에 이 내용이 어떻게 수정 되었는지를 다루겠다.
- thread_create를 통해 만들어진 file_name이라는 이름의 쓰레드는 initd라는 함수를 fn_copy라는 매개변수와 함께 실행한다.
해당 파일은 이어진다.
static void initd (void *f_name) {
process_init ();
if (process_exec (f_name) < 0)
PANIC("Fail to launch initd\n");
NOT_REACHED ();
}
int process_exec (void *f_name) {
char *file_name = f_name;
bool success;
// point of implement : do parse here!!
struct intr_frame _if;
_if.ds = _if.es = _if.ss = SEL_UDSEG;
_if.cs = SEL_UCSEG;
_if.eflags = FLAG_IF | FLAG_MBS;
process_cleanup ();
success = load (file_name, &_if);
// point of implement : stack insert!!
palloc_free_page (file_name);
if (!success) return -1;
do_iret (&_if);
NOT_REACHED ();
}
point of implement : do parse here!!를 보자.
- 사실, load에서 file_name을 이용한 파일 열람이 직접적으로 일어나기 전까지는 파싱을 끝내야한다. 그래서 그냥 한 함수에 끝내려는 의도이다.
point of implement : stack insert!!를 주목하자.
- 왜 이 위치를 선택했느냐?
- load() 함수는 내부에 setup_stack(..) 함수를 포함한다. 즉, 이 위치가 지난 시점에서 stack 포인터를 활용 할 수있다.
- palloc_free_page(file_name)이 진행 되기전 스택에 직접 매핑이 이루어져야한다.
스택 삽입이 끝나면 do_iret()이 호출되어서 이제 유저 프로그램이 본격적으로 실행되는 단계가 시작된다. 다음 글에서 차례로 구현을 다뤄보자.
'구현하기' 카테고리의 다른 글
PintOS P2 #4. System Call 서론 (0) | 2025.05.28 |
---|---|
PintOS P2 #3 : Argument Passing 본편 (0) | 2025.05.21 |
PintOS P2 #1 : User Program 서론 (0) | 2025.05.20 |
PintOS #3 : Priority Scheduling (0) | 2025.05.16 |
PintOS #2 : Alarm Clock (0) | 2025.05.16 |