-
[PintOS] Project 2 - Argument Passing (1)Projects/Krafton_Jungle_4 2024. 3. 15. 01:03728x90
Argument passing
int main()
각종 초기화 진행 및 부팅 시작
만약 명령줄에 다음과 같이 입력되었다고 치면
read_comman_line을 통해 앞의 pintos 구동과 관련된 명령어를 제외하게 된다.
pintos -v -k -T 60 -m 20 --fs-disk=10 -p tests/userprog/args-single:args-single -- -q -f run 'args-single onearg'
다음과 같은 명령어를 실행한다고 했을 때, main에 들어오게 되는 제일 처음 action과 argv는 다음과 같다.
printf("Boot complete.\\n"); 후 run_actions(argv) 를 호출한다.
run_action의 action 배열이 {run, 2, run_task}와 같다면 즉, 만약 명령어가 run 이라면 run_task() 를 실행한다.
run_actions(argv)
static void run_actions(char **argv) { /* An action. */ struct action { char *name; /* Action name. */ int argc; /* # of args, including action name. */ void (*function)(char **argv); /* Function to execute action. */ }; /* Table of supported actions. */ static const struct action actions[] = { {"run", 2, run_task}, #ifdef FILESYS {"ls", 1, fsutil_ls}, {"cat", 2, fsutil_cat}, {"rm", 2, fsutil_rm}, {"put", 2, fsutil_put}, {"get", 2, fsutil_get}, #endif {NULL, 0, NULL}, }; while (*argv != NULL) { const struct action *a; int i; /* Find action name. */ for (a = actions;; a++) if (a->name == NULL) PANIC("unknown action `%s' (use -h for help)", *argv); else if (!strcmp(*argv, a->name)) break; /* Check for required arguments. */ for (i = 1; i < a->argc; i++) if (argv[i] == NULL) PANIC("action `%s' requires %d argument(s)", *argv, a->argc - 1); /* Invoke action and advance. */ a->function(argv); argv += a->argc; } }
argv 배열로 들어온 터미널 명령을 수행
action 구조체
- name: 작업의 이름을 나타내는 문자열
- argc: 작업을 실행하는 데 필요한 인자의 총 개수입니다. 작업 이름도 인자에 포함
- function: 해당 작업을 수행하는 함수의 포인터
while(*argv != NULL)
- 명령어 파싱
- ex) run ‘args-single onearg’의 action은 run이다.
run
run_task(char **argv) { const char *task = argv[1]; printf("run_task에서 argv 배열: %s, %s, %s \\n\\n", *argv, *(argv + 1), *(argv + 2)); printf("Executing '%s':\\n", task); #ifdef USERPROG if (thread_tests) { run_test(task); } else { process_wait(process_create_initd(task)); } #else run_test(task); #endif printf("Execution of '%s' complete.\\n", task); }
static void run_task(char **argv)
static void run_task (char **argv) { const char *task = argv[1]; printf ("Executing '%s':\\n", task); #ifdef USERPROG if (thread_tests){ run_test (task); } else { process_wait (process_create_initd (task)); } #else run_test (task); #endif printf ("Execution of '%s' complete.\\n", task); }
#ifdef USERPROG에 걸리고
우리의 명령줄에는 일단 thread_tests가 없으니 else 문으로 빠진다.
process_wait()이 실행되고 인자로 process_create_initd(task)를 넣어준다.
tid_t process_create_initd(const char *file_name)
이거 왜 file_name_parsing 해주어야함?
→ static void run_task(char **argv)에서 호출되기 때문임
얘의 역할은 initd를 하는 프로세스를 만드는 것임
thread_create(const char *name, int priority, thread_func *function, void *aux)를 통해 file_name을 실행할 스레드를 만들어줌
이 때, 인자로 file_name의 이름으로 initd라는 함수를 넣어주는데,
initd는 다음과 같다.
**static void initd(void *f_name)**
static void initd(void *f_name) { #ifdef VM supplemental_page_table_init(&thread_current()->spt); #endif process_init(); if (process_exec(f_name) < 0) PANIC("Fail to launch initd\\n"); NOT_REACHED(); }
주석에 ‘첫 번째 유저 프로세스를 실행시키는 함수’라고 나와있다.
즉, process_init()을 수행하는데 process_init()을 보면
static void process_init(void)
/* General process initializer for initd and other process. */ static void process_init(void) { struct thread *current = thread_current(); }
프로세스를 초기화해주는 함수라고 나와있고, thread_current()를 호출한다.
이때 thread_yield()를 통해 스케쥴해준다.
여기서 스레드가 스케쥴 되는 것 같다.
이후 create_thread() 에서 빠져나오고
이후 실행할 스레드를 다 만들어주고 나면 process_wait()으로 돌아가서
process_wait() 에서 무한 루프를 돌며 대기한다.
→ load() 의 file_sys()에서 터지지 않으려면 무한루프 처리 해줘야 한다.
왜 터지는지는 나중에 알아봐야 할듯
- tid = thread_create(*file_name*, PRI_DEFAULT, initd, fn_copy);그리고 이 initd에서 최종적으로 process_exec를 해준다!!!!!!!!!
- 여기에서 initd는 thread_create가 수행되고 난 다음 실행되는 것이 맞다.
int process_exec(void **f_name*)
int process_exec(void *f_name) { char *file_name = f_name; bool success; /* We cannot use the intr_frame in the thread structure. * This is because when current thread rescheduled, * it stores the execution information to the member. */ struct intr_frame _if; _if.ds = _if.es = _if.ss = SEL_UDSEG; _if.cs = SEL_UCSEG; _if.eflags = FLAG_IF | FLAG_MBS; /* We first kill the current context */ process_cleanup(); printf("로드할 파일 이름: %s \\n\\n", file_name); /* And then load the binary */ success = load(file_name, &_if); /* If load failed, quit. */ palloc_free_page(file_name); if (!success) return -1; /* Start switched process. */ do_iret(&_if); NOT_REACHED(); }
이 load에서 더 이상 안넘어 가지는데… 일단 load를 까보자
success = load(file_name, &_if);
load(const char *file_name, struct intr_frame *if_) { struct thread *t = thread_current(); struct ELF ehdr; struct file *file = NULL; off_t file_ofs; bool success = false; int i; /* Allocate and activate page directory. */ t->pml4 = pml4_create(); if (t->pml4 == NULL) goto done; process_activate(thread_current()); /* Open executable file. */ file = filesys_open(file_name); if (file == NULL) { printf("load: %s: open failed\\n", file_name); goto done; } /* Read and verify executable header. */ if (file_read(file, &ehdr, sizeof ehdr) != sizeof ehdr || memcmp(ehdr.e_ident, "\\177ELF\\2\\1\\1", 7) || ehdr.e_type != 2 || ehdr.e_machine != 0x3E // amd64 || ehdr.e_version != 1 || ehdr.e_phentsize != sizeof(struct Phdr) || ehdr.e_phnum > 1024) { printf("load: %s: error loading executable\\n", file_name); goto done; } /* Read program headers. */ file_ofs = ehdr.e_phoff; for (i = 0; i < ehdr.e_phnum; i++) { struct Phdr phdr; if (file_ofs < 0 || file_ofs > file_length(file)) goto done; file_seek(file, file_ofs); if (file_read(file, &phdr, sizeof phdr) != sizeof phdr) goto done; file_ofs += sizeof phdr; switch (phdr.p_type) { case PT_NULL: case PT_NOTE: case PT_PHDR: case PT_STACK: default: /* Ignore this segment. */ break; case PT_DYNAMIC: case PT_INTERP: case PT_SHLIB: goto done; case PT_LOAD: if (validate_segment(&phdr, file)) { bool writable = (phdr.p_flags & PF_W) != 0; uint64_t file_page = phdr.p_offset & ~PGMASK; uint64_t mem_page = phdr.p_vaddr & ~PGMASK; uint64_t page_offset = phdr.p_vaddr & PGMASK; uint32_t read_bytes, zero_bytes; if (phdr.p_filesz > 0) { /* Normal segment. * Read initial part from disk and zero the rest. */ read_bytes = page_offset + phdr.p_filesz; zero_bytes = (ROUND_UP(page_offset + phdr.p_memsz, PGSIZE) - read_bytes); } else { /* Entirely zero. * Don't read anything from disk. */ read_bytes = 0; zero_bytes = ROUND_UP(page_offset + phdr.p_memsz, PGSIZE); } if (!load_segment(file, file_page, (void *)mem_page, read_bytes, zero_bytes, writable)) goto done; } else goto done; break; } } /* Set up stack. */ if (!setup_stack(if_)) goto done; /* Start address. */ if_->rip = ehdr.e_entry; /* TODO: Your code goes here. * TODO: Implement argument passing (see project2/argument_passing.html). */ success = true; done: /* We arrive here whether the load is successful or not. */ file_close(file); return success; }
그리고 이 안의 filesys_open() 에서 터지는 것 같다.
process_wait()을 무한 루프로 구현하니 syscall을 부르고 timeout을 하긴 하는데.. 잘 모르겠다.
일단 여러 팀원들의 도움을 받아 setup_stack()에서 if_의 rsp를 USER_STACK의 위치로 놓는다는 것을 알았다.
이제 스택 공부를 할 차례다.
'Projects > Krafton_Jungle_4' 카테고리의 다른 글
[PintOS] Project 3 - Virtual Memory git book, Introduction (2) 2024.03.23 [PintOS] Project 2 - System Call (3) (0) 2024.03.20 [PintOS] Project 2 - System Call (2) (0) 2024.03.19 [PintOS] Project 2 - User Memory Access (1) (0) 2024.03.18 [PintOS] Project 2 - Argument Passing (2) (4) 2024.03.16