for가 fork인 이유…

Screen Shot 2022-11-27 at 11.29.31 PM.png

pid_t fork (const char *thread_name);

시스템 콜 fork는 인자로 전달한 thread_name 의 이름을 가진 복제본 프로세스를 생성한다.

이때 복제본이라는 것은 동일한 유저가상메모리, 동일한 fd_list (file_descriptor 리스트), 동일한 user-level 문맥을 가져야 한다는 것을 의미한다. fork 함수는 부모프로세스에게 복제본 프로세스(이하 자식 프로세스)의 pid를 반환하며, 자식 프로세스의 경우 이 값이 0 이다.

예를 들어, a = fork(process) 를 실행하면 fork() 함수가 리턴되기 직전에는 두 개의 프로세스가 존재한다. 하나는 fork()를 실행한 부모 프로세스이고, 나머지 하나는 fork()로 인해 생성된 자식 프로세스이다. 이때 fork()가 리턴하는 값 a는 부모 프로세스의 경우에는 자식 프로세스의 pid이고, 자식 프로세스의 경우에는 0 이다. 따라서 이 값으로 부모 프로세스와 자식 프로세스를 구분할 수 있다.

부모 프로세스는 fork를 반환하기 전에 자식 프로세스가 성공적으로 복제되어있는지의 여부를 반드시 알아야 한다. 따라서 자식 프로세스가 리소스를 모두 복제할 때까지 기다려야 하며, 자식 프로세스가 리소스를 복제하지 못하면 부모의 fork() 호출은 TID_ERROR를 반환한다.

fork를 실행하면 시스템 콜 핸들러에 의해 syscall_fork가 실행된다.

syscall_fork

pid_t syscall_fork (struct intr_frame *f){
	char *thread_name = f->R.rdi;
	int return_value;
	return_value = process_fork(thread_name, f);
	f->R.rax = return_value;
}

syscall_fork는 process_fork 라는 함수에 스레드 이름과 인터럽트 프레임을 인자로 전달해서 호출한다.

인터럽트 프레임에는 부모 프로세스가 유저모드에서 실행했던 모든 문맥들이 담겨있기 때문에 자식 프로세스는 이 정보를 자신의 스레드 구조체 내의 tf 로 갖고 있어야 한다.

process_fork

tid_t process_fork(const char *name, struct intr_frame *if_)
{
	struct fork_info *fork_info = (struct fork_info *)malloc(sizeof(struct fork_info));
	fork_info->parent_t = thread_current();
	fork_info->if_ = if_;

	tid_t pid = thread_create(name, PRI_DEFAULT, __do_fork, fork_info);
	process_fork_sema_down();

if (!thread_current()->child_forked_ok)
	{
		return -1;
	}
	return pid;
}

🔎 fork_sema 의 흐름에 대한 정리는 여기서!