PintOS P2 #2 : Argument Passing을 다루기 전

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