PintOS_2 | User Programs | Flow of PintOS

wony·2022년 6월 26일
0

핀토스 2주차 userprogram, thread보다 훨씬 복잡하고 읽어야할 코드도 많아졌다. 하나씩도 봐야겠지만 큰 흐름이 너무 간절하다. 그래서 조금 오래 걸릴지라도 main부터 흘러가는 코드의 흐름을 따라가보았다.

원리는 단순했다

  1. 모르는 게 나오면 타고 들어간다.
  2. 뭐하는 함수인지 알게되면 돌아나와서 대입하고 이해해본다

노션에서 볼땐 토글이 가능해서 조금 볼만했는데 이렇게 옮겨놓으니 가독성이 너무 떨어진다.. 더 좋은 방법을 찾으면 수정해야겠다;

main()

/* Pintos main program. */
int
main (void) {
  	printf("##### ##### threads/init.c main() start ----- \n");
	uint64_t mem_end;
	char **argv;

	/* Clear BSS and get machine's RAM size. */
	bss_init ();					        // bss 세그먼트 0으로 초기화

	/* Break command line into arguments and parse options. */
	argv = read_command_line ();	// command_line을 읽고 argv를 잘라줌
									              // -q -f put args-single run 'args-single oneargs' 만 남김
	argv = parse_options (argv);	// 옵션 파싱해서 해당되는 변수(flag같은 기능)를 true로 바꿔줌
									              // (e.g. mlfq, thread_tests)

	/* Initialize ourselves as a thread so we can use locks,
	   then enable console locking. */
	thread_init ();               // 프로그램을 시작한 thread, "main" 초기화
	console_init ();              // console_lock을 초기화
																// -> vprintf, putbuf 등 출력에 사용되는 lock

	/* Initialize memory system. */
	mem_end = [palloc_init](https://www.notion.so/OS-Project-2-User-Programs-bb14ba07b63a4d72acd2a692f1f2a1e4) ();		  // page allocator를 초기화하고 초기 메모리 사이즈를 반환함
	[malloc_init](https://www.notion.so/OS-Project-2-User-Programs-bb14ba07b63a4d72acd2a692f1f2a1e4) ();
	[paging_init](https://www.notion.so/OS-Project-2-User-Programs-bb14ba07b63a4d72acd2a692f1f2a1e4) (mem_end);			  // page table을 커널가상매핑으로 채우고(?) 새 페이지 디렉터리를 사용하도록 cpu 세팅, pml4_activate

#ifdef USERPROG
	tss_init ();					        // tss를 위한 페이지를 할당하고 0으로 초기화, tss->rsp0에 커널 스택 포인터 저장
	gdt_init ();					        // global descriptor table 초기화
#endif

	/* Initialize interrupt handlers. */
	intr_init ();
	timer_init ();                // timer_interrupt()
	kbd_init ();                  // keyboard_interrupt()
	input_init ();                // intq_init()

#ifdef USERPROG
	exception_init ();
	syscall_init ();
#endif

	/* Start thread scheduler and enable interrupts. */
	thread_start ();				      // idle 스레드 생성
	serial_init_queue ();
	timer_calibrate ();

#ifdef FILESYS
	/* Initialize file system. */
	disk_init ();
	filesys_init (format_filesys);
#endif

#ifdef VM
	vm_init ();
#endif

	printf ("Boot complete.\n");

	/* Run actions specified on kernel command line. */
	**run_actions (argv);**

	/* Finish up. */
	if (power_off_when_done)
		power_off ();
	thread_exit ();
}
  • bss_init()
    /* Clear BSS */
    static void
    bss_init (void) {
    	/* The "BSS" is a segment that should be initialized to zeros.
    	   It isn't actually stored on disk or zeroed by the kernel
    	   loader, so we have to zero it ourselves.
    		 bss는 0으로 초기화해야하는 세그먼트
    		 실제로 디스크에 저장되거나 커널 로더에 의해 0이되지 않음
    		 그래서 우리가 직접 0으로 만들어줘야함
    
    	   The start and end of the BSS segment is recorded by the
    	   linker as _start_bss and _end_bss.  See kernel.lds. 
    		 bss 세그먼트의 시작과 끝은 _start_bss 와 _end_bss 링커에 의해 기록됨(kernel.lds)*/
    
    	extern char _start_bss, _end_bss;
    	memset (&_start_bss, 0, &_end_bss - &_start_bss);    // 0으로 초기화
    }
  • read_command_line()
    /* Breaks the kernel command line into words and returns them as
       an argv-like array. */
    // 물리주소를 가상주소로 변환시켜줌
    // 오버플로우 나면 패닉 만들어주고
    // 입력값 터미널에 출력해줌
    
    static char **
    read_command_line (void) {
    	static char *argv[LOADER_ARGS_LEN / 2 + 1];
    	char *p, *end;
    	int argc;
    	int i;
    
    	argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
    	p = ptov (LOADER_ARGS);
    	end = p + LOADER_ARGS_LEN;
    	for (i = 0; i < argc; i++) {
    		if (p >= end)
    			PANIC ("command line arguments overflow");
    
    		argv[i] = p;
    		p += strnlen (p, end - p) + 1;
    	}
    	argv[argc] = NULL;
    
    	/* Print kernel command line. */
    	printf ("Kernel command line:");
    	for (i = 0; i < argc; i++)
    		if (strchr (argv[i], ' ') == NULL)
    			printf (" %s", argv[i]);
    		else
    			printf (" '%s'", argv[i]);
    	printf ("\n");
    
    	return argv;
    }
    
    /*************************************************************/
    
    /* Sizes of loader data structures. */
    #define LOADER_SIG_LEN 2
    #define LOADER_ARGS_LEN 128
    #define LOADER_ARG_CNT_LEN 4
    
    #define LOADER_SIG (LOADER_END - LOADER_SIG_LEN)   /* 0xaa55 BIOS signature. */
    
    #define LOADER_ARGS (LOADER_SIG - LOADER_ARGS_LEN)     /* Command-line args. */
  • parse_options()
    /* Parses options in ARGV[]
       and returns the first non-option argument. */
    /* 옵션부분을 파싱해서 필요한 변수를 true로 설정 */
    static char **
    parse_options (char **argv) {
    	for (; *argv != NULL && **argv == '-'; argv++) {
    		char *save_ptr;
    		char *name = strtok_r (*argv, "=", &save_ptr);
    		char *value = strtok_r (NULL, "", &save_ptr);
    
    		if (!strcmp (name, "-h"))
    			usage ();
    		else if (!strcmp (name, "-q"))
    			power_off_when_done = true;
    #ifdef FILESYS
    		else if (!strcmp (name, "-f"))
    			format_filesys = true;
    #endif
    		else if (!strcmp (name, "-rs"))
    			random_init (atoi (value));
    		else if (!strcmp (name, "-mlfqs"))
    			thread_mlfqs = true;
    #ifdef USERPROG
    		else if (!strcmp (name, "-ul"))
    			user_page_limit = atoi (value);
    		else if (!strcmp (name, "-threads-tests"))
    			thread_tests = true;
    #endif
    		else
    			PANIC ("unknown option `%s' (use -h for help)", name);
    	}
    
    	return argv;
    }
  • thread_init()
    /* Initializes the threading system by transforming the code
       that's currently running into a thread.  This can't work in
       general and it is possible in this case only because loader.S
       was careful to put the bottom of the stack at a page boundary.
    
       Also initializes the run queue and the tid lock.
    
       After calling this function, be sure to initialize the page
       allocator before trying to create any threads with
       thread_create().
    
       It is not safe to call thread_current() until this function
       finishes. */
    /*  */
    void thread_init(void)
    {
    	ASSERT(intr_get_level() == INTR_OFF);
    
    	/* Reload the temporal gdt for the kernel
    	 * This gdt does not include the user context.
    	 * The kernel will rebuild the gdt with user context, in gdt_init (). */
    	struct desc_ptr gdt_ds = {
    		.size = sizeof(gdt) - 1,
    		.address = (uint64_t)gdt};
    	lgdt(&gdt_ds);
    
    	/* Init the globla thread context */
    	lock_init(&tid_lock);
    	list_init(&ready_list);
    	list_init(&sleep_list);
    	list_init(&destruction_req);
    
    	/* Set up a thread structure for the running thread. */
    	initial_thread = running_thread();
    	init_thread(initial_thread, "main", PRI_DEFAULT);
    	initial_thread->status = THREAD_RUNNING;
    	initial_thread->tid = allocate_tid();
    }
  • console_init()
    /* Enable console locking. */
    void
    console_init (void) {
    	lock_init (&console_lock);
    	use_console_lock = true;
    }
    • console_lock 사용위치 - acquire_console(), release_console()
      /* Enable console locking. */
      void
      **console_init** (void) {
      	lock_init (&console_lock);
      	use_console_lock = true;
      }
      
      /* Notifies the console that a kernel panic is underway,
         which warns it to avoid trying to take the console lock from
         now on. */
      void
      **console_panic** (void) {
      	use_console_lock = false;
      }
      
      /* Acquires the console lock. */
      	static void
      **acquire_console** (void) {
      	if (!intr_context () && use_console_lock) {
      		if (lock_held_by_current_thread (&console_lock)) 
      			console_lock_depth++; 
      		else
      			lock_acquire (&console_lock); 
      	}
      }
      
      /* Releases the console lock. */
      static void
      **release_console** (void) {
      	if (!intr_context () && use_console_lock) {
      		if (console_lock_depth > 0)
      			console_lock_depth--;
      		else
      			lock_release (&console_lock); 
      	}
      }
      
      /* Returns true if the current thread has the console lock,
         false otherwise. */
      static bool
      **console_locked_by_current_thread** (void) {
      	return (intr_context ()
      			|| !use_console_lock
      			|| lock_held_by_current_thread (&console_lock));
      }
      
      /* Writes C to the vga display and serial port.
         The caller has already acquired the console lock if
         appropriate. */
      static void
      **putchar_have_lock** (uint8_t c) {
      	ASSERT (console_locked_by_current_thread ());
      	write_cnt++;
      	serial_putc (c);
      	vga_putc (c);
      }
    • acquire_console 호출 위치 - vprintf(printf가 호출), putbuf(write가 호출), putchar(msg가 호출)
      lib/kernel/console.c:
      
      /* Acquires the console lock. */
      static void
      **acquire_console** (void) {
      	if (!intr_context () && use_console_lock) {
      		if (lock_held_by_current_thread (&console_lock)) 
      			console_lock_depth++; 
      		else
      			lock_acquire (&console_lock); 
      	}
      }
      
      /* The standard vprintf() function,
         which is like printf() but uses a va_list.
         Writes its output to both vga display and serial port. */
      int
      **vprintf** (const char *format, va_list args) {
      	int char_cnt = 0;
      
      	**acquire_console** ();
      	__vprintf (format, args, vprintf_helper, &char_cnt);
      	release_console ();
      
      	return char_cnt;
      }
      
      /* Writes string S to the console, followed by a new-line
         character. */
      int
      **puts** (const char *s) {
      	**acquire_console** ();
      	while (*s != '\0')
      		putchar_have_lock (*s++);
      	putchar_have_lock ('\n');
      	release_console ();
      
      	return 0;
      }
      
      /* Writes the N characters in BUFFER to the console. */
      void
      **putbuf** (const char *buffer, size_t n) {
      	**acquire_console** ();
      	while (n-- > 0)
      		putchar_have_lock (*buffer++);
      	release_console ();
      }
      
      /* Writes C to the vga display and serial port. */
      int
      **putchar** (int c) {
      	**acquire_console** ();
      	putchar_have_lock (c);
      	release_console ();
      
      	return c;
      }
    • /stdio.c/printf()
      /* Writes formatted output to the console.
         In the kernel, the console is both the video display and first
         serial port.
         In userspace, the console is file descriptor 1. */
      int
      **printf** (const char *format, ...) {
      	va_list args;
      	int retval;
      
      	va_start (args, format);
      	retval = **vprintf** (format, args);
      	va_end (args);
      
      	return retval;
      }
    • /syscall.c/write()
      int **write**(int fd, const void *buffer, unsigned size){
      	
      	...
      
      	else if(f_obj==2){			 // STDOUT : 표준출력
      		**putbuf**(buffer, size);
      		ret = size;
      	}
      	
      	...
      
      	return ret;
      }
    • /tests.c/msg()
      /* Prints FORMAT as if with printf(),
         prefixing the output by the name of the test
         and following it with a new-line character. */
      void **msg**(const char *format, ...)
      {
        va_list args;
      
        **printf**("(%s) ", test_name);
        va_start(args, format);  // #define va_start(LIST, ARG)	__builtin_va_start (LIST, ARG)
        **vprintf**(format, args);
        va_end(args);            // #define va_end(LIST) __builtin_va_end (LIST)
        **putchar**('\n');
      }
  • palloc_init()
    /* Initializes the page allocator and get the memory size */
    uint64_t
    palloc_init (void) {
      /* End of the kernel as recorded by the linker.
         See kernel.lds.S. */
    	extern char _end;
    	struct area base_mem = { .size = 0 };
    	struct area ext_mem = { .size = 0 };
    
    	resolve_area_info (&base_mem, &ext_mem);
    	// Iterate on the e820 entry, parse the range of basemem and extmem.
    	// **해석필요**
    	printf ("Pintos booting with: \n");
    	printf ("\tbase_mem: 0x%llx ~ 0x%llx (Usable: %'llu kB)\n",
    		  base_mem.start, base_mem.end, base_mem.size / 1024);
    	printf ("\text_mem: 0x%llx ~ 0x%llx (Usable: %'llu kB)\n",
    		  ext_mem.start, ext_mem.end, ext_mem.size / 1024);
    	populate_pools (&base_mem, &ext_mem);
    	return ext_mem.end;
    }
    • populate_pools()
      /*
       * Populate the pool.
       * All the pages are manged by this allocator, even include code page.
       * Basically, give half of memory to kernel, half to user.
       * We push base_mem portion to the kernel as much as possible.
       * 풀을 채움
       * 코드 페이지를 포함한 모든 페이지는 이 할당기에 의해 관리됨
       * 기본적으로 메모리의 절반은 커널에, 절반은 사용자에게 줌
       * 우리는 가능하난 base_mem 부분을 커널에 푸시한다? 준다?
       */
      static void
      populate_pools (struct area *base_mem, struct area *ext_mem) {
      	extern char _end;
      	void *free_start = pg_round_up (&_end);
      
      	uint64_t total_pages = (base_mem->size + ext_mem->size) / PGSIZE;
      	uint64_t user_pages = total_pages / 2 > user_page_limit ?
      		user_page_limit : total_pages / 2;
      	uint64_t kern_pages = total_pages - user_pages;
      
      	// Parse E820 map to claim the memory region for each pool.
      	enum { KERN_START, KERN, USER_START, USER } state = KERN_START;
      	uint64_t rem = kern_pages;
      	uint64_t region_start = 0, end = 0, start, size, size_in_pg;
      
      	struct multiboot_info *mb_info = ptov (MULTIBOOT_INFO);
      	struct e820_entry *entries = ptov (mb_info->mmap_base);
      
      	uint32_t i;
      	for (i = 0; i < mb_info->mmap_len / sizeof (struct e820_entry); i++) {
      		struct e820_entry *entry = &entries[i];
      		if (entry->type == ACPI_RECLAIMABLE || entry->type == USABLE) {
      			start = (uint64_t) ptov (APPEND_HILO (entry->mem_hi, entry->mem_lo));
      			size = APPEND_HILO (entry->len_hi, entry->len_lo);
      			end = start + size;
      			size_in_pg = size / PGSIZE;
      
      			if (state == KERN_START) {
      				region_start = start;
      				state = KERN;
      			}
      
      			switch (state) {
      				case KERN:
      					if (rem > size_in_pg) {
      						rem -= size_in_pg;
      						break;
      					}
      					// generate kernel pool
      					init_pool (&kernel_pool,
      							&free_start, region_start, start + rem * PGSIZE);
      					// Transition to the next state
      					if (rem == size_in_pg) {
      						rem = user_pages;
      						state = USER_START;
      					} else {
      						region_start = start + rem * PGSIZE;
      						rem = user_pages - size_in_pg + rem;
      						state = USER;
      					}
      					break;
      				case USER_START:
      					region_start = start;
      					state = USER;
      					break;
      				case USER:
      					if (rem > size_in_pg) {
      						rem -= size_in_pg;
      						break;
      					}
      					ASSERT (rem == size);
      					break;
      				default:
      					NOT_REACHED ();
      			}
      		}
      	}
      
      	// generate the user pool
      	init_pool(&user_pool, &free_start, region_start, end);
      
      	// Iterate over the e820_entry. Setup the usable.
      	uint64_t usable_bound = (uint64_t) free_start;
      	struct pool *pool;
      	void *pool_end;
      	size_t page_idx, page_cnt;
      
      	for (i = 0; i < mb_info->mmap_len / sizeof (struct e820_entry); i++) {
      		struct e820_entry *entry = &entries[i];
      		if (entry->type == ACPI_RECLAIMABLE || entry->type == USABLE) {
      			uint64_t start = (uint64_t)
      				ptov (APPEND_HILO (entry->mem_hi, entry->mem_lo));
      			uint64_t size = APPEND_HILO (entry->len_hi, entry->len_lo);
      			uint64_t end = start + size;
      
      			// TODO: add 0x1000 ~ 0x200000, This is not a matter for now.
      			// All the pages are unuable
      			if (end < usable_bound)
      				continue;
      
      			start = (uint64_t)
      				pg_round_up (start >= usable_bound ? start : usable_bound);
      split:
      			if (page_from_pool (&kernel_pool, (void *) start))
      				pool = &kernel_pool;
      			else if (page_from_pool (&user_pool, (void *) start))
      				pool = &user_pool;
      			else
      				NOT_REACHED ();
      
      			pool_end = pool->base + bitmap_size (pool->used_map) * PGSIZE;
      			page_idx = pg_no (start) - pg_no (pool->base);
      			if ((uint64_t) pool_end < end) {
      				page_cnt = ((uint64_t) pool_end - start) / PGSIZE;
      				bitmap_set_multiple (pool->used_map, page_idx, page_cnt, false);
      				start = (uint64_t) pool_end;
      				goto split;
      			} else {
      				page_cnt = ((uint64_t) end - start) / PGSIZE;
      				bitmap_set_multiple (pool->used_map, page_idx, page_cnt, false);
      			}
      		}
      	}
      }
      • init_pool()
        /* Initializes pool P as starting at START and ending at END 
         * start 부터 end 까지의 pool P를 초기화*/
        static void
        init_pool (struct pool *p, void **bm_base, uint64_t start, uint64_t end) {
          /* We'll put the pool's used_map at its base.
             Calculate the space needed for the bitmap
             and subtract it from the pool's size. */
          /* **해석필요**... */
        	uint64_t pgcnt = (end - start) / PGSIZE;
        	size_t bm_pages = DIV_ROUND_UP (bitmap_buf_size (pgcnt), PGSIZE) * PGSIZE;
        
        	lock_init(&p->lock);
        	p->used_map = bitmap_create_in_buf (pgcnt, *bm_base, bm_pages);
        	p->base = (void *) start;
        
        	// Mark all to unusable.
        	bitmap_set_all(p->used_map, true);
        
        	*bm_base += bm_pages;
        }
  • malloc_init()
    /* Initializes the malloc() descriptors. */
    void
    malloc_init (void) {
    	size_t block_size;
    
    	for (block_size = 16; block_size < PGSIZE / 2; block_size *= 2) {
    		struct desc *d = &descs[desc_cnt++];
    		ASSERT (desc_cnt <= sizeof descs / sizeof *descs);
    		d->block_size = block_size;
    		d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size;
    		list_init (&d->free_list);
    		lock_init (&d->lock);
    	}
    }
  • paging_init()
    /* Populates the page table with the kernel virtual mapping,
     * and then sets up the CPU to use the new page directory.
     * Points base_pml4 to the pml4 it creates.
     * 페이지 테이블을 커널 가상 매핑으로 채우고, 
     * 새 페이지 디렉터리를 사용하도록 cpu 세팅
     * base_pml4는 생성되는 pml4를 가리킴??(해석필요) */
    static void
    paging_init (uint64_t mem_end) {
    	uint64_t *pml4, *pte;
    	int perm;
    	pml4 = base_pml4 = palloc_get_page (PAL_ASSERT | PAL_ZERO);
    	// free page 하나를 할당받고 가상주소를 반환
    	extern char start, _end_kernel_text;
    	// Maps physical address [0 ~ mem_end] to
    	//   [LOADER_KERN_BASE ~ LOADER_KERN_BASE + mem_end].
    	// 물리주소를 가상주소로 변환
    	// LOADER_KERN_BASE : 커널의 가상주소 0이자 물리주소 0
    	for (uint64_t pa = 0; pa < mem_end; pa += PGSIZE) {
    		uint64_t va = (uint64_t) ptov(pa);
    
    		perm = PTE_P | PTE_W;
    		if ((uint64_t) &start <= va && va < (uint64_t) &_end_kernel_text)
    			perm &= ~PTE_W;
    
    		if ((pte = pml4e_walk (pml4, va, 1)) != NULL)
    			*pte = pa | perm;
    	}
    
    	// reload cr3
    	pml4_activate(0);		// 페이지 디렉토리 pd를 cpu의 page directory base register에 저장
    }
    • palloc_get_page() - palloc.c
      /* Obtains a single free page and returns its kernel virtual
         address.
         If PAL_USER is set, the page is obtained from the user pool,
         otherwise from the kernel pool.  If PAL_ZERO is set in FLAGS,
         then the page is filled with zeros.  If no pages are
         available, returns a null pointer, unless PAL_ASSERT is set in
         FLAGS, in which case the kernel panics. */
         /* 사용 가능한 단일 페이지를 가져오고 커널 가상 주소를 반환
      	PAL_USER가 설정된 경우 페이지는 User pool에서 가져오고 
      	그렇지 않은 경우 Kernel pool에서 가져옴
      	flags가 PAL_ZERO면 페이지는 0으로 채워짐
      	사용 가능한 페이지가 없으면 null 포인터를 반환할건데, 
      	PAL_ASERT가 설정되어 있으면 커널 패닉이, 아니면 그냥 NULL을 반환함 */
      void *
      palloc_get_page (enum palloc_flags flags) {
      	return palloc_get_multiple (flags, 1);
      }
      
      /*------------------------------------------------------*/
      
      /* Obtains and returns a group of PAGE_CNT contiguous free pages.
         If PAL_USER is set, the pages are obtained from the user pool,
         otherwise from the kernel pool.  If PAL_ZERO is set in FLAGS,
         then the pages are filled with zeros.  If too few pages are
         available, returns a null pointer, unless PAL_ASSERT is set in
         FLAGS, in which case the kernel panics. */
      /* page_cnt만큼 연속된 사용가능한 페이지를 가져와서 반환
      	 PAL_ZERO면 페이지들은 0으로 채워짐
      	사용 가능한 페이지가 없으면 null 포인터를 반환할건데, 
      	PAL_ASERT가 설정되어 있으면 커널 패닉이, 아니면 그냥 NULL을 반환함 */
      void *
      palloc_get_multiple (enum palloc_flags flags, size_t page_cnt) {
      	struct pool *pool = flags & PAL_USER ? &user_pool : &kernel_pool;
      
      	lock_acquire (&pool->lock);
      	size_t page_idx = **bitmap_scan_and_flip** (pool->used_map, 0, page_cnt, false);
      	lock_release (&pool->lock);
      	void *pages;
      
      	if (page_idx != BITMAP_ERROR)
      		**pages = pool->base + PGSIZE * page_idx;**
      	else
      		pages = NULL;
      
      	if (pages) {
      		if (flags & PAL_ZERO)
      			memset (pages, 0, PGSIZE * page_cnt);
      	} else {
      		if (flags & PAL_ASSERT)
      			PANIC ("palloc_get: out of pages");
      	}
      
      	return pages;
      }
      • bitmap_scan_and_flip() - bitmap.c
        /* Finds the first group of CNT consecutive bits in B at or after
           START that are all set to VALUE, flips them all to !VALUE,
           and returns the index of the first bit in the group.
           If there is no such group, returns BITMAP_ERROR.
           If CNT is zero, returns 0.
           Bits are set atomically, but testing bits is not atomic with
           setting them. */
        /* 비트맵 b에서 start이후에 모두 VALUE로 세팅된 cnt만큼 연속된 비트그룹을 찾고,
         * 모든 비트를 !VALUE로 만들고, 그룹의 첫번째 비트의 인덱스를 반환 
         * 비트는 원자적으로 설정되지만 테스트 비트는 원자적으로 설정되지 않음 */
        size_t
        bitmap_scan_and_flip (struct bitmap *b, size_t start, size_t cnt, bool value) {
        	size_t idx = bitmap_scan (b, start, cnt, value);
        	if (idx != BITMAP_ERROR)
        		bitmap_set_multiple (b, idx, cnt, !value);
        	return idx;
        }
        
        /*------------------------------------------*/
        /* Finds and returns the starting index of the first group of CNT
           consecutive bits in B at or after START that are all set to
           VALUE.
           If there is no such group, returns BITMAP_ERROR. */
        /* 비트맵 b에서 start이후에 모두 VALUE로 설정된 cnt만큼 연속된 비트그룹을 찾고,
         * 시작 인덱스를 반환
         * 없으면 BITMAP_ERROR를 반환 */
        size_t
        bitmap_scan (const struct bitmap *b, size_t start, size_t cnt, bool value) {
        	ASSERT (b != NULL);
        	ASSERT (start <= b->bit_cnt);
        
        	if (cnt <= b->bit_cnt) {
        		size_t last = b->bit_cnt - cnt;
        		size_t i;
        		for (i = start; i <= last; i++)
        			if (!bitmap_contains (b, i, cnt, !value))
        				return i;
        	}
        	return BITMAP_ERROR;
        }
        
        /*------------------------------------------*/
        /* Sets the CNT bits starting at START in B to VALUE. */
        /* 비트맵 b에서 start로 시작하는 cnt 비트를 value로 설정 */
        void
        bitmap_set_multiple (struct bitmap *b, size_t start, size_t cnt, bool value) {
        	size_t i;
        
        	ASSERT (b != NULL);
        	ASSERT (start <= b->bit_cnt);
        	ASSERT (start + cnt <= b->bit_cnt);
        
        	for (i = 0; i < cnt; i++)
        		bitmap_set (b, start + i, value);
        }
        • bitmap_contains()
          /* Returns true if any bits in B between START and START + CNT,
             exclusive, are set to VALUE, and false otherwise. */
          bool
          bitmap_contains (const struct bitmap *b, size_t start, size_t cnt, bool value) {
          	size_t i;
          
          	ASSERT (b != NULL);
          	ASSERT (start <= b->bit_cnt);
          	ASSERT (start + cnt <= b->bit_cnt);
          
          	for (i = 0; i < cnt; i++)
          		if (bitmap_test (b, start + i) == value)
          			return true;
          	return false;
          }
          
          /* Returns the value of the bit numbered IDX in B. */
          bool
          bitmap_test (const struct bitmap *b, size_t idx) {
          	ASSERT (b != NULL);
          	ASSERT (idx < b->bit_cnt);
          	return (b->bits[elem_idx (idx)] & bit_mask (idx)) != 0;
          }
          
          /* Returns the index of the element that contains the bit
             numbered BIT_IDX. */
          static inline size_t
          elem_idx (size_t bit_idx) {
          	return bit_idx / ELEM_BITS;
          }
          
          /* Returns an elem_type where only the bit corresponding to
             BIT_IDX is turned on. */
          static inline elem_type
          bit_mask (size_t bit_idx) {
          	return (elem_type) 1 << (bit_idx % ELEM_BITS);
          }
        • bitmap_set()
          /* Setting and testing single bits. */
          
          /* Atomically sets the bit numbered IDX in B to VALUE. */
          void
          bitmap_set (struct bitmap *b, size_t idx, bool value) {
          	ASSERT (b != NULL);
          	ASSERT (idx < b->bit_cnt);
          	if (value)
          		bitmap_mark (b, idx);
          	else
          		bitmap_reset (b, idx);
          }
          
          /* Atomically sets the bit numbered BIT_IDX in B to true. */
          void
          bitmap_mark (struct bitmap *b, size_t bit_idx) {
          	size_t idx = elem_idx (bit_idx);
          	elem_type mask = bit_mask (bit_idx);
          
          	/* This is equivalent to `b->bits[idx] |= mask' except that it
          	   is guaranteed to be atomic on a uniprocessor machine.  See
          	   the description of the OR instruction in [IA32-v2b]. */
          	asm ("lock orq %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc");
          }
          
          /* Atomically sets the bit numbered BIT_IDX in B to false. */
          void
          bitmap_reset (struct bitmap *b, size_t bit_idx) {
          	size_t idx = elem_idx (bit_idx);
          	elem_type mask = bit_mask (bit_idx);
          
          	/* This is equivalent to `b->bits[idx] &= ~mask' except that it
          	   is guaranteed to be atomic on a uniprocessor machine.  See
          	   the description of the AND instruction in [IA32-v2a]. */
          	asm ("lock andq %1, %0" : "=m" (b->bits[idx]) : "r" (~mask) : "cc");
          }
          
        • elem_idx(), bitmap_set()
          /* Returns the index of the element that contains the bit
             numbered BIT_IDX. */
          static inline size_t
          elem_idx (size_t bit_idx) {
          	return bit_idx / ELEM_BITS;
          }
          
          /* Returns an elem_type where only the bit corresponding to
             BIT_IDX is turned on. */
          static inline elem_type
          bit_mask (size_t bit_idx) {
          	return (elem_type) 1 << (bit_idx % ELEM_BITS);
          }
    • pml4e_walk() - palloc.c
      /* Returns the address of the page table entry for virtual
       * address VADDR in page map level 4, pml4.
       * If PML4E does not have a page table for VADDR, behavior depends
       * on CREATE.  If CREATE is true, then a new page table is
       * created and a pointer into it is returned.  Otherwise, a null
       * pointer is returned. */
      /* page map level 4 : pml4 에서
       * 가상주소 vaddr에 대한 페이지 테이블 항목의 주소를 반환
       * pml4에 vaddr에 대한 페이지 테이블이 없는 경우 동작은 create 변수에 따라 달라짐
       * create가 true이면 새 페이지 테이블이 작성되고, 해당 테이블로의 포인터가 반환
       * create가 false이면 null 포인터가 반환 */
      uint64_t *
      pml4e_walk (uint64_t *pml4e, const uint64_t va, int create) {
      	uint64_t *pte = NULL;
      	int idx = PML4 (va);
      	int allocated = 0;
      	if (pml4e) {
      		uint64_t *pdpe = (uint64_t *) pml4e[idx];
      		if (!((uint64_t) pdpe & PTE_P)) {
      			if (create) {
      				uint64_t *new_page = [palloc_get_page](https://www.notion.so/OS-Project-2-User-Programs-bb14ba07b63a4d72acd2a692f1f2a1e4) (PAL_ZERO);
      				if (new_page) {
      					pml4e[idx] = vtop (new_page) | PTE_U | PTE_W | PTE_P;
      					allocated = 1;
      				} else
      					return NULL;
      			} else
      				return NULL;
      		} 
      		pte = pdpe_walk (ptov (PTE_ADDR (pml4e[idx])), va, create);
      	}
      	if (pte == NULL && allocated) {
      		palloc_free_page ((void *) ptov (PTE_ADDR (pml4e[idx])));
      		pml4e[idx] = 0;
      	}
      	return pte;
      }
      
      /****************************************/
      #define PML4(la)  ((((uint64_t) (la)) >> PML4SHIFT) & 0x1FF)
      
      • pte.h
        #ifndef THREADS_PTE_H
        #define THREADS_PTE_H
        
        #include "threads/vaddr.h"
        
        /* Functions and macros for working with x86 hardware page tables.
         * See vaddr.h for more generic functions and macros for virtual addresses.
         *
         * Virtual addresses are structured as follows:
         *  63          48 47            39 38            30 29            21 20         12 11         0
         * +-------------+----------------+----------------+----------------+-------------+------------+
         * | Sign Extend |    Page-Map    | Page-Directory | Page-directory |  Page-Table |  Physical  |
         * |             | Level-4 Offset |    Pointer     |     Offset     |   Offset    |   Offset   |
         * +-------------+----------------+----------------+----------------+-------------+------------+
         *               |                |                |                |             |            |
         *               +------- 9 ------+------- 9 ------+------- 9 ------+----- 9 -----+---- 12 ----+
         *                                         Virtual Address
         */
        
        #define PML4SHIFT 39UL
        #define PDPESHIFT 30UL
        #define PDXSHIFT  21UL
        #define PTXSHIFT  12UL
        
        #define PML4(la)  ((((uint64_t) (la)) >> PML4SHIFT) & 0x1FF)
        #define PDPE(la) ((((uint64_t) (la)) >> PDPESHIFT) & 0x1FF)
        #define PDX(la)  ((((uint64_t) (la)) >> PDXSHIFT) & 0x1FF)
        #define PTX(la)  ((((uint64_t) (la)) >> PTXSHIFT) & 0x1FF)
        #define PTE_ADDR(pte) ((uint64_t) (pte) & ~0xFFF)
        
        /* The important flags are listed below.
           When a PDE or PTE is not "present", the other flags are
           ignored.
           A PDE or PTE that is initialized to 0 will be interpreted as
           "not present", which is just fine. */
        #define PTE_FLAGS 0x00000000000000fffUL    /* Flag bits. */
        #define PTE_ADDR_MASK  0xffffffffffffff000UL /* Address bits. */
        #define PTE_AVL   0x00000e00             /* Bits available for OS use. */
        #define PTE_P 0x1                        /* 1=present, 0=not present. */
        #define PTE_W 0x2                        /* 1=read/write, 0=read-only. */
        #define PTE_U 0x4                        /* 1=user/kernel, 0=kernel only. */
        #define PTE_A 0x20                       /* 1=accessed, 0=not acccessed. */
        #define PTE_D 0x40                       /* 1=dirty, 0=not dirty (PTEs only). */
        
        #endif /* threads/pte.h */
      • pdpe_walk()
        static uint64_t *
        pdpe_walk (uint64_t *pdpe, const uint64_t va, int create) {
        	uint64_t *pte = NULL;
        	int idx = PDPE (va);
        	int allocated = 0;
        	if (pdpe) {
        		uint64_t *pde = (uint64_t *) pdpe[idx];
        		if (!((uint64_t) pde & PTE_P)) {
        			if (create) {
        				uint64_t *new_page = palloc_get_page (PAL_ZERO);
        				if (new_page) {
        					pdpe[idx] = vtop (new_page) | PTE_U | PTE_W | PTE_P;
        					allocated = 1;
        				} else
        					return NULL;
        			} else
        				return NULL;
        		}
        		pte = pgdir_walk (ptov (PTE_ADDR (pdpe[idx])), va, create);
        	}
        	if (pte == NULL && allocated) {
        		palloc_free_page ((void *) ptov (PTE_ADDR (pdpe[idx])));
        		pdpe[idx] = 0;
        	}
        	return pte;
        }
        • pgdir_walk()
          static uint64_t *
          pgdir_walk (uint64_t *pdp, const uint64_t va, int create) {
          	int idx = PDX (va);
          	if (pdp) {
          		uint64_t *pte = (uint64_t *) pdp[idx];
          		if (!((uint64_t) pte & PTE_P)) {
          			if (create) {
          				uint64_t *new_page = palloc_get_page (PAL_ZERO);
          				if (new_page)
          					pdp[idx] = vtop (new_page) | PTE_U | PTE_W | PTE_P;
          				else
          					return NULL;
          			} else
          				return NULL;
          		}
          		return (uint64_t *) ptov (PTE_ADDR (pdp[idx]) + 8 * PTX (va));
          	}
          	return NULL;
          }
      • votp(), ptov()
        /* Returns physical address at which kernel virtual address VADDR
         * is mapped. */
        #define vtop(vaddr) \
        ({ \
        	ASSERT(is_kernel_vaddr(vaddr)); \
        	((uint64_t) (vaddr) - (uint64_t) KERN_BASE);\
        })
        
        /* Returns kernel virtual address at which physical address PADDR
         *  is mapped. */
        #define ptov(paddr) ((void *) (((uint64_t) paddr) + KERN_BASE))
      • PTE_ADDR()
        #define PTE_ADDR(pte) ((uint64_t) (pte) & ~0xFFF)
      • palloc_free_page()
        /* Frees the page at PAGE. */
        void
        palloc_free_page (void *page) {
        	palloc_free_multiple (page, 1);
        }
        • palloc_free_multiple()
          /* Frees the PAGE_CNT pages starting at PAGES. */
          void
          palloc_free_multiple (void *pages, size_t page_cnt) {
          	struct pool *pool;
          	size_t page_idx;
          
          	ASSERT (pg_ofs (pages) == 0);
          	if (pages == NULL || page_cnt == 0)
          		return;
          
          	if (page_from_pool (&kernel_pool, pages))
          		pool = &kernel_pool;
          	else if (page_from_pool (&user_pool, pages))
          		pool = &user_pool;
          	else
          		NOT_REACHED ();
          
          	page_idx = pg_no (pages) - pg_no (pool->base);
          
          #ifndef NDEBUG
          	memset (pages, 0xcc, PGSIZE * page_cnt);
          #endif
          	ASSERT (bitmap_all (pool->used_map, page_idx, page_cnt));
          	bitmap_set_multiple (pool->used_map, page_idx, page_cnt, false);
          }
    • pml4_activate() - mmu.c
      /* Loads page directory PD into the CPU's page directory base
       * register. */
      // 페이지 디렉토리 pd를 cpu의 page directory base register에 저장
      void
      pml4_activate (uint64_t *pml4) {
      	lcr3 (vtop (pml4 ? pml4 : base_pml4));
      }
      
      /*-------------------------*/
      /* intrinsic.h*/
      /* Store the physical address of the page directory into CR3
         aka PDBR (page directory base register).  This activates our
         new page tables immediately.  See [IA32-v2a] "MOV--Move
         to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
         of the Page Directory". */
      __attribute__((always_inline))
      static __inline void lcr3(uint64_t val) {
      	__asm __volatile("movq %0, %%cr3" : : "r" (val));
      }
      
      /* vaddr.h */
      /* Returns physical address at which kernel virtual address VADDR
       * is mapped. */
      #define vtop(vaddr) \
      ({ \
      	ASSERT(is_kernel_vaddr(vaddr)); \
      	((uint64_t) (vaddr) - (uint64_t) KERN_BASE);\
      })
      // #define vtop(vaddr) ({ ASSERT(is_kernel_vaddr(vaddr)); ((uint64_t) (vaddr) - (uint64_t) KERN_BASE);})
      
      /* loader.h */
      /* Physical address of kernel base. */
      #define LOADER_KERN_BASE 0x8004000000
      
  • tss_init() - tss.c
    /* Initializes the kernel TSS. */
    
    void tss_init (void) {
    	/* Our TSS is never used in a call gate or task gate, so only a
    	 * few fields of it are ever referenced, and those are the only
    	 * ones we initialize.
    	 * tss는 call gate나 task gate에서 절대 사용되지 않기 때문에
    	 * tss의 몇가지 필드만 참조되며, 우리가 초기화하는 유일한 필드(..? **해석필요**) */
    	// call gate, task gate에서는 안씀
    	// 32비트까지는 유용했는데 64비트로 오면서 안쓰지만 커널 스택포인터 찾는데만 쓴다
    	// tss의 멤버변수중에서 쓰는것만 초기화시킴
    	tss = palloc_get_page (PAL_ASSERT | PAL_ZERO);	// 페이지 없으면 패닉, 있으면 0으로 초기화
    	tss_update (thread_current ());                 // tss->rsp0 업데이트
    }
    
    /* Sets the ring 0 stack pointer in the TSS to point to the end
     * of the thread stack. */
    
    void tss_update (struct thread *next) {      // cpu 점유할때마다 업데이트
    	ASSERT (tss != NULL);
    	tss->rsp0 = (uint64_t) next + PGSIZE;	// **스레드위치 + page사이즈 -> rsp??**
    }
  • gdt_init() - gdt.c
    /* Sets up a proper GDT.  The bootstrap loader's GDT didn't
       include user-mode selectors or a TSS, but we need both now. */
    void
    gdt_init (void) {
    	/* Initialize GDT. */
    	struct segment_descriptor64 *tss_desc =
    		(struct segment_descriptor64 *) &gdt[SEL_TSS >> 3];
    	struct task_state *tss = tss_get ();
    
    	*tss_desc = (struct segment_descriptor64) {
    		.lim_15_0 = (uint64_t) (sizeof (struct task_state)) & 0xffff,
    		.base_15_0 = (uint64_t) (tss) & 0xffff,
    		.base_23_16 = ((uint64_t) (tss) >> 16) & 0xff,
    		.type = 0x9,
    		.s = 0,
    		.dpl = 0,
    		.p = 1,
    		.lim_19_16 = ((uint64_t)(sizeof (struct task_state)) >> 16) & 0xf,
    		.avl = 0,
    		.rsv1 = 0,
    		.g = 0,
    		.base_31_24 = ((uint64_t)(tss) >> 24) & 0xff,
    		.base_63_32 = ((uint64_t)(tss) >> 32) & 0xffffffff,
    		.res1 = 0,
    		.clear = 0,
    		.res2 = 0
    	};
    
    	lgdt (&gdt_ds);
    	/* reload segment registers */
    	asm volatile("movw %%ax, %%gs" :: "a" (SEL_UDSEG));
    	asm volatile("movw %%ax, %%fs" :: "a" (0));
    	asm volatile("movw %%ax, %%es" :: "a" (SEL_KDSEG));
    	asm volatile("movw %%ax, %%ds" :: "a" (SEL_KDSEG));
    	asm volatile("movw %%ax, %%ss" :: "a" (SEL_KDSEG));
    	asm volatile("pushq %%rbx\n"
    			"movabs $1f, %%rax\n"
    			"pushq %%rax\n"
    			"lretq\n"
    			"1:\n" :: "b" (SEL_KCSEG):"cc","memory");
    	/* Kill the local descriptor table */
    	lldt (0);
    }
  • intr_init() - interrupt.c
    /* Initializes the interrupt system. */
    void
    intr_init (void) {
    	int i;
    
    	/* Initialize interrupt controller. */
    	pic_init ();
    
    	/* Initialize IDT. */
    	for (i = 0; i < INTR_CNT; i++) {
    		make_intr_gate(&idt[i], intr_stubs[i], 0);
    		intr_names[i] = "unknown";
    	}
    
    #ifdef USERPROG
    	/* Load TSS. */
    	ltr (SEL_TSS);
    #endif
    
    	/* Load IDT register. */
    	lidt(&idt_desc);
    
    	/* Initialize intr_names. */
    	intr_names[0] = "#DE Divide Error";
    	intr_names[1] = "#DB Debug Exception";
    	intr_names[2] = "NMI Interrupt";
    	intr_names[3] = "#BP Breakpoint Exception";
    	intr_names[4] = "#OF Overflow Exception";
    	intr_names[5] = "#BR BOUND Range Exceeded Exception";
    	intr_names[6] = "#UD Invalid Opcode Exception";
    	intr_names[7] = "#NM Device Not Available Exception";
    	intr_names[8] = "#DF Double Fault Exception";
    	intr_names[9] = "Coprocessor Segment Overrun";
    	intr_names[10] = "#TS Invalid TSS Exception";
    	intr_names[11] = "#NP Segment Not Present";
    	intr_names[12] = "#SS Stack Fault Exception";
    	intr_names[13] = "#GP General Protection Exception";
    	intr_names[14] = "#PF Page-Fault Exception";
    	intr_names[16] = "#MF x87 FPU Floating-Point Error";
    	intr_names[17] = "#AC Alignment Check Exception";
    	intr_names[18] = "#MC Machine-Check Exception";
    	intr_names[19] = "#XF SIMD Floating-Point Exception";
    }
  • timer_init() - timer.c
    /* Sets up the 8254 Programmable Interval Timer (PIT) to
       interrupt PIT_FREQ times per second, and registers the
       corresponding interrupt. */
    void timer_init(void)
    {
    	/* 8254 input frequency divided by TIMER_FREQ, rounded to
    	   nearest. */
    	uint16_t count = (1193180 + TIMER_FREQ / 2) / TIMER_FREQ;
    
    	outb(0x43, 0x34); /* CW: counter 0, LSB then MSB, mode 2, binary. */
    	outb(0x40, count & 0xff);
    	outb(0x40, count >> 8);
    
    	intr_register_ext(0x20, timer_interrupt, "8254 Timer");
    }
    • intr_register_ext()
      /* Registers external interrupt VEC_NO to invoke HANDLER, which
         is named NAME for debugging purposes.  The handler will
         execute with interrupts disabled. */
      void
      intr_register_ext (uint8_t vec_no, intr_handler_func *handler,
      		const char *name) {
      	ASSERT (vec_no >= 0x20 && vec_no <= 0x2f);
      	register_handler (vec_no, 0, INTR_OFF, handler, name);
      }
  • kbd_init() - kbd.c
    /* Initializes the keyboard. */
    void
    kbd_init (void) {
    	intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard");
    }
  • input_init() - input.c
    /* Initializes the input buffer. */
    void
    input_init (void) {
    	intq_init (&buffer);
    }
    
    /* intq.c */
    
    /* Initializes interrupt queue Q. */
    void
    intq_init (struct intq *q) {
    	lock_init (&q->lock);
    	q->not_full = q->not_empty = NULL;
    	q->head = q->tail = 0;
    }
  • exeception_init()
    /* Registers handlers for interrupts that can be caused by user
       programs.
    
       In a real Unix-like OS, most of these interrupts would be
       passed along to the user process in the form of signals, as
       described in [SV-386] 3-24 and 3-25, but we don't implement
       signals.  Instead, we'll make them simply kill the user
       process.
    
       Page faults are an exception.  Here they are treated the same
       way as other exceptions, but this will need to change to
       implement virtual memory.
    
       Refer to [IA32-v3a] section 5.15 "Exception and Interrupt
       Reference" for a description of each of these exceptions. */
    void
    exception_init (void) {
    	/* These exceptions can be raised explicitly by a user program,
    	   e.g. via the INT, INT3, INTO, and BOUND instructions.  Thus,
    	   we set DPL==3, meaning that user programs are allowed to
    	   invoke them via these instructions. */
    	intr_register_int (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
    	intr_register_int (4, 3, INTR_ON, kill, "#OF Overflow Exception");
    	intr_register_int (5, 3, INTR_ON, kill,
    			"#BR BOUND Range Exceeded Exception");
    
    	/* These exceptions have DPL==0, preventing user processes from
    	   invoking them via the INT instruction.  They can still be
    	   caused indirectly, e.g. #DE can be caused by dividing by
    	   0.  */
    	intr_register_int (0, 0, INTR_ON, kill, "#DE Divide Error");
    	intr_register_int (1, 0, INTR_ON, kill, "#DB Debug Exception");
    	intr_register_int (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
    	intr_register_int (7, 0, INTR_ON, kill,
    			"#NM Device Not Available Exception");
    	intr_register_int (11, 0, INTR_ON, kill, "#NP Segment Not Present");
    	intr_register_int (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
    	intr_register_int (13, 0, INTR_ON, kill, "#GP General Protection Exception");
    	intr_register_int (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
    	intr_register_int (19, 0, INTR_ON, kill,
    			"#XF SIMD Floating-Point Exception");
    
    	/* Most exceptions can be handled with interrupts turned on.
    	   We need to disable interrupts for page faults because the
    	   fault address is stored in CR2 and needs to be preserved. */
    	intr_register_int (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception");
    }
    • intr_register_ext() - interrupt.c
      /* Registers internal interrupt VEC_NO to invoke HANDLER, which
         is named NAME for debugging purposes.  The interrupt handler
         will be invoked with interrupt status LEVEL.
      
         The handler will have descriptor privilege level DPL, meaning
         that it can be invoked intentionally when the processor is in
         the DPL or lower-numbered ring.  In practice, DPL==3 allows
         user mode to invoke the interrupts and DPL==0 prevents such
         invocation.  Faults and exceptions that occur in user mode
         still cause interrupts with DPL==0 to be invoked.  See
         [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1
         "Accessing Nonconforming Code Segments" for further
         discussion. */
      void
      intr_register_int (uint8_t vec_no, int dpl, enum intr_level level,
      		intr_handler_func *handler, const char *name)
      {
      	ASSERT (vec_no < 0x20 || vec_no > 0x2f);
      	register_handler (vec_no, dpl, level, handler, name);
      }
  • syscall_init() - syscall.c (in userprog)
    void syscall_init(void)
    {
    	write_msr(MSR_STAR, ((uint64_t)SEL_UCSEG - 0x10) << 48 |
    							((uint64_t)SEL_KCSEG) << 32);
    	write_msr(MSR_LSTAR, (uint64_t)syscall_entry);
    
    	/* The interrupt service rountine should not serve any interrupts
    	 * until the syscall_entry swaps the userland stack to the kernel
    	 * mode stack. Therefore, we masked the FLAG_FL. */
    	write_msr(MSR_SYSCALL_MASK,
    			  FLAG_IF | FLAG_TF | FLAG_DF | FLAG_IOPL | FLAG_AC | FLAG_NT);
    }
  • thread_start() - thread.c
    /* Starts preemptive thread scheduling by enabling interrupts.
       Also creates the idle thread. */
    void
    thread_start (void) {
    	/* Create the idle thread. */
    	struct semaphore idle_started;
    	sema_init (&idle_started, 0);
    	thread_create ("idle", PRI_MIN, idle, &idle_started);
    
    	/* Start preemptive thread scheduling. */
    	intr_enable ();
    
    	/* Wait for the idle thread to initialize idle_thread. */
    	sema_down (&idle_started);
    }
  • serial_init_queue() - serial.c
    /* Initializes the serial port device for queued interrupt-driven
       I/O.  With interrupt-driven I/O we don't waste CPU time
       waiting for the serial device to become ready. */
    void
    serial_init_queue (void) {
    	enum intr_level old_level;
    
    	if (mode == UNINIT)
    		init_poll ();
    	ASSERT (mode == POLL);
    
    	intr_register_ext (0x20 + 4, serial_interrupt, "serial");
    	mode = QUEUE;
    	old_level = intr_disable ();
    	write_ier ();
    	intr_set_level (old_level);
    }
  • timer_calibrate - timer.c
    /* Calibrates loops_per_tick, used to implement brief delays. */
    void timer_calibrate(void)
    {
    	unsigned high_bit, test_bit;
    
    	ASSERT(intr_get_level() == INTR_ON);
    	printf("Calibrating timer...  ");
    
    	/* Approximate loops_per_tick as the largest power-of-two
    	   still less than one timer tick. */
    	loops_per_tick = 1u << 10;
    	while (!too_many_loops(loops_per_tick << 1))
    	{
    		loops_per_tick <<= 1;
    		ASSERT(loops_per_tick != 0);
    	}
    
    	/* Refine the next 8 bits of loops_per_tick. */
    	high_bit = loops_per_tick;
    	for (test_bit = high_bit >> 1; test_bit != high_bit >> 10; test_bit >>= 1)
    		if (!too_many_loops(high_bit | test_bit))
    			loops_per_tick |= test_bit;
    
    	printf("%'" PRIu64 " loops/s.\n", (uint64_t)loops_per_tick * TIMER_FREQ);
    }
  • disk_init() - disk.c
    /* Initialize the disk subsystem and detect disks. */
    void
    disk_init (void) {
    	size_t chan_no;
    
    	for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++) {
    		struct channel *c = &channels[chan_no];
    		int dev_no;
    
    		/* Initialize channel. */
    		snprintf (c->name, sizeof c->name, "hd%zu", chan_no);
    		switch (chan_no) {
    			case 0:
    				c->reg_base = 0x1f0;
    				c->irq = 14 + 0x20;
    				break;
    			case 1:
    				c->reg_base = 0x170;
    				c->irq = 15 + 0x20;
    				break;
    			default:
    				NOT_REACHED ();
    		}
    		lock_init (&c->lock);
    		c->expecting_interrupt = false;
    		sema_init (&c->completion_wait, 0);
    
    		/* Initialize devices. */
    		for (dev_no = 0; dev_no < 2; dev_no++) {
    			struct disk *d = &c->devices[dev_no];
    			snprintf (d->name, sizeof d->name, "%s:%d", c->name, dev_no);
    			d->channel = c;
    			d->dev_no = dev_no;
    
    			d->is_ata = false;
    			d->capacity = 0;
    
    			d->read_cnt = d->write_cnt = 0;
    		}
    
    		/* Register interrupt handler. */
    		intr_register_ext (c->irq, interrupt_handler, c->name);
    
    		/* Reset hardware. */
    		reset_channel (c);
    
    		/* Distinguish ATA hard disks from other devices. */
    		if (check_device_type (&c->devices[0]))
    			check_device_type (&c->devices[1]);
    
    		/* Read hard disk identity information. */
    		for (dev_no = 0; dev_no < 2; dev_no++)
    			if (c->devices[dev_no].is_ata)
    				identify_ata_device (&c->devices[dev_no]);
    	}
    
    	/* DO NOT MODIFY BELOW LINES. */
    	register_disk_inspect_intr ();
    }
  • filesys_init() - filesys.c
    /* Initializes the file system module.
     * If FORMAT is true, reformats the file system. */
    void
    filesys_init (bool format) {
    	filesys_disk = disk_get (0, 1);
    	if (filesys_disk == NULL)
    		PANIC ("hd0:1 (hdb) not present, file system initialization failed");
    
    	inode_init ();
    
    #ifdef EFILESYS
    	fat_init ();
    
    	if (format)
    		do_format ();
    
    	fat_open ();
    #else
    	/* Original FS */
    	free_map_init ();
    
    	if (format)
    		do_format ();
    
    	free_map_open ();
    #endif
    }
  • vm_init() - vm.c
    /* Initializes the virtual memory subsystem by invoking each subsystem's
     * intialize codes. */
    void
    vm_init (void) {
    	vm_anon_init ();
    	vm_file_init ();
    #ifdef EFILESYS  /* For project 4 */
    	pagecache_init ();
    #endif
    	register_inspect_intr ();
    	/* DO NOT MODIFY UPPER LINES. */
    	/* TODO: Your code goes here. */
    }
    
    /* Get the type of the page. This function is useful if you want to know the
     * type of the page after it will be initialized.
     * This function is fully implemented now. */
    enum vm_type
    page_get_type (struct page *page) {
    	int ty = VM_TYPE (page->operations->type);
    	switch (ty) {
    		case VM_UNINIT:
    			return VM_TYPE (page->uninit.type);
    		default:
    			return ty;
    	}
    }
  • run_action() run_action()
  • power_off()
    /* Powers down the machine we're running on,
       as long as we're running on Bochs or QEMU. */
    void
    power_off (void) {
    #ifdef FILESYS
    	filesys_done ();
    #endif
    
    	print_stats ();
    
    	printf ("Powering off...\n");
    	outw (0x604, 0x2000);               /* Poweroff command for qemu */
    	for (;;);
    }
  • thread_exit()
    /* Deschedules the current thread and destroys it.  Never
       returns to the caller. */
    void
    thread_exit (void) {
    	ASSERT (!intr_context ());
    
    #ifdef USERPROG
    	process_exit ();
    #endif
    
    	/* Just set our status to dying and schedule another process.
    	   We will be destroyed during the call to schedule_tail(). */
    	intr_disable ();
    	do_schedule (THREAD_DYING);
    	NOT_REACHED ();
    }

run_action()

/* Executes all of the actions specified in ARGV[]
   up to the null pointer sentinel. */
static void
run_actions (char **argv) {			      // argv : run 'args-single oneargs'
	/* 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**},		    // name : run, argc : 2, function : run_task 인 action 구조체
#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++)				      // actions 배열을 돌면서
			if (a->name == NULL)				        // 이름이 NULL이면 PANIC
				PANIC ("unknown action `%s' (use -h for help)", *argv);
			else if (!strcmp (*argv, a->name))	// argv(지금은 run)와 이름이 같으면 break
				break;

		/* Check for required arguments. */
		for (i = 1; i < a->argc; i++)			    // argc만큼 돌면서 값이 제대로 들어있는지 확인
			if (argv[i] == NULL)
				PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);

		/* Invoke action and advance. */
		a->function (argv);						        // run_task(argv) 호출
		argv += a->argc;
	}

}

run_task()

/* Runs the task specified in ARGV[1]. */
static void
run_task (char **argv) {
	const char *task = argv[1];		// [0]:run [1]:'args-single onearg\n'

	printf ("Executing '%s':\n", task);
#ifdef USERPROG
	if (thread_tests){
		run_test (task);
	} else {
		**process_wait** (**process_create_initd** (task));	// proc ~ initd()의 return값을 인자로 wait()호출
	}
#else
	run_test (task);
#endif
	printf ("Execution of '%s' complete.\n", task);
}

process_create_initd() - process.c

/* Starts the first userland program, called "initd", loaded from FILE_NAME.
 * The new thread may be scheduled (and may even exit)
 * before process_create_initd() returns. Returns the initd's
 * thread id, or TID_ERROR if the thread cannot be created.
 * Notice that THIS SHOULD BE CALLED ONCE. */
tid_t process_create_initd(const char *file_name)	// 'args-single onearg'
{
	char *fn_copy; 					
	tid_t tid;

	/* Make a copy of FILE_NAME.
	 * Otherwise there's a race between the caller and load(). */
	fn_copy = palloc_get_page(0);			// file_name을 저장하기 위한 할당
	if (fn_copy == NULL)
		return TID_ERROR;
	strlcpy(fn_copy, file_name, PGSIZE);	// file_name을 fn_copy에 PGSIZE만큼 복사

	/* strtok_r(file_name, delimiters, ptr)
	 * 문자열을 delimiter 단위로 자름
	 * strtok_r() 함수는 thread safe를 보장
	 */
	char *next_ptr;
	strtok_r(file_name, " ", &next_ptr);

	/* Create a new thread to execute FILE_NAME. */
	tid = **thread_create**(file_name, PRI_DEFAULT, **initd**, fn_copy); 
	// 스레드 생성 후 initd(fn_copy) 실행

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

thread_create() - thread.c

tid_t
thread_create (const char *name, int priority, thread_func *function, void *aux) {
	struct thread *t;
	tid_t tid;

	ASSERT (function != NULL);

	/* Allocate thread. */
	t = palloc_get_page (PAL_ZERO);
	if (t == NULL)
		return TID_ERROR;

	/* Initialize thread. */
	init_thread (t, name, priority);
	tid = t->tid = allocate_tid ();

	/* project2 - user programs */

	/* 현재 스레드의 자식 리스트에 새로 생성한 스레드 추가 */
	struct thread *curr = thread_current();
	list_push_back(&curr->child_list, &t->child_elem);

	/* 파일 디스크립터 초기화 */
	t->fdTable = palloc_get_multiple(PAL_ZERO, FDT_PAGES);
	if (t->fdTable == NULL)
		return TID_ERROR;

	/* 추가로 fd table의 표준입력과 표준출력에 더미의 값을 넣음
	 * 이는 read, write, close, dup2 시스템 콜을 사용할 때, 
	 * 표준 입력, 표준 출력을 구분하기 위한 장치 -> 임의의 값 1, 2 */
	t->fdIdx = 2; // 2부터 File Descriptor 값 할당
	t->fdTable[0] = 1; // STDIN
	t->fdTable[1] = 2; // STDOUT

	t->stdin_count = 1;
	t->stdout_count = 1;
	/* ---------------------project2--------------------*/

	/* Call the kernel_thread if it scheduled.
	 * Note) rdi is 1st argument, and rsi is 2nd argument. */
	t->tf.rip = (uintptr_t)**kernel_thread**;
	t->tf.R.rdi = (uint64_t)function;
	t->tf.R.rsi = (uint64_t)aux;
	t->tf.ds = SEL_KDSEG;
	t->tf.es = SEL_KDSEG;
	t->tf.ss = SEL_KDSEG;
	t->tf.cs = SEL_KCSEG;
	t->tf.eflags = FLAG_IF;

	/* Add to run queue. */
	thread_unblock(t);

	/* project1 */ 
	/* ready_list의 첫번째 스레드의 priority가 더 높으면 yield()하고 선점 */
	test_max_priority();

	return tid;
}
  • init_thread()
    /* Does basic initialization of T as a blocked thread named
       NAME. */
    static void
    init_thread(struct thread *t, const char *name, int priority)
    {
    	ASSERT(t != NULL);
    	ASSERT(PRI_MIN <= priority && priority <= PRI_MAX);
    	ASSERT(name != NULL);
    
    	memset(t, 0, sizeof *t);
    	t->status = THREAD_BLOCKED;
    	strlcpy(t->name, name, sizeof t->name);
    	t->tf.rsp = (uint64_t)t + PGSIZE - sizeof(void *);
    	t->priority = priority;
    	t->magic = THREAD_MAGIC;
    
    	/* project - Priority Donation init */
    	t->init_priority = priority;
    	list_init(&t->donations);
    
    	/* project - advanced scheduler */
    	if (thread_mlfqs)
    	{
    		t->nice = NICE_DEFAULT;
    		t->recent_cpu = RECENT_CPU_DEFAULT;
    	}
    	/* 자식 리스트 및 세마포어 초기화 */
    	/* project - user programs */
    	list_push_back(&all_list, &t->allelem);
    	list_init(&t->child_list);
    	sema_init(&t->wait_sema, 0);
    	sema_init(&t->fork_sema, 0);
    	sema_init(&t->free_sema, 0);
    
    	t->running = NULL;
    }
  • kernel_thread()
    /* Function used as the basis for a kernel thread. */
    static void
    kernel_thread (thread_func *function, void *aux) {
    	ASSERT (function != NULL);
    
    	intr_enable ();       /* The scheduler runs with interrupts off. */
    	**function (aux);**       /* Execute the thread function. */
    	thread_exit ();       /* If function() returns, kill the thread. */
    }
    wrapper 함수 thread_exit을 보장함 스택에서도 맨위에 먼저 쌓임

initd() - process.c

static void
initd(void *f_name)
{
// printf("\n##### debuging ##### start initd \n f_name : %s \n\n", 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()
    /* General process initializer for initd and other process. */
    static void
    process_init(void)
    {
    	struct thread *current = thread_current();
    }

process_exec() - process.c

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. */

	/* SEL_UDSEG : user data segment  SEL_UCSEG : useo code segment */
	struct intr_frame _if;
	_if.cs = SEL_UCSEG;					           // code selector
	_if.ds = _if.es = _if.ss = SEL_UDSEG;  // data selector
	_if.eflags = FLAG_IF | FLAG_MBS;	     // Interrupt Flag, Parity Flag 

	/* We first kill the current context */
	process_cleanup();                // current page의 pml4 초기화 // **더 찾아보기**

	/* To Do : Argument passing parsing file name and arguments */
	char *parse[128]; 

	char *next_ptr;
	char *token = strtok_r(file_name, " ", &next_ptr);

	int cnt = 0;
	for (; token != NULL; token = strtok_r(NULL, " ", &next_ptr))
	{
		parse[cnt] = token;
		cnt++;
	}

	/* And then load the binary */
	success = **load**(file_name, &_if);  // 성공 : 1, 실패 : 0

	/* If load failed, quit. */
	if (!success)
	{
		palloc_free_page(file_name);
		return -1;
	}
	

	**argument_stack**(parse, cnt, &_if.rsp);
	_if.R.rdi = cnt;
	_if.R.rsi = _if.rsp + WORD_ALIGN;
	
	// hex_dump(_if.rsp, _if.rsp, USER_STACK - _if.rsp, true);

	/* Start switched process. */
	**do_iret**(&_if);
	NOT_REACHED();
}
  • process_cleanup()
    /* Free the current process's resources. */
    static void
    process_cleanup(void)
    {
    	//    printf("\n##### debuging ##### start process_cleanup \n\n");
    	struct thread *curr = thread_current();
    
    #ifdef VM
    	supplemental_page_table_kill(&curr->spt);
    #endif
    
    	uint64_t *pml4;
    	/* Destroy the current process's page directory and switch back
    	 * to the kernel-only page directory. */
    	pml4 = curr->pml4;
    	if (pml4 != NULL)
    	{
    		/* Correct ordering here is crucial.  We must set
    		 * cur->pagedir to NULL before switching page directories,
    		 * so that a timer interrupt can't switch back to the
    		 * process page directory.  We must activate the base page
    		 * directory before destroying the process's page
    		 * directory, or our active page directory will be one
    		 * that's been freed (and cleared). */
    		curr->pml4 = NULL;
    		pml4_activate(NULL);	// NULL -> base_pml4
    		pml4_destroy(pml4);		// 페이지 관련 참조들을 반환함
    	}
    }
    • pml4_activate() - mmu.c
      /* Loads page directory PD into the CPU's page directory base
       * register. */
      // 페이지 디렉토리 pd를 cpu의 page directory base register에 저장
      void
      pml4_activate (uint64_t *pml4) {
      	lcr3 (vtop (pml4 ? pml4 : base_pml4));
      }
    • pml4_destroy() - mmu.c
      /* Destroys pml4e, freeing all the pages it references. */
      void
      pml4_destroy (uint64_t *pml4) {
      	if (pml4 == NULL)
      		return;
      	ASSERT (pml4 != base_pml4);
      
      	/* if PML4 (vaddr) >= 1, it's kernel space by define. */
      	uint64_t *pdpe = ptov ((uint64_t *) pml4[0]);
      	if (((uint64_t) pdpe) & PTE_P)
      		pdpe_destroy ((void *) PTE_ADDR (pdpe));
      	palloc_free_page ((void *) pml4);
      }

load()

static bool
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 = 0;

	/* 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;
	}

	/* thread 구조체의 run_file을 현재 실행 할 파일로 초기화 */
	t->running = file;

	/* 현재 오픈한 파일에 다른내용 쓰지 못하게 함 */
	file_deny_write(file);

	/* 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 || 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;

	success = true;

done:
	/* We arrive here whether the load is successful or not. */
	return success;
}
  • pml4_create() - mmu.c
    /* Creates a new page map level 4 (pml4) has mappings for kernel
     * virtual addresses, but none for user virtual addresses.
     * Returns the new page directory, or a null pointer if memory
     * allocation fails. */
    uint64_t *
    pml4_create (void) {
    	uint64_t *pml4 = palloc_get_page (0);	// 0이면 **flags & PAL_USER -> false -> kernel pool**
    	if (pml4)
    		memcpy (pml4, base_pml4, PGSIZE);   // pml4에 base_pml4를 PGSIZE만큼 복사
    	return pml4;
    }
  • process_activate() - process.c
    /* Sets up the CPU for running user code in the nest thread.
     * This function is called on every context switch. */
    void process_activate(struct thread *next)
    {
    	/* Activate thread's page tables. */
    	pml4_activate(next->pml4);
    
    	/* Set thread's kernel stack for use in processing interrupts. */
    	tss_update(next); // **tss->rsp0 = thread + PGSIZE(1<<12), 커널의 스택포인터 업데이트**
    }
    • pml4_activate() - mmu.c
      /* Loads page directory PD into the CPU's page directory base
       * register. */
      // 페이지 디렉토리 pd를 cpu의 page directory base register에 저장
      void
      pml4_activate (uint64_t *pml4) {
      	lcr3 (vtop (pml4 ? pml4 : base_pml4));
      }
    • tss_update() - tss.c
      /* Sets the ring 0 stack pointer in the TSS to point to the end
       * of the thread stack. */
      void
      tss_update (struct thread *next) {
      	ASSERT (tss != NULL);
      	tss->rsp0 = (uint64_t) next + PGSIZE;
      }
  • file 관련
    • filesys_open()
      /* Opens the file with the given NAME.
       * Returns the new file if successful or a null pointer
       * otherwise.
       * Fails if no file named NAME exists,
       * or if an internal memory allocation fails. */
      struct file *
      filesys_open(const char *name)
      {
      	struct dir *dir = dir_open_root(); // 루트 디렉토리 오픈
      	struct inode *inode = NULL;
      
      	if (dir != NULL)
      		dir_lookup(dir, name, &inode); // 해당 dir에 해당되는 name에 해당되는 inode에 저장
      	dir_close(dir);					   // 디렉토리는 쓰고 닫아야 함
      
      	return file_open(inode);		   // 해당 inode로 연 파일 객체를 리턴
      
      	/* inode란? 리눅스 시스템에서, 파일 시스템을 처리할 때, 리눅스 전용 특수한 인덱스를 아이노드라고 한다. 
      	 * 아이노드는 index-node의 줄임말이다. 이름에서 유추할 수 있듯이 무언가를 빠르게 찾기위한 노드이며, 
      	 * 그 무언가는 리눅스 시스템에서 파일을 의미한다. 즉, 아이노드는 리눅스의 모든 파일에 일종의 번호를 부여한다. 
      	 * 아이노드는 파일에 대한 정보(메타데이터)를 포함하고 있고, 인덱스 값도 가진 노드(데이터)이다. 
      	 */
      }
    • file_deny_write()
      /* Prevents write operations on FILE's underlying inode
       * until file_allow_write() is called or FILE is closed. */
      void file_deny_write(struct file *file)
      {
      	ASSERT(file != NULL);
      	if (!file->deny_write)
      	{
      		file->deny_write = true;
      		inode_deny_write(file->inode);
      	}
      }
    • file_read()
      /* Reads SIZE bytes from FILE into BUFFER,
       * starting at the file's current position.
       * Returns the number of bytes actually read,
       * which may be less than SIZE if end of file is reached.
       * Advances FILE's position by the number of bytes read. */
      // 파일의 현재 위치에서 시작하여 파일에서 버퍼로 크기 바이트를 읽음
      off_t file_read(struct file *file, void *buffer, off_t size)
      {
      	off_t bytes_read = inode_read_at(file->inode, buffer, size, file->pos);
      	file->pos += bytes_read;
      	return bytes_read;
      }
    • file_length()
      /* Returns the size of FILE in bytes. */
      off_t file_length(struct file *file)
      {
      	ASSERT(file != NULL);
      	return inode_length(file->inode);
      }
    • file_seek()
      /* Sets the current position in FILE to NEW_POS bytes from the
       * start of the file. */
      void file_seek(struct file *file, off_t new_pos)
      {
      	ASSERT(file != NULL);
      	ASSERT(new_pos >= 0);
      	file->pos = new_pos;
      }
    • validate_segment()
      /* Checks whether PHDR describes a valid, loadable segment in
       * FILE and returns true if so, false otherwise. */
      static bool
      validate_segment(const struct Phdr *phdr, struct file *file)
      {
      	/* p_offset and p_vaddr must have the same page offset. */
      	if ((phdr->p_offset & PGMASK) != (phdr->p_vaddr & PGMASK))
      		return false;
      
      	/* p_offset must point within FILE. */
      	if (phdr->p_offset > (uint64_t)file_length(file))
      		return false;
      
      	/* p_memsz must be at least as big as p_filesz. */
      	if (phdr->p_memsz < phdr->p_filesz)
      		return false;
      
      	/* The segment must not be empty. */
      	if (phdr->p_memsz == 0)
      		return false;
      
      	/* The virtual memory region must both start and end within the
      	   user address space range. */
      	if (!is_user_vaddr((void *)phdr->p_vaddr))
      		return false;
      	if (!is_user_vaddr((void *)(phdr->p_vaddr + phdr->p_memsz)))
      		return false;
      
      	/* The region cannot "wrap around" across the kernel virtual
      	   address space. */
      	if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr)
      		return false;
      
      	/* Disallow mapping page 0.
      	   Not only is it a bad idea to map page 0, but if we allowed
      	   it then user code that passed a null pointer to system calls
      	   could quite likely panic the kernel by way of null pointer
      	   assertions in memcpy(), etc. */
      	if (phdr->p_vaddr < PGSIZE)
      		return false;
      
      	/* It's okay. */
      	return true;
      }
    • load_segment()
      /* Loads a segment starting at offset OFS in FILE at address
       * UPAGE.  In total, READ_BYTES + ZERO_BYTES bytes of virtual
       * memory are initialized, as follows:
       *
       * - READ_BYTES bytes at UPAGE must be read from FILE
       * starting at offset OFS.
       *
       * - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed.
       *
       * The pages initialized by this function must be writable by the
       * user process if WRITABLE is true, read-only otherwise.
       *
       * Return true if successful, false if a memory allocation error
       * or disk read error occurs. */
      static bool
      load_segment(struct file *file, off_t ofs, uint8_t *upage,
      			 uint32_t read_bytes, uint32_t zero_bytes, bool writable)
      {
      	ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
      	ASSERT(pg_ofs(upage) == 0);
      	ASSERT(ofs % PGSIZE == 0);
      
      	file_seek(file, ofs);
      	while (read_bytes > 0 || zero_bytes > 0)
      	{
      		/* Do calculate how to fill this page.
      		 * We will read PAGE_READ_BYTES bytes from FILE
      		 * and zero the final PAGE_ZERO_BYTES bytes. */
      		size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
      		size_t page_zero_bytes = PGSIZE - page_read_bytes;
      
      		/* Get a page of memory. */
      		uint8_t *kpage = palloc_get_page(PAL_USER);
      		if (kpage == NULL)
      			return false;
      
      		/* Load this page. */
      		if (file_read(file, kpage, page_read_bytes) != (int)page_read_bytes)
      		{
      			palloc_free_page(kpage);
      			return false;
      		}
      		memset(kpage + page_read_bytes, 0, page_zero_bytes);
      
      		/* Add the page to the process's address space. */
      		if (!install_page(upage, kpage, writable))
      		{
      			printf("fail\n");
      			palloc_free_page(kpage);
      			return false;
      		}
      
      		/* Advance. */
      		read_bytes -= page_read_bytes;
      		zero_bytes -= page_zero_bytes;
      		upage += PGSIZE;
      	}
      	return true;
      }
  • setup_stack()
    /* Create a minimal stack by mapping a zeroed page at the USER_STACK */
    /* USER_STACK에서 0으로 설정된 페이지를 매핑하여 최소 스택 생성 */
    static bool
    setup_stack(struct intr_frame *if_)
    {
    	uint8_t *kpage;
    	bool success = false;
    
    	kpage = palloc_get_page(PAL_USER | PAL_ZERO);
    	if (kpage != NULL)
    	{
    		success = install_page(((uint8_t *)USER_STACK) - PGSIZE, kpage, true);
    		if (success)
    			if_->rsp = USER_STACK;
    
    		else
    			palloc_free_page(kpage);
    	}
    	return success;
    }
    • install_page() - process.c
      /* Adds a mapping from user virtual address UPAGE to kernel
       * virtual address KPAGE to the page table.
       * If WRITABLE is true, the user process may modify the page;
       * otherwise, it is read-only.
       * UPAGE must not already be mapped.
       * KPAGE should probably be a page obtained from the user pool
       * with palloc_get_page().
       * Returns true on success, false if UPAGE is already mapped or
       * if memory allocation fails. */
      /* 사용자 가상 주소 UPAGE에서 커널 가상 주소 KPAGE로의 매핑을 페이지 테이블에 추가함
       * writable이 true이면 user process가 페이지를 수정가능
       * 어나면 읽기 전용
       * UPAGE가 이미 매핑되어 있지 않아야 함
       * KPAGE는 palloc_get_page()를 통해 USER pool에서 가져온 페이지여야 함
       * 성공 시 true를 반환하고, UPAGE가 이미 매핑되어 있거나 메모리 할당이 실패하면 false를 반환 */
      static bool
      install_page(void *upage, void *kpage, bool writable)
      {
      	struct thread *t = thread_current();
      
      	/* Verify that there's not already a page at that virtual
      	 * address, then map our page there. */
      	/* 해당 가상 주소에 페이지가 아직 없는지 확인한 후 해당 페이지에 매핑 */
      	return (pml4_get_page(t->pml4, upage) == NULL 
      						&& 
      					pml4_set_page(t->pml4, upage, kpage, writable));
      }
      • pml4_get_page() - mmu.c
        /* Looks up the physical address that corresponds to user virtual
         * address UADDR in pml4.  Returns the kernel virtual address
         * corresponding to that physical address, or a null pointer if
         * UADDR is unmapped. */
        void *
        pml4_get_page (uint64_t *pml4, const void *uaddr) {
        	ASSERT (is_user_vaddr (uaddr));
        
        	uint64_t *pte = pml4e_walk (pml4, (uint64_t) uaddr, 0);
        
        	if (pte && (*pte & PTE_P))
        		return ptov (PTE_ADDR (*pte)) + pg_ofs (uaddr);
        	return NULL;
        }
      • pml4_set_page() - mmu.c
        /* Adds a mapping in page map level 4 PML4 from user virtual page
         * UPAGE to the physical frame identified by kernel virtual address KPAGE.
         * UPAGE must not already be mapped. KPAGE should probably be a page obtained
         * from the user pool with palloc_get_page().
         * If WRITABLE is true, the new page is read/write;
         * otherwise it is read-only.
         * Returns true if successful, false if memory allocation
         * failed. */
        bool
        pml4_set_page (uint64_t *pml4, void *upage, void *kpage, bool rw) {
        	ASSERT (pg_ofs (upage) == 0);
        	ASSERT (pg_ofs (kpage) == 0);
        	ASSERT (is_user_vaddr (upage));
        	ASSERT (pml4 != base_pml4);
        
        	uint64_t *pte = pml4e_walk (pml4, (uint64_t) upage, 1);
        
        	if (pte)
        		*pte = vtop (kpage) | PTE_P | (rw ? PTE_W : 0) | PTE_U;
        	return pte != NULL;
        }

argument_stack()

void argument_stack(char **parse, int count, void **rsp)
{
	int i, j;
	void *addr_argv[count];

	/* parse로 들어온값을 스택에 push (뒤에서부터) */
	for (i = count-1; i >= 0; i--)	// v,u,t,s, ... ,c,b,a,args-single
	{
		for (j = strlen(parse[i]); j >= 0; j--) // NULL 문자를 읽기 위해 str + 1 만큼 읽음
		{
			/* *rsp는 끝 주소를 적어놨기 때문에 감소 후에 데이터를 push 해야한다. */
			*rsp = *rsp - 1;
			**(char **)rsp = parse[i][j];
		}
		/* 인자값들의 주소를 저장(argv[]의 주소) */
		addr_argv[i] = *rsp;		// args-single, a,b,c,...,s,t,u,v
	}

	/* word-align (padding)
	 * uintptr_t = 8byte
	 * rsp 포인터를 8의 배수로 정렬해주기 위함
	 */
	uintptr_t dst = *(uintptr_t *)rsp & (0xfffffff8);
	for (*rsp -= 1; *rsp >= dst; *rsp -= 1)
	{		
		**(char **)rsp = NULL;
	}
	*rsp += 1;

	/* argv[argc] */
	// for (int idx = 0; idx < WORD_ALIGN; idx++)
	// {
	// 	*rsp = *rsp - 1;
	// 	**(char **)rsp = NULL;
	// }

	/* argv[argc] */
	*rsp = *rsp - WORD_ALIGN;
	**(uintptr_t **)rsp = NULL;

	/* argv[]의 주소값 역순으로 저장 */
	for (i = count-1; i >= 0; i--)
	{
		*rsp = *rsp - WORD_ALIGN;
		**(uintptr_t **)rsp = addr_argv[i];
	}

	/* fake return address */
	*rsp = *rsp - WORD_ALIGN;
	**(uintptr_t **)rsp = NULL;
}

do_iret()

/* Use iretq to launch the thread */
void do_iret(struct intr_frame *tf)
{
	__asm __volatile(
		"movq %0, %%rsp\n"				  /* tf의 주소를 rsp에 저장 */
		"movq 0(%%rsp),%%r15\n"			/* rsp위치의 값을 레지스터 r15에 저장 */
		"movq 8(%%rsp),%%r14\n"			/* rsp + 8 의 위치의 값을 레지스터 r14에 저장 */
		"movq 16(%%rsp),%%r13\n"		/* rsp + 16 의 위치의 값을 레지스터 r13에 저장 */
		"movq 24(%%rsp),%%r12\n"		/* rsp + 24 의 위치의 값을 레지스터 r12에 저장 */
		"movq 32(%%rsp),%%r11\n"		/* rsp + 32 의 위치의 값을 레지스터 r11에 저장 */
		"movq 40(%%rsp),%%r10\n"		/* rsp + 40 의 위치의 값을 레지스터 r10에 저장 */
		"movq 48(%%rsp),%%r9\n"			/* rsp + 48 의 위치의 값을 레지스터 r9에 저장 */
		"movq 56(%%rsp),%%r8\n"			/* rsp + 56 의 위치의 값을 레지스터 r8에 저장 */
		"movq 64(%%rsp),%%rsi\n"		/* rsp + 64 의 위치의 값을 레지스터 rsi에 저장 */
		"movq 72(%%rsp),%%rdi\n"		/* rsp + 72 의 위치의 값을 레지스터 rdi에 저장 */
		"movq 80(%%rsp),%%rbp\n"		/* rsp + 80 의 위치의 값을 레지스터 rbp에 저장 */
		"movq 88(%%rsp),%%rdx\n"		/* rsp + 88 의 위치의 값을 레지스터 rdx에 저장 */
		"movq 96(%%rsp),%%rcx\n"		/* rsp + 96 의 위치의 값을 레지스터 rcx에 저장 */
		"movq 104(%%rsp),%%rbx\n"		/* rsp + 104 의 위치의 값을 레지스터 rbx에 저장 */
		"movq 112(%%rsp),%%rax\n"		/* rsp + 112 의 위치의 값을 레지스터 rax에 저장 */
		"addq $120,%%rsp\n"				  /* rsp를 120만큼 증가시킴 */
		"movw 8(%%rsp),%%ds\n"			/* padding을 제외한 값을 레지스터 ds에 저장 */
		"movw (%%rsp),%%es\n"			  /* padding을 제외한 값을 레지스터 es에 저장 */
		"addq $32, %%rsp\n"				  /* rsp를 32만큼 증가시킴 -> rip를 가리킴(?) */
		"iretq"							        /* rip, cs, rflags, rsp, ss는 cpu가 push하고
																	 **유저모드로 넘어감** */
		:
		: "g"((uint64_t)tf)				  /* 인자로 tf를 받음 */
		: "memory");					      /* memory의 값이 변경됨 */
}
  • intr_frame 구조 참고
    /* Interrupt stack frame. */
    struct gp_registers {
    	uint64_t r15;
    	uint64_t r14;
    	uint64_t r13;
    	uint64_t r12;
    	uint64_t r11;
    	uint64_t r10;
    	uint64_t r9;
    	uint64_t r8;
    	uint64_t rsi;
    	uint64_t rdi;
    	uint64_t rbp;
    	uint64_t rdx;
    	uint64_t rcx;
    	uint64_t rbx;
    	uint64_t rax;
    } __attribute__((packed));
    
    struct intr_frame {
    	/* Pushed by intr_entry in intr-stubs.S.
    	   These are the interrupted task's saved registers. */
    	struct gp_registers R;
    	uint16_t es;
    	uint16_t __pad1;
    	uint32_t __pad2;
    	uint16_t ds;
    	uint16_t __pad3;
    	uint32_t __pad4;
    	/* Pushed by intrNN_stub in intr-stubs.S. */
    	uint64_t vec_no; /* Interrupt vector number. */
    /* Sometimes pushed by the CPU,
       otherwise for consistency pushed as 0 by intrNN_stub.
       The CPU puts it just under `eip', but we move it here. */
    	uint64_t error_code;
    /* Pushed by the CPU.
       These are the interrupted task's saved registers. */
    	uintptr_t rip;
    	uint16_t cs;
    	uint16_t __pad5;
    	uint32_t __pad6;
    	uint64_t eflags;
    	uintptr_t rsp;
    	uint16_t ss;
    	uint16_t __pad7;
    	uint32_t __pad8;
    } __attribute__((packed));

0개의 댓글