

init.c
init.c의 main 함수에서 read_command_line()함수를 호출하여 명령어를 읽어온다. -> argv
명령어로 들어오는 인자의 형태는 명령어와 그 명령어의 대상이다.
예를 들어 인자가 1개만 들어오는 경우(args-single.ck)라면, argv는 run 'args-single onearg'의 형태를 가진다.
호출된 명령어 parse_options를 통해 option에 따라 명령어를 적절히 parsing한다.
명령어는 run_action 함수의 인자로 전달된다. argv안의 명령어 문자열에 있는 명령어 run은 run_task 함수를 호출한다.
run_task 함수에서 task = argv[1]로 정해진다. 그 이유는 명령어로 들어오는 인자의 형태는 명령어와 그 명령어의 대상이다. 따라서 명령어의 대상에 해당되는 인자인 'args-single onearg'가 process_create_initd의 인자가 된다.
해당 인자는 프로그램 파일이름과 프로그램들의 인자들이 같이 위치한다. 따라서 프로그램 파일명과 인자들을 공백을 기준으로 parsing하여야 한다.
process.c
process_create_initd()의 인자로 입력받은 명령어의 대상(task = argv[1])이 들어온다. 해당 문자열은 thread_create의 인자로 들어가서 해당 인자를 이름으로 한 kernel 스레드를 생성한다. 해당 스레드는 생성된 이후 running thread가 되는 시점에 인자로 들어간 함수 initd를 호출한다.
initd() 함수는 process_exec()함수를 호출한다.
process_exec() 함수는 load()함수를 호출한다.
load함수에서 입력받은 인자를 parsing하고 해당 커널 스레드의 인터럽트 프레임에 인자의 주소를 저장하고, 이후 실행할 명령어의 주소를 스택에 저장한다.(if->rip)
이때 filesys_open에 전달되는 file_name은 입력받은 인자를 공백으로 parsing할 때 등장하는 첫 번째 문자열이다.두 번째 문자열부터 인자가 된다.
파싱한 문자열을 스택에 쌓는다. 스택은 주소가 감소하면서 확장한다.(위->아래). 인자 -> 8바이트 정렬을 맞추기 위한 공백 공간 -> 파싱한 인자의 스택 주소 -> 가짜 반환 주소 순으로 저장한다.
과정 중 헷갈렸던 부분과 알게 된 점
userprog/process.c의 tid_t process_create_initd (const char *file_name)thread_create 함수에서 인자로 받은 함수가 언제 실행되는 것인가?thread_create(file_name, PRI_DEFAULT, initd, fn_copy)를 호출하며 새로운 kernel 스레드를 생성한다. 이 kernel 스레드는 initd() 함수를 thread routine으로 가지는 스레드로, 생성 후 ready list에 들어간 뒤, running thread가 되는 시점에 initd()를 실행한다.다만 해당 함수는 반환되지 않기 때문에 해당 return address로 이동하지 않는다. 하지만 다른 스택 프레임과 동일한 구조를 갖기 위해서 가짜 반환주소를 넣는다.

load()(in userprog/process.c)내에서 구현한다.thread.h 내의 struct fild_fd 선언과 struct thread 내 멤버 추가struct file_fd
{
int fd; /* fd: 파일 식별자 */
struct file *file; /* file */
struct list_elem fd_elem; /* list 구조체의 구성원 */
};
struct thread
{
....
struct list fd_list; /* file_fd 구조체를 저장하는 Doubley Linked List */
int fd_count; /* fd를 확인하기 위한 count*/
int exit_status
struct semaphore fork_sema; /* 자식 프로세스의 fork가 완료될 때까지 기다리도록 하기 위한 세마포어 */
struct semaphore wait_sema;
struct semaphore exit_sema;
struct list child_list; /* 자식 스레드를 보관하는 리스트 */
struct list_elem child_elem; /* 자식 리스트 element */
struct file *now_file; /* 현재 프로세스가 실행 중인 파일을 저장하기 위한 변수 */
....
};
목표: 총 14개의 구현해야할 syscall
프로세스 관련 system call
halt(), wait(), fork(), exit(), exec()파일 관련 system call
open(), filesize(), close(), read(), write(), seek(), tell(), create(), remove()create()와 remove()를 제외한 파일 관련 system call들은 file descriptor를 반환하거나, file descriptor를 이용해서 file에 대한 작업을 수행한다.
kernel은 file descriptor와 실제 file 구조체를 매핑하여 관리하며 이를 위한 도구가 바로 fd table이다.
halt(): pintos를 종료시키는 시스템 콜power_off()함수를 사용하여 pintos를 종료시켰다.exit(): 현재 프로세스를 종료시키는 시스템 콜exit() 함수를 호출했거나 다른 어떤 이유들로 유저 프로세스 종료 시 프로세스 이름과 exit code를 아래와 같이 지정된 형식으로 출력한다.printf("%s: exit(%d)\n", ....);filesize(): fd로 열려있는 파일 사이즈를 리턴해주는 시스템 콜fd_list를 순회하면서 찾으려는 fd와 mapping된 file을 찾는다.file_length()를 호출한다.seek() : fd로 열려있는 파일의 (읽고 쓸 위치를 알려주는) 포인터의 위치를 변경해주는 시스템 콜fd_list를 순회하면서 찾으려는 fd와 mapping된 file을 찾는다.file_seek()를 호출한다.tell() : fd에서 읽히거나 써질 다음 바이트의 위치를 반환fd_list를 순회하면서 찾으려는 fd와 mapping된 file을 찾는다.file_tell()를 호출한다.filesize(), seek(), tell()은 시스템 콜이 잘 동작하는지 확인할 수 있는 test case 부제로 test를 돌릴 수 있는 시스템 콜에서 호출하는 방식으로 호출됨을 확인하였다.~~create() : 파일을 생성하는 시스템 콜
filesys_create()를 호출 한다.remove(): 파일을 삭제하는 시스템 콜
filesys_remove()를 호출한다.read() : fd를 통해 열린 파일의 데이터를 읽는 시스템 콜.file_read() 함수를 호출함.write() : fd를 통해 열린 파일의 데이터를 기록하는 시스템 콜.file_write() 함수를 호출함.file_deny_write(): 파일을 open 할 때, 실행 파일에 대해 쓰기를 방지한다. 메모리에 파일을 load한 후에 수정하면 안 되기 때문이다. 이를 통해, file synchronization Issue를 해결할 수 있다.file_allow_write(): 파일의 데이터가 변경되는 것을 허락하는 함수이다. file_close() 함수 호출 시 해당 함수가 호출된다.exec() : 현재 프로세스를 인자로 주어진 이름을 갖는 실행 파일로 변경하는 시스템 콜.
process_exec() 호출한다.fork() : 현재 프로세스의 복제본인 프로세스를 생성하는 시스템 콜
movq %rsp, %rbx를 통해 rbx에 유저스택포인터가 저장되고 있으며, 10-12행의 movabs $tss, %r12 -> movq (%r12), %r12 -> movq 4(%r12), %rsp를 통해 rsp에는 커널 스택 포인터가 저장된다.fork_data를 선언하였다. fork_data 구조체의 멤버변수로 thread *parent와 intr_frame *user_level_f를 활용하여 부모 스레드와 유저레벨 인터럽트를 저장한 후 해당 구조체를 __do_fork의 인자로 전달하였다.__do_fork함수 내부에서 pml4_for_each()함수를 통해 인자로 duplicate_pte()함수를 실행하여 실행부모의 유저 메모리 공간을 복사하여 새로 생성한 자식의 페이지에 넣어준다.fd_list의 요소들과 fd_count를 복사하여준다.fork_sema를 활용해서 자식 프로세스에 대한 복사가 완료될때까지 process_fork함수가 끝나지 않도록 하였다.TID_ERROR를 반환하도록 한다.wait() : 자식 프로세스가 수행되고 종료될 때까지 부모 프로세스가 대기하는 시스템 콜process_exit()에서 open되어있는 파일을 close 하지 않았던 문제 struct list *exit_list = &curr->fd_list;
struct list_elem *start = list_begin(exit_list);
while (!list_empty(exit_list))
{
struct file_fd *exit_fd = list_entry(start, struct file_fd, fd_elem);
file_close(exit_fd->file);
start = list_remove(&exit_fd->fd_elem);
free(exit_fd);
}
process_exit() 즉, 프로세스를 종료해야할 시점에서 열린 모든 파일을 close 해주어야한다.write()와 read()를 할 때 입력받는 인자 buff가 올바른 address 인지 체크하지 않았던 문제process_wait()와 process_exit() sema_down과 sema_up의 순서파악