ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [PintOS] Project 2 - Argument Passing (1)
    Projects/Krafton_Jungle_4 2024. 3. 15. 01:03
    728x90

    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의 위치로 놓는다는 것을 알았다.

    이제 스택 공부를 할 차례다.

Designed by Tistory.