핀토스 운영체제에서 유저프로세스가 어떻게 실행되는지에 대한 구체적인 흐름을 꼭 잡고 가고싶어서 기록해본다.

init.c 파일 main() 함수

언제나 시작은 init.c 로 부터. main() 함수 내에서 모든 부팅 과정을 마치면 run_actions (argv); 로 함수를 호출한다. argv는 입력한 명령어가 공백을 기준으로 분리된 배열이다.

i.e. 만약 명령어가 run alarm-single 이었으면, argv[0] = run, argv[1] = alarm-single, argv[2]= NULL 인 상태

run_actions

actions 배열을 순회하면서 명령어의 첫번째 단어와 매칭되는 함수를 찾아서 실행한다. 유저 프로그램의 경우 run 명령어로 실행하므로 run과 매칭되는 함수인 run_task를 실행한다. 아래의 코드가 run_task(argv)를 호출하는 부분.

/* Invoke action and advance. */
		a->function (argv);

run_task(argv)

run_task는 명령어에서 실행해야 할 task를 parsing 한다. argv에서 argv[0]은 run, ls 등 실행해야 할 명령의 종류가 담겨있다면 argv[1] 에는 어떤 파일을 실행해야 하는지에 대한 정보가 담겨있다. 이 부분을 task로 분리한다.

const char *task = argv[1];

이후 task 로 테스트를 실행하거나, 유저프로그램을 실행한다. 유저 프로그램이 실행되는 경우 아래의 코드를 통해 실행되는데, 이 흐름을 따라 가보자.

process_wait (process_create_initd (task)); 

process_create_initd(task)

이 함수가 인자로 받는 task는 ‘실행해야 할 파일 이름’ 일 수도 있지만 ‘실행해야 할 파일 이름+ 전달될 인자들’ 일 수도 있다. 따라서 첫 공백을 기준으로 실행해야 할 파일이름을 파싱하여, 이 이름으로 스레드를 생성한다.

이 스레드는 생성된 후 readylist 로 갈 것이고, 이 스레드가 실행될 때 initd(fn_copy);를 실행할 것이다.

tid_t
 process_create_initd (const char *file_name) { 
	
		char *fn_copy;
    char *save_ptr;
    char *not_used;
		tid_t tid;

	fn_copy = palloc_get_page (0);
	if (fn_copy == NULL)
		return TID_ERROR;
	strlcpy (fn_copy, file_name, PGSIZE); 

	file_name = strtok_r(file_name," ",&not_used); 
	tid = thread_create (file_name, PRI_DEFAULT, initd, fn_copy); 

	if (tid == TID_ERROR)
		palloc_free_page (fn_copy);
	return tid;
}

💡 filename을 굳이 copy해서 써야 하는 이유는?

💡 palloc_get_page() 없이 fn_copy에 바로 strlcopy를 하면 안 되는가?