DevLog ๐Ÿ“จ

[DevLog][PintOS] PRJ3 Virtual Memory/Memory Mapped Files to Copy-on-write

cece00 2023. 10. 24. 04:52

04) Memory Mapped Files

์‹œ์Šคํ…œ ์ฝœ mmap๊ณผ munmap์„ ๊ตฌํ˜„ํ•œ๋‹ค. ํ˜„์žฌ๊นŒ์ง€๋Š” ๋ชจ๋“  ํŽ˜์ด์ง€๊ฐ€ anonymous, ์ฆ‰ ์Šคํƒ ํŽ˜์ด์ง€์˜€์ง€๋งŒ mmap์„ ํ†ตํ•ด ํŒŒ์ผ๊ณผ ๋งคํ•‘๋œ ํŽ˜์ด์ง€๋Š” file-backed page๊ฐ€ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ๋งคํ•‘์ด ํ•ด์ œ๋  ๋•Œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ํŒŒ์ผ์— ๋‹ค์‹œ ๊ธฐ๋กํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ๋””์Šคํฌ์— ํŒŒ์ผ์„ ์“ฐ๋Š” ์ž‘์—…์€ RAM๋Œ€๋น„ ์†๋„๊ฐ€ ๋งค์šฐ ๋Š๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์—, ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์žˆ๋Š” ํŽ˜์ด์ง€๋งŒ ๋””์Šคํฌ์— ๊ธฐ๋กํ•ด์ค„ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. ํŽ˜์ด์ง€๊ฐ€ ๋ณ€๊ฒฝ๋  ๊ฒฝ์šฐ, mmu๋Š” ์•Œ์•„์„œ pml4 ํ…Œ์ด๋ธ”์— dirty bit๋ฅผ ์„ธํŒ…ํ•ด์ค€๋‹ค.

/* Write back a single page to file. Called when unmap. */
static bool file_write_back(struct page *page, struct page *head) {
  ASSERT(page->operations->type == VM_FILE)
  struct thread *curr = thread_current();

  if (!pml4_is_dirty(curr->pml4, page->va)) {
    /* If page is present and
     * not dirty, return. */
    return true;
  }

  /* Write back to file. */
  if (!do_file_io(page, head, filesys_write)) {
    printf("Fail to write back file.\n");
    return false;
  }
  return true;
}

// filesys.c
off_t filesys_write(struct file *file, const void *buf, off_t size) {
  int bytes;
  lock_acquire(&filesys_lock);
  bytes = file_write(file, buf, size);
  lock_release(&filesys_lock);
  return bytes;
}

์œ„ ํ•จ์ˆ˜๋Š” ํ•˜๋‚˜์˜ ํŽ˜์ด์ง€๊ฐ€ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์žˆ๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๊ณ , ์žˆ์„ ๊ฒฝ์šฐ ํŒŒ์ผ์— ๋‹ค์‹œ ์“ฐ๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ํ•€ํ† ์Šค์˜ ํ”„๋กœ์ ํŠธ 3๊นŒ์ง€๋Š” ํŒŒ์ผ์‹œ์Šคํ…œ์— ์ ‘๊ทผํ•  ๋•Œ lock์„ ๊ฑธ์–ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์œผ๋ฏ€๋กœ, ๋ณ„๋„๋กœ filesys_write์ด๋ผ๋Š” file_write wrapper function์„ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉํ–ˆ๋‹ค. do_file_io๋Š” page์— ๋Œ€ํ•ด ํ•ด๋‹น ํŽ˜์ด์ง€์˜ head-page*๋ฅผ ์ฐพ์•„์„œ ์„ธ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. disk์— ์ ‘๊ทผํ•˜๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ๋„ ๋ฝ์„ ํ•œ๋ฒˆ ๋” ๊ฑด๋‹ค.

/* Calculate page offset in mapped area
 * and execute func handed by argument. */
static bool do_file_io(struct page *page, struct page *head, file_io func) {
  struct file *file = head->file.file;
  off_t offset = page->file.offset;
  size_t length = page->file.length;
  void *map_addr = page->file.map_addr;
  void *kva = page->frame->kva;

  ASSERT(page->frame && kva)

  /* Calculate page offset. Starts from 1. */
  off_t page_offset = (page->va - map_addr) / PGSIZE;

  /* Calculate read-write-start point. */
  off_t start = offset + page_offset * PGSIZE;

  /* Calculate read-write-bytes. */
  filesys_seek(file, start);

  /*
     offset                                   len  page-aligned
       +----------+----------+----------+------+---+
       |          |          |          |      |   |
       |          |          |          |      |   |
       |   page   |   page   |   page   |   page   |
       |          |          |          |      |   |
       |          |          |          |      |   |
       +----------+----------+----------+------+---+

       +----------+-------+
       |                  |
       |       file       |
       |                  |
       +----------+-------+

       Take min(PGSIZE, (off+len)-rw_start, file_left) as rw_bytes of this page.
       Page 0 takes PGSIZE,
       Page 1 takes file_left, and so on.
  */

  off_t file_left = filesys_length(file) - file->pos;
  size_t bytes = (offset + length) - start;
  bytes = bytes < PGSIZE ? bytes : PGSIZE;
  bytes = bytes < file_left ? bytes : file_left;

  /* Do func. */
  if (func(file, kva, bytes) != (int)bytes) {
    return false;
  }
  return true;
}

filesys_write ๋˜๋Š” filesys_read์˜ ๋ฐ˜ํ™˜๊ฐ’์ธ byte๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์˜๋„ํ•˜๋Š” ๋งŒํผ ํŒŒ์ผ ์ฝ๊ธฐ/์“ฐ๊ธฐ๊ฐ€ ์ž˜ ์ˆ˜ํ–‰๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ •ํ™•ํ•œ ๋ฐ”์ดํŠธ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ด์ฃผ์—ˆ๋‹ค. ํ•€ํ† ์Šค ํ”„๋กœ์ ํŠธ 3๊นŒ์ง€๋Š” ํŒŒ์ผ์ด grow ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ํŒŒ์ผ ๊ธธ์ด, ํŽ˜์ด์ง€ ์‚ฌ์ด์ฆˆ, length ์ค‘์—์„œ ๊ฐ€์žฅ ์ž‘์€ ๊ฐ’์„ ํƒํ•ด io function์— ์ „๋‹ฌํ•œ๋‹ค.

mmap์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ๊ฐ€์žฅ ๊นŒ๋‹ค๋กœ์› ๋˜ ์  ์ค‘ ํ•˜๋‚˜๋Š” ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€์— ๊ฑธ์ณ ๋งคํ•‘๋œ ํŒŒ์ผ์„ ๋‹ค๋ฃจ๋Š” ์ผ์ด์—ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋งคํ•‘์ด ์‹œ์ž‘๋˜๋Š” ํŽ˜์ด์ง€์ธ head-page์™€ ์ด์— ๋”ธ๋ฆฐ ํŽ˜์ด์ง€๋“ค์ธ sub-pages ๊ฐœ๋…์ด ์ƒ๊ฒผ๋‹ค. ๊ตฌํ˜„ ๋ฐฉํ–ฅ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. file-backed page๊ฐ€ ๋งคํ•‘์ด ํ•ด์ œ๋˜๋Š” ๊ฒฝ์šฐ, ํ•ด๋‹น ํŽ˜์ด์ง€์˜ head-page๋ฅผ spt์—์„œ ์ฐพ๋Š”๋‹ค.
  2. head-page์—์„œ ๋งคํ•‘๋œ ํŒŒ์ผ ์ •๋ณด๋ฅผ ์ฝ์–ด์˜จ๋‹ค.
  3. ํŒŒ์ผ์— writeback์„ ์ˆ˜ํ–‰ํ•œ ๋‹ค์Œ ํŽ˜์ด์ง€๋ฅผ swap-outํ•˜๊ฑฐ๋‚˜ destroyํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•œ ์ด์œ ๋Š”, ํ•˜๋‚˜์˜ ๋งคํ•‘์— ์†ํ•œ ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€๋“ค์ด ๋ชจ๋‘ ํŒŒ์ผ์˜ ์‚ฌ๋ณธ (๊ฐ™์€ inode์ง€๋งŒ file pointer๊ฐ€ ๋‹ค๋ฅธ)์„ ๊ฐ€์ง€์ง€ ์•Š๊ฒŒ ํ•˜๋ฉด์„œ๋„, ํ•˜๋‚˜์˜ file pointer์— ๋Œ€ํ•ด filesys_close๋ฅผ ๋‘ ๋ฒˆ ์ด์ƒ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค. sub-pages๋Š” file pointer๋ฅผ ๊ฐ–์ง€ ์•Š๊ณ , head-page์˜ ๊ฐ€์ƒ์ฃผ์†Œ ์ •๋ณด๋ฅผ ๊ฐ–๋Š”๋‹ค.

// ํŽ˜์ด์ง€ ๊ตฌ์กฐ์ฒด ๋‚ด ํŒŒ์ผ ํŽ˜์ด์ง€
struct file_page {
  size_t slot;
  off_t offset;
  size_t length;
  void *map_addr;
  struct file *file;
};


/* Get head page of this mapping. */
static struct page *spt_get_head(struct page *page) {
  struct thread *curr = thread_current();

  /* Get file page struct. */
  struct file_page *file_page = get_file_page(page);

  if (is_file_head(page, file_page)) {
    return page;
  }
  return spt_find_page(&curr->spt, file_page->map_addr);
}

/* Hash action function which write all file-backed pages
 * back to disk if page is dirty.
 * See supplemental_page_table_kill in vm.c */
void spt_file_writeback(struct hash_elem *e, void *aux) {
  struct page *page = hash_entry(e, struct page, table_elem);
  if (page_get_type(page) != VM_FILE) return;
  if (!is_file_head(page, get_file_page(page))) return;

  /* If page is head-page of
   * file-backed pages, write back. */
  file_write_back_all(page);
}


/* Write back all file-backed pages starting from head-page. */
static void file_write_back_all(struct page *head) {
  struct thread *curr = thread_current();

  void *p = head->va;
  void *map_addr = head->va;
  struct page *page = head;

  while (page && get_file_page(page)->map_addr == map_addr) {
    if (!pg_present(page)) {
      /* Clearing uninit page will be handled by uninit_destroy.
       * If swap-out page, no need to write back.
       * Just go to next loop. */
      p += PGSIZE;
      page = spt_find_page(&curr->spt, p);
      continue;
    }

    /* Write back to file. */
    file_write_back(page, head);

    p += PGSIZE;
    page = spt_find_page(&curr->spt, p);
  }

  /* Close file. */
  filesys_close(get_file_page(head)->file);
}

munmap์‹œ์Šคํ…œ ์ฝœ์ด ํ˜ธ์ถœ๋  ๋•Œ๋Š” ์ธ์ž๋กœ ๋ฐ›๋Š” ๊ฐ€์ƒ์ฃผ์†Œ๊ฐ€ head-page์˜ ๊ฐ€์ƒ์ฃผ์†Œ์—ฌ์•ผ ํ•œ๋‹ค๋Š” ์ œ์•ฝ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์ฃผ์†Œ์— ๋Œ€ํ•ด file_write_back_all๋งŒ ํ˜ธ์ถœํ•ด์ค€ ํ›„, spt_remove_page๋กœ ํŽ˜์ด์ง€๋ฅผ destroyํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ๋‹ค๋งŒ ํ•ด์‹œ ํ…Œ์ด๋ธ”์„ ๋Œ๋ฉด์„œ spt ์ „์ฒด๋ฅผ killํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” spt๋ฅผ ์‚ญ์ œํ•˜๊ธฐ ์ „์— ๋จผ์ € ๊ฐ file-backed page์˜ head-page๋ฅผ ๊ณจ๋ผ file_write_back_all์„ ํ•œ๋ฒˆ์”ฉ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค. (์œ„์˜ spt_file_writebackํ•จ์ˆ˜๊ฐ€ ํ•ด์‹œ์˜ ๊ฐ ์—”ํŠธ๋ฆฌ์— ๋Œ€ํ•ด ์ˆ˜ํ–‰๋œ๋‹ค.)

mmap, munmap ์—์„œ trickyํ•œ ๋ถ€๋ถ„์€ ์ด์ •๋„์˜€๋˜ ๊ฒƒ ๊ฐ™๋‹ค. ์ฒ˜์Œ์—๋Š” destroy function ์•ˆ์—์„œ file writeback์„ ์ˆ˜ํ–‰ํ•˜๋ ค๊ณ  ํ–ˆ์œผ๋‚˜ spt ํ…Œ์ด๋ธ”์„ ์กฐ์ž‘ํ•˜๋ฉด์„œ head-page๋ฅผ ์ฐพ๋‹ค ๋ณด๋‹ˆ ์ค‘๊ฐ„์— head๊ฐ€ ๋‚ ์•„๊ฐ€๋ฒ„๋ฆฌ๋Š” ๋ถˆ์ƒ์‚ฌ๊ฐ€ ์ƒ๊ฒผ๋‹ค. ๋”ฐ๋ผ์„œ writeback์€ writeback๋Œ€๋กœ, remove๋Š” remove๋Œ€๋กœ ์ˆ˜ํ–‰ํ•˜๋„๋ก ๋กœ์ง์„ ๋ณ€๊ฒฝํ–ˆ๋‹ค.

/* Free the resource hold by the supplemental page table. */
void supplemental_page_table_kill(struct supplemental_page_table *spt) {
  if (!spt) return;

  /* Write back file_backed_pages first, */
  hash_apply(&spt->hash, spt_file_writeback);

  /* Destroy hash. */
  hash_destroy(&spt->hash, spt_free_page);
  return;
}

ํ•œ๊ฐ€์ง€ ๋” ์‹ ๊ฒฝ ์ผ๋˜(?) ๋ถ€๋ถ„์€ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ์˜ ์‚ฌ์šฉ์œผ๋กœ, filesys_read, filesys_write์˜ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋™์ผํ•˜๋‹ค๋Š” ์ ์„ ํ™œ์šฉํ•ด ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ์ˆ˜ํ–‰ํ•˜๋Š” do_file_io ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด ์ค‘๋ณต๋˜๋Š” ๋กœ์ง์„ ์ค„์ผ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์œ ์‚ฌํ•˜๊ฒŒ, anon page swap in/out์—์„œ swap disk io์ชฝ๋„ ๋™์ผํ•œ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ๋‹ค. ๋ณ€๊ฒฝํ•˜๋Š” ๊น€์— syscall ์ชฝ๋„ ๋ชจ๋‘ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ syscall table๋กœ ์ˆ˜์ •ํ•ด๋‘์—ˆ๋‹ค. ์ด๋Ÿฐ ์‹์œผ๋กœ ๋กœ์ง์„ ๋”ฐ๋กœ ๋นผ ๋‘๋ฉด, ์ผ๋‹จ 1. ์ˆ˜์ •ํ•˜๊ธฐ ํŽธํ•˜๊ณ  2. ๊ณต์œ  ์ž์›์ธ ๊ฒฝ์šฐ lock์ด๋‚˜ semaphore๋ฅผ ๊ฑธ๊ธฐ ์ˆ˜์›”ํ•ด์ง€๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

๐Ÿ‘€ ์™„์„ฑ๋œ mmap & munmap


/* Do the mmap */
void *do_mmap(void *addr, size_t length, int writable, struct file *file,
              off_t offset) {
  struct thread *curr = thread_current();

  /* If addr or offset is not page-aligned. */
  if ((uint64_t)addr % PGSIZE || offset % PGSIZE) {
    return NULL;
  }

  /* If addr is in spt. */
  for (void *p = addr; p < addr + length; p += PGSIZE) {
    if (spt_find_page(&curr->spt, p)) {
      return NULL;
    }
  }

  /* If addr in stack area. */
  if (addr + length > STACK_LIMIT && addr < USER_STACK) {
    return NULL;
  }

  /* Allocate page with lazy loading. */
  struct file *mmap_file = file_duplicate(file);
  long left = (long)length;
  int cnt = 0;

  while (left > 0) {
    /* Save infos for initializing file-backed page. */
    struct file_page *aux = calloc(1, sizeof(struct file_page));

    /* Only head page holds mmap_file address. */
    aux->file = cnt == 0 ? mmap_file : NULL; /* map file from */
    aux->offset = offset;                    /* offset, */
    aux->length = length;                    /* to length bytes. */
    aux->map_addr = addr;                    /* va where mapping starts. */

    if (!vm_alloc_page_with_initializer(VM_FILE, addr + (cnt * PGSIZE),
                                        writable, lazy_load_file, aux)) {
      return NULL;
    }

    left -= PGSIZE;
    cnt++;
  }
  return addr;
}


/* Do the munmap. Unmap can be called when
 * head or sub pages are not initialized. */
void do_munmap(void *addr) {
  struct thread *curr = thread_current();
  struct page *page = spt_find_page(&curr->spt, addr);

  /* If page is not found, return. */
  if (page == NULL) return;

  /* Virtual address must be the map_addr. */
  struct file_page *file_page = get_file_page(page);
  if (file_page->map_addr != addr) return;

  size_t length = file_page->length;

  /* Write back all pages starting from head. */
  file_write_back_all(page);

  /* Remove head-page and all sub-pages from spt and pml4. */
  for (void *p = addr; p < addr + length; p += PGSIZE) {
    page = spt_find_page(&curr->spt, p);
    spt_remove_page(&curr->spt, page);
  }
}


/* Initialize file-backed frame. */
static bool lazy_load_file(struct page *page, void *aux) {
  ASSERT(page->operations->type == VM_FILE)
  ASSERT(page->frame)

  /* Get head-page. */
  struct thread *curr = thread_current();
  struct page *head = spt_get_head(page);
  ASSERT(head != NULL)

  /* Read file to page. */
  bool succ;
  lock_acquire(&load_lock);
  succ = do_file_io(page, head, file_read);
  lock_release(&load_lock);

  return succ;
}

05) Swap in/out

์œ ์ € ํ’€์—์„œ ํ”„๋ ˆ์ž„์„ ํ• ๋‹น๋ฐ›์•„์˜ค๋Š” ํ˜„์žฌ์˜ vm_get_frameํ•จ์ˆ˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์ด ๋ฐ”๋‹ฅ๋‚œ ๊ฒฝ์šฐ ํ”„๋ ˆ์ž„์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. ํ”„๋กœ์„ธ์Šค๋Š” ์ปดํ“จํ„ฐ๋ฅผ ์ถ”์ƒํ™”ํ•˜๋ฏ€๋กœ, ๋ฉ”๋ชจ๋ฆฌ ์—ญ์‹œ ๋…๋ฆฝ์ ์ธ ๊ฐ€์ƒ์ฃผ์†Œ๊ณต๊ฐ„์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์›ฌ๋งŒํ•ด์„  ๋ฐ”๋‹ฅ๋‚˜์ง€ ๋ชปํ•˜๋„๋ก swap disk๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์ž.

  • ๋ฉ”๋ชจ๋ฆฌ์ƒ์˜ ํ”„๋ ˆ์ž„์„ ์Šค์™‘ ๋””์Šคํฌ๋กœ ๋‚ด๋ฆฌ๋Š” ๊ฒƒ์„ swap-out์ด๋ผ๊ณ  ํ•œ๋‹ค.
  • ์Šค์™‘ ๋””์Šคํฌ์— ์žˆ๋Š” ํ”„๋ ˆ์ž„์„ ๋‹ค์‹œ ๋ฉ”๋ชจ๋ฆฌ๋กœ ์ฝ์–ด ์˜ค๋Š” ๊ฒƒ์„ swap-in์ด๋ผ๊ณ  ํ•œ๋‹ค.
static struct frame *vm_get_frame(void) {
  struct frame *frame = NULL;
  frame = calloc(1, sizeof(struct frame));
  if (frame == NULL) {
    printf("Frame allocation failed.\n");
    return NULL;
  }

  /* Get anonymous frame. */
  frame->kva = palloc_get_page(PAL_USER | PAL_ZERO);
  if (frame->kva == NULL) {
    free(frame);
    /* If out of memory,
     * evict frame from frame table.*/
    frame = vm_evict_frame();
  }

  /* Init page list, push into frame table. */
  list_init(&frame->pages);
  list_push_back(&frame_table, &frame->elem);

  ASSERT(frame != NULL);
  ASSERT(list_empty(&frame->pages));
  return frame;
}

์ˆ˜์ •๋œ vm_get_frameํ•จ์ˆ˜๋Š” palloc_get_page(PAL_USER | PAL_ZERO);๊ฐ€ ์‹คํŒจํ•  ๊ฒฝ์šฐ, ํ”„๋ ˆ์ž„ ํ…Œ์ด๋ธ”์—์„œ ํ”„๋ ˆ์ž„์„ ํ•˜๋‚˜ swap-outํ•˜๊ณ  ๊ฐ€์ ธ์˜จ๋‹ค. ์ฆ‰ ๋‚จ์ด ์“ฐ๋˜ ์ค‘๊ณ ์ธ ๊ฒƒ์ด๋‹ค.. ๋”ฐ๋ผ์„œ evict_frame์„ ํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ”„๋ ˆ์ž„์„ ๊นจ๋—์ด 0์œผ๋กœ ๋ฐ€์–ด์ค˜์•ผ ํ•œ๋‹ค.

/* Evict one page and return the corresponding frame.
 * Return NULL on error.*/
static struct frame *vm_evict_frame(void) {
  struct frame *victim = vm_get_victim();

  ASSERT(!list_empty(&victim->pages));

  struct list_elem *front = list_front(&victim->pages);
  struct page *page = list_entry(front, struct page, frame_elem);

  /* Remove all pages from frame and
   * push into swap table. */
  if (swap_out(page)) {
    /* After, initialize victim. */
    ASSERT(list_empty(&victim->pages));
    memset(victim->kva, 0, PGSIZE); // ๐Ÿ”ฅ 0์œผ๋กœ ๋ฆฌ์…‹
    return victim;
  }
  return NULL;
}

์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐ ํ”„๋กœ์„ธ์Šค๋“ค์— ํ• ๋‹น๋œ ํ”„๋ ˆ์ž„์˜ ์ฃผ์†Œ๋ฅผ ์—ฎ์–ด ๋†“์€ ์ „์—ญ์ ์ธ frame table์ด ํ•„์š”ํ•˜๋‹ค. ํ”„๋ ˆ์ž„ ํ…Œ์ด๋ธ”์˜ ์ž๋ฃŒ๊ตฌ์กฐ๋Š” ์Šค์™‘ ์•„์›ƒํ•  ํ”„๋ ˆ์ž„์„ ๊ณ ๋ฅด๋Š” ๋ฐฉ๋ฒ•, ์ฆ‰ evict policy์— ์˜ํ•ด ๊ฒฐ์ •๋  ์ˆ˜ ์žˆ๋‹ค. ์šฐ๋ฆฌ ์กฐ๋Š” ๋‹จ์ˆœ FIFO๋กœ ํ•œ๋ฒˆ, ๊ทธ๋ฆฌ๊ณ  ์›ํ˜• ํ๋ฅผ ํ™œ์šฉํ•œ Clock Algorithm์œผ๋กœ ํ•œ๋ฒˆ ๊ตฌํ˜„ํ•ด๋ณด์•˜๋‹ค.

// ์ „์—ญ๋ณ€์ˆ˜
static struct list_elem *clock_hand;
static struct list frame_table;
static struct lock frame_lock;

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

  /* Used when each process access frame table. */ 
  lock_init(&frame_lock);

  /* Create frame table as CLL. */
  list_init(&frame_table);
  clock_hand = &frame_table.head;
  frame_table.head.prev = &frame_table.tail;
  frame_table.tail.next = &frame_table.head;
}

ํ•€ํ† ์Šค์˜ <list.h>๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณต๋˜๋Š” ๋ฆฌ์ŠคํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด์ค‘ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์— head์™€ tail๋งŒ ์—ฐ๊ฒฐํ•ด์ฃผ๋ฉด ์›ํ˜• ํ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. Clock ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ธฐ๋ณธ ์•„์ด๋””์–ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. Clock hand๊ฐ€ ์›ํ˜• ํ๋ฅผ ๋Œ๋ฉด์„œ ํ”„๋ ˆ์ž„๊ณผ ์—ฐ๊ฒฐ๋œ ํŽ˜์ด์ง€์˜ ์ ‘๊ทผ ๋น„ํŠธ access bit๋ฅผ ๊ฒ€์‚ฌํ•œ๋‹ค.
  2. ์ ‘๊ทผ ๋น„ํŠธ๊ฐ€ 1๋กœ ์„ค์ •๋œ ๊ฒฝ์šฐ, 0์œผ๋กœ ์„ค์ •ํ•˜๊ณ  ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ„๋‹ค.
  3. ์ ‘๊ทผ ๋น„ํŠธ๊ฐ€ 0์œผ๋กœ ์„ค์ •๋œ ๊ฒฝ์šฐ, ํ•ด๋‹น ํ”„๋ ˆ์ž„์„ victim์œผ๋กœ ์„ ์ •ํ•˜๊ณ  ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

Clock์€ LRU (Least Recently Used)๋ฅผ ๊ทผ์‚ฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์—ฌ๊ฒจ์ง€๋Š”๋ฐ, Clock hand๊ฐ€ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•œ ๋ฐ”ํ€ด ๋„๋Š” ๋™์•ˆ ์ ‘๊ทผ ๋น„ํŠธ๊ฐ€ 0์ธ, ์ฆ‰ ์ ‘๊ทผ๋˜์ง€ ์•Š์€ ํŽ˜์ด์ง€๋ฅผ '์ตœ๊ทผ์— ์‚ฌ์šฉ๋˜์ง€ ์•Š์€' ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•œ๋‹ค๋Š” ์ ์—์„œ ๊ทธ๋ ‡๋‹ค. ๋งŒ์•ฝ ์ ‘๊ทผ ๋น„ํŠธ๊ฐ€ 1์ธ ๊ฒฝ์šฐ 0์œผ๋กœ ์„ธํŒ…ํ•˜๊ณ  ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ€๋ฏ€๋กœ Second-chance๋ฅผ ์ฃผ๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋ผ๊ณ  ๋ณผ ์ˆ˜๋„ ์žˆ๋‹ค. ์ด๋Š” ๊ฐ€์žฅ ์ตœ๊ทผ์— ์ฐธ์กฐ๋˜์—ˆ๋˜ ํŽ˜์ด์ง€๊ฐ€ ์•ž์œผ๋กœ๋„ ๊ฐ€์žฅ ๋นจ๋ฆฌ ๋‹ค์‹œ ์ฐธ์กฐ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค๋Š” ์ฐธ์กฐ์ง€์—ญ์„ฑ ๊ฐœ๋…์— ๊ธฐ๋ฐ˜ํ•œ๋‹ค. (๋ฐ˜๋ณต๋ฌธ ๊ฐ™์€ ๊ฒฝ์šฐ๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž.)

Test์—์„œ๋Š” ๋ณดํ†ต ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ๋‹จ ํ•œ๋ฒˆ์”ฉ๋งŒ ์ ‘๊ทผํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„ FIFO์™€ Clock์˜ ์„ฑ๋Šฅ์ด ์œ ์˜๋ฏธํ•œ ์ฐจ์ด๋ฅผ ๋ณด์ด์ง€ ์•Š์•˜๋˜ ๊ฒƒ ๊ฐ™์ง€๋งŒ, ๋งคํ•‘๋œ ์ผ๋ถ€ ํŽ˜์ด์ง€๋งŒ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ ‘๊ทผํ•˜๊ฑฐ๋‚˜ ๋žœ๋คํ•œ ์ˆœ์„œ๋กœ ์ ‘๊ทผํ•  ๊ฒฝ์šฐ Clock์˜ ์„ฑ๋Šฅ์ด ๋†’์•„์ง€๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

/* Get the struct frame, that will be evicted. */
static struct frame *vm_get_victim(void) {
  struct list_elem *head = &frame_table.head;
  struct list_elem *tail = &frame_table.tail;

  if (list_empty(&frame_table)) {
    PANIC("No entry in frame table.");
  }

  lock_acquire(&frame_lock);

  struct list_elem *curr = clock_hand;
  size_t frame_access = SIZE_MAX;
  size_t least_access = SIZE_MAX;

  struct frame *frame = NULL;
  struct frame *victim = NULL;

  /* Clock algorithm with CLL */
  while (frame_access != 0) {
    if (curr == head || curr == tail) {
      curr = list_next(curr);
      continue;
    }

    frame = list_entry(curr, struct frame, elem);
    frame_access = get_access_pages(frame);
    clear_access_pages(frame);

    if (frame_access < least_access) {
      least_access = frame_access;
      victim = frame;
    }

    curr = list_next(curr);
  }

  clock_hand = curr;
  list_remove(&victim->elem);

  lock_release(&frame_lock);
  return victim;
}

๋‹ค์Œ์€ ์ปค์Šคํ…€ ํ…Œ์ŠคํŠธ๋กœ Clock๊ณผ FIFO๋ฅผ ๋Œ๋ฆฐ ๊ฒฐ๊ณผ์ด๋‹ค. Tick์ˆ˜์™€ hd1:1(swap disk) ioํšŸ์ˆ˜๊ฐ€ ์œ ์˜๋ฏธํ•œ ์ฐจ์ด๋ฅผ ๋ณด์ด๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ‘€ ๊ทธ๋Ÿฌ๋ฉด FIFO๊ฐ€ Clock๋ณด๋‹ค ํ•ญ์ƒ ๋‚˜์œ ๊ฑด ์•„๋‹ˆ๋„ค?

๋งŒ์•ฝ ์šด์˜์ฒด์ œ๊ฐ€ ํ”„๋กœ๊ทธ๋žจ์˜ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ํŒจํ„ด์„ ๋ฏธ๋ฆฌ ์•Œ๊ณ  ์žˆ๋‹ค๋ฉด ์ด์— ์ตœ์ ํ™”๋œ ๊ต์ฒด ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํƒํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์ ‘๊ทผ ํŒจํ„ด์ด ๋ฐ˜๋ณต์ ์ด์ง€ ์•Š๊ณ  ์ˆœ์ฐจ์ ์ด๋ผ๋ฉด, clock์— ํ•„์š”ํ•œ ์—ฌ๋Ÿฌ ๋ฃจํ‹ด๋“ค (pml4์—์„œ ์ ‘๊ทผ ๋น„ํŠธ ์ฝ์–ด์˜ค๊ธฐ, ๋ฆฌ์ŠคํŠธ ์ˆœํšŒํ•˜๊ธฐ..) ์€ ์˜ค๋ฒ„ํ—ค๋“œ๋กœ ๋‚จ๊ฒŒ ๋œ๋‹ค. ๋ฆฌ๋ˆ…์Šค์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ํ•ด์„œ clock์ด ํ•ญ์ƒ ๋น ๋ฅธ ๊ฒŒ ์•„๋‹Œ์ง€ ์ƒ๊ฐํ–ˆ์—ˆ๋Š”๋ฐ, ๋ง‰์ƒ ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ ค๋ณด๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๊ด€์ฐฐํ•˜๋‹ค๋ณด๋‹ˆ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์–ด๋–ค ์‹์œผ๋กœ ์ ‘๊ทผ๋˜๋Š”์ง€์— ๋”ฐ๋ผ ๊ฐ ๊ต์ฒด ์ •์ฑ…์ด ์œ ๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋ถˆ๋ฆฌํ•  ๋•Œ๊ฐ€ ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค. ๋งŒ์•ฝ ํ”„๋กœ๊ทธ๋žจ์˜ ์ ‘๊ทผ ํŒจํ„ด์„ ์˜ˆ์ธกํ•  ์ˆ˜ ์—†๊ฑฐ๋‚˜, FIFO ๋Œ€๋น„ ๋ณต์žกํ•œ Clock์˜ ๋ฃจํ‹ด์ด FIFO์˜ ๋‹จ์ ์„ ์ปค๋ฒ„ํ•  ๊ฒฝ์šฐ, Clock ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

// CLOCK.
Executing 'swap-iter':
(swap-iter) begin
(swap-iter) open "large.txt"
(swap-iter) mmap "large.txt"
(swap-iter) end
swap-iter: exit(0)
Execution of 'swap-iter' complete.
Timer: 9237 ticks
Thread: 30 idle ticks, 216 kernel ticks, 8992 user ticks
hd0:0: 0 reads, 0 writes
hd0:1: 43158 reads, 15934 writes
hd1:0: 7948 reads, 0 writes
hd1:1: 281224 reads, 320328 writes

// FIFO
Executing 'swap-iter':
(swap-iter) begin
(swap-iter) open "large.txt"
(swap-iter) mmap "large.txt"
(swap-iter) end
swap-iter: exit(0)
Execution of 'swap-iter' complete.
Timer: 9398 ticks
Thread: 30 idle ticks, 212 kernel ticks, 9156 user ticks
hd0:0: 0 reads, 0 writes
hd0:1: 43158 reads, 15934 writes
hd1:0: 7948 reads, 0 writes
hd1:1: 281800 reads, 320904 writes

ํŽ˜์ด์ง€ ๊ต์ฒด ์ •์ฑ…์„ ์„ ์ •ํ–ˆ์œผ๋ฉด ์ด์ œ swap in/out์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. anon page์™€ file-backed page ๋ชจ๋‘ ๋ฃจํ‹ด์€ ๋™์ผํ•˜๋ฉฐ, ๊ฐ๊ฐ ์Šค์™‘ ๋””์Šคํฌ์™€ ํŒŒ์ผ์— ์จ์ค€๋‹ค๋Š” ๊ฒƒ๋งŒ ๋‹ค๋ฅด๋‹ค. anon์˜ ๊ฒฝ์šฐ ์Šค์™‘ ๋””์Šคํฌ์˜ ์–ด๋–ค ์„นํ„ฐ์— ๊ธฐ๋กํ–ˆ๋Š”์ง€๋ฅผ ์•Œ์•„์•ผ ๋‚˜์ค‘์— ๋‹ค์‹œ swap inํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, anon_page ๊ตฌ์กฐ์ฒด ์•ˆ์— ์Šฌ๋กฏ ๋„˜๋ฒ„๋ฅผ ๊ธฐ๋กํ•ด๋‘”๋‹ค. ๋””์Šคํฌ ์„นํ„ฐ์˜ ๊ฐ€์šฉ ์ƒํƒœ๋งŒ ๊ด€๋ฆฌํ•˜๋ฉด ๋˜๋ฏ€๋กœ, bitmap์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

/* Swap table. */
#define DISK_SEC 512
#define SEC_PER_PAGE 8
#define MAX_SLOTS 32768
#define SLOT_INIT (size_t)(-1)

static struct bitmap *swap_slot;
static struct page **swap_table; // used for copy-on-write.

/* Semaphore for sector allocation. */
static struct lock slot_lock;
static struct lock disk_lock;

/* Initialize the data for anonymous pages */
void vm_anon_init(void) {
  /* Set up the swap_disk. */
  swap_disk = disk_get(1, 1);

  /* Calculate disk size. */
  size_t slots = disk_size(swap_disk) / SEC_PER_PAGE;
  slots = MAX_SLOTS < slots ? MAX_SLOTS : slots;

  /* Create swap slot table. */
  swap_slot = bitmap_create(slots);

  /* Create swap page table: copy-on-write. */
  size_t page_cnt = (slots * sizeof(struct page *)) / 0x1000;
  swap_table = palloc_get_multiple(PAL_ZERO, page_cnt);

  /* Initialize locks. */
  lock_init(&slot_lock);
  lock_init(&disk_lock);
}

/* Find free disk slot. */
static size_t allocate_slot() {
  lock_acquire(&slot_lock);
  size_t slot = bitmap_scan_and_flip(swap_slot, 0, 1, false);
  lock_release(&slot_lock);
  return slot;
}

/* Mark as free slot. */
static void free_slot(size_t slot) {
  lock_acquire(&slot_lock);
  bitmap_set(swap_slot, slot, false);
  lock_release(&slot_lock);
}

์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์Šค์™‘ ํ…Œ์ด๋ธ”์„ ๊ณต์œ ํ•˜๋ฏ€๋กœ ๋ฝ์„ ๊ฑธ์–ด ๋นˆ ์Šฌ๋กฏ์„ ํ• ๋‹น/ํ•ด์ œํ•˜๋Š” ์‹์œผ๋กœ ๊ตฌํ˜„ํ•ด์ฃผ์—ˆ๋‹ค. swap_table์€ copy-on-write์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋‹น์žฅ์€ ํ•„์š”์—†๋‹ค.

/* Swap out the page by writing contents to the swap disk. */
static bool anon_swap_out(struct page *page) {
  struct frame *frame = page->frame;
  void *kva = page->frame->kva;
  ASSERT(frame && kva)


  /* Find free disk sector */
  size_t slot = allocate_slot();
  if (slot == BITMAP_ERROR) {
    printf("Swap disk out of memory.\n");
    return false;
  }

  /* Write to swap disk. */
  disk_sector_t sec = slot * SEC_PER_PAGE;
  do_disk_io(sec, SEC_PER_PAGE, kva, disk_write);

  /* Clear all linked pages. */
  struct thread *t;
  struct page *prev;
  struct list_elem *front;
  while (!list_empty(&frame->pages)) {
    front = list_pop_front(&frame->pages);
    page = list_entry(front, struct page, frame_elem);
    t = page->thread;

    /* Unlink with frame. */
    page->frame = NULL;
    page->flags = page->flags & ~PTE_P;
    pml4_set_accessed(t->pml4, page->va, false);
    pml4_clear_page(t->pml4, page->va);

    /* Link with disk slot. */
    page->anon.slot = slot;
    swap_table_push(slot, page);
    ASSERT(!swap_table_empty(slot));
  }

  return true;
}

/* Swap in the page by read contents from the swap disk. */
static bool anon_swap_in(struct page *page, void *kva) {
  ASSERT(page_get_type(page) == VM_ANON)

  struct frame *frame = page->frame;
  ASSERT(frame && kva)

  size_t slot = page->anon.slot;
  if (slot == SLOT_INIT) {
    /* If page was never swapped out, just install. */
    list_push_back(&frame->pages, &page->frame_elem);
    return vm_install_page(page, page->thread);
  }

  /* Read from disk_sec */
  disk_sector_t sec = slot * SEC_PER_PAGE;
  do_disk_io(sec, SEC_PER_PAGE, kva, disk_read);

  /* Set this page's access bit. */
  pml4_set_accessed(page->thread->pml4, page->va, true);

  /* Set all linked pages present. */
  while (!swap_table_empty(slot)) {
    page = swap_table_pop(slot);
    ASSERT(page != NULL);

    /* Link with frame. */
    page->frame = frame;
    page->flags = page->flags | PTE_P;
    vm_map_frame(page, frame);
    vm_install_page(page, page->thread);

    /* Unlink with disk slot. */
    page->anon.slot = SLOT_INIT;
  }

  /* Mark as free sector. */
  free_slot(slot);
  return true;
}

๊ตฌํ˜„๋œ anon page์˜ swap in/out ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. ํ”„๋ ˆ์ž„ ์•ˆ์— pages ๋ฆฌ์ŠคํŠธ๊ฐ€ ์žˆ๋Š” ๋ถ€๋ถ„์ด ์˜์•„ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๋Š” copy-on-write๋กœ ๋ถ€๋ชจ์™€ ์ž์‹์˜ ํŽ˜์ด์ง€๊ฐ€ ๋™์ผํ•œ ํ”„๋ ˆ์ž„์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ ํ”„๋ ˆ์ž„:ํŽ˜์ด์ง€๊ฐ€ ์ผ๋Œ€๋‹ค ๊ด€๊ณ„๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ frame์€ page์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ–๊ณ  page๋Š” frame์˜ ์ฃผ์†Œ๋ฅผ ์ฐธ์กฐํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ swap out๋˜๋ฉด, ํ”„๋ ˆ์ž„์˜ pages ๋ฆฌ์ŠคํŠธ๋ฅผ ๋Œ๋ฉด์„œ ํ•ด๋‹น ํŽ˜์ด์ง€๋ฅผ ๊ฐ๊ฐ์˜ thread->pml4์—์„œ ํด๋ฆฌ์–ดํ•ด์ฃผ๊ณ , ์ ‘๊ทผ ๋น„ํŠธ๋ฅผ false๋กœ ์„ค์ •ํ•ด์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ํŽ˜์ด์ง€๋“ค์„ swap_table์— ๋ฐฑ์—…ํ•ด๋‘”๋‹ค.

SLOT_INIT ๋งคํฌ๋กœ๋Š” ์ดˆ๊ธฐ anon page์˜ ์Šฌ๋กฏ์„ ์ดˆ๊ธฐํ™”ํ•ด์ค„ ๋•Œ ๋„ฃ๋Š” ๊ฐ’์ธ๋ฐ, ์ด๋Š” ํŠนํžˆ spt table์„ ๋ณต์‚ฌํ•˜๋Š” fork ๊ณผ์ • (copy-on-write๋ผ๋ฉด vm_handle_wp)์—์„œ ์ค‘์š”ํ•˜๋‹ค. ๋ถ€๋ชจ์˜ page๊ฐ€ ์ด๋ฏธ anon์œผ๋กœ ์ดˆ๊ธฐํ™”๋œ ๊ฒฝ์šฐ, ์ž์‹์˜ page์—์„œ vm_do_claim_page์˜ swap_in ์—์„œ ๋ถˆ๋ฆฌ๋Š” ํ•จ์ˆ˜๋Š” uninit_swap_in์ด ์•„๋‹ˆ๋ผ anon_swap_in์ด๋‹ค. ์ด๋•Œ์˜ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•ด์ฃผ๊ธฐ ์œ„ํ•ด slot์ด SLOT_INIT์ธ ์ƒํƒœ์ธ์ง€๋ฅผ ํ™•์ธํ•œ๋‹ค.

๐Ÿ‘€ ์ด ์ฝ”๋“œ์—์„œ swap_table์˜ ์ž๋ฃŒ๊ตฌ์กฐ?

๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” <list.h>์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•ด list์˜ ๋ฐฐ์—ด๋กœ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ, ๋ฆฌ์ŠคํŠธ ๊ตฌ์กฐ์ฒด ํ•˜๋‚˜๋‹น ์ฃผ์†Œ๊ฐ’์ด 4๊ฐœ๊ฐ€ ๋“ค์–ด๊ฐ€๋ฏ€๋กœ 32๋ฐ”์ดํŠธ๊ฐ€ ๋œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์–ด์ฐจํ”ผ ์ค‘๊ฐ„ ์‚ญ์ œ๊ฐ€ ๋งŽ์ง€ ์•Š๊ณ , stack ์ฒ˜๋Ÿผ ํ•œ๋ฐฉํ–ฅ์œผ๋กœ push, pop์„ ์ˆ˜ํ–‰ํ•  ๊ฒƒ์ด๋ผ๋ฉด ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋กœ ๊ตฌํ˜„ํ•ด๋„ ๊ดœ์ฐฎ์ง€ ์•Š์„๊นŒ ์‹ถ์—ˆ๋‹ค. (swap-out์—์„œ๋Š” frame->pages ์— ์†ํ•œ ๋ชจ๋“  ํŽ˜์ด์ง€๋ฅผ ๋‹ค ๋ฝ‘์•„์„œ swap_table[slot]์œผ๋กœ ๋ณด๋‚ด๊ณ , swap-in์—์„œ๋Š” ๊ทธ ๋ฐ˜๋Œ€๋กœ ํ•˜๋ฏ€๋กœ..) ๊ทธ๋ž˜์„œ struct page ์•ˆ์— struct page* ํƒ€์ž…์˜ next_swap ๋ฉค๋ฒ„๋ฅผ ๋„ฃ์–ด ๋†“๊ณ , ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ์˜ ๋งํฌ๋กœ ํ™œ์šฉํ•œ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์Šฌ๋กฏ ํ•˜๋‚˜๋‹น 32๋ฐ”์ดํŠธ ๋Œ€์‹  8๋ฐ”์ดํŠธ๋งŒ ์“ฐ๊ณ , ๋Œ€์‹  ํŽ˜์ด์ง€ ๊ตฌ์กฐ์ฒด ์•ˆ์— struct page* ์ฃผ์†Œ๋ฅผ ๋„ฃ์–ด ๋‘๋ฏ€๋กœ ์ด 16๋ฐ”์ดํŠธ๋กœ.. ์•ฝ ๋‘ ๋ฐฐ ์ •๋„ ๊ณต๊ฐ„์„ ์ ˆ์•ฝํ–ˆ๋‹ค.

+) ๊ทธ๋Ÿฐ๋ฐ ์‚ฌ์‹ค ํŽ˜์ด์ง€์˜ ์ƒํƒœ๋ณ„๋กœ ๋™์‹œ์— ์กด์žฌํ•  ์ˆ˜ ์žˆ๋Š” ํ…Œ์ด๋ธ”์„ ์ƒ๊ฐํ•ด๋ณด๋ฉด, swap-in ๋œ ํŽ˜์ด์ง€๋Š” ํ”„๋ ˆ์ž„์˜ ๋ฆฌ์ŠคํŠธ์— ์—ฎ์—ฌ ์žˆ๊ณ , swap-out๋œ ํŽ˜์ด์ง€๋Š” ์Šค์™‘ ํ…Œ์ด๋ธ”์— ์—ฎ์—ฌ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ”„๋ ˆ์ž„ ๊ตฌ์กฐ์ฒด ์•ˆ์˜ struct list pages๋„ ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋กœ ๊ตฌํ˜„ํ•ด์„œ ์ฃผ์†Œ๊ฐ’ ํ•˜๋‚˜๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋„๋ก ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

ํŽ˜์ด์ง€ ์ƒํƒœ ํ…Œ์ด๋ธ”
uninitialized spt
swap-in spt, frame->pages
swap-out spt, swap_table[slot]
struct page {
  const struct page_operations *operations;
  void *va;            /* Address in terms of user space */
  struct frame *frame; /* Back reference for frame */

  struct hash_elem table_elem; /* Hash elem for spt. */
  struct list_elem frame_elem; /* List elem for frame-mapping. */

  struct page* next_swap;      /* ๐Ÿ”ฅ Singly lisked list for swap-table. */
  struct thread* thread;       /* Thread info. */
  uint16_t flags;              /* Flags. */
  ...
}


/* Push front into swap table. */
static void swap_table_push(size_t slot, struct page *page) {
  ASSERT(slot < MAX_SLOTS)
  lock_acquire(&slot_lock);
  struct page *curr = swap_table[slot];
  page->next_swap = curr;
  swap_table[slot] = page;
  lock_release(&slot_lock);
}

/* Pop front from swap table. */
static struct page *swap_table_pop(size_t slot) {
  ASSERT(slot < MAX_SLOTS)
  lock_acquire(&slot_lock);
  /* Returns NULL if table is empty. */
  struct page *top = swap_table[slot];
  if (top != NULL) {
    swap_table[slot] = top->next_swap;
    top->next_swap = NULL;
  }
  lock_release(&slot_lock);
  return top;
}

/* Returns if swap table is empty. */
static bool swap_table_empty(size_t slot) {
  ASSERT(slot < MAX_SLOTS)
  bool rtn;
  lock_acquire(&slot_lock);
  rtn = (swap_table[slot] == NULL);
  lock_release(&slot_lock);
  return rtn;
}

/* Remove page from swap table. */
static struct page *swap_table_remove(size_t slot, struct page *page) {
  ASSERT(slot != SLOT_INIT);

  lock_acquire(&slot_lock);
  struct page *before = swap_table[slot];
  if (before == page) {
    lock_release(&slot_lock);
    return swap_table_pop(slot);
  }

  while (before->next_swap != page) {
    before = before->next_swap;
    if (before == NULL) {
      /* Page is not found. */
      lock_release(&slot_lock);
      return NULL;
    }
  }
  before->next_swap = page->next_swap;
  lock_release(&slot_lock);
  return page;
}

๋‹จ์ ์ด๋ผ๋ฉด, swap out๋œ ํŽ˜์ด์ง€๊ฐ€ destroy ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ƒ๊ธด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด swap_table[slot] ๋ฆฌ์ŠคํŠธ์—์„œ๋„ ์‚ญ์ œํ•ด ์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ.. SLL์ด๋ผ ์ค‘๊ฐ„ ์‚ญ์ œ์˜ ๊ฒฝ์šฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•œ๋ฒˆ ๊ฒ€์ƒ‰ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์ด๋•Œ $O(n)$์˜ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฐ๋‹ค. ์—ฌ๊ธฐ์„œ n์€ ํ•˜๋‚˜์˜ ํ”„๋ ˆ์ž„์„ ๊ณต์œ ํ•˜๊ณ  ์žˆ๋Š” ํŽ˜์ด์ง€์˜ ์ˆ˜์ด๋‹ค.

06) Copy-on-write

์–ด๋ ต๋‹ค.. ์–ด๋ ค์šด๋ฐ? .. ์–ด๋ ต๋„ค..

์•„์ด๋””์–ด๋Š” ๋ถ€๋ชจ์™€ ์ž์‹์˜ ํŽ˜์ด์ง€ ๋ณต์‚ฌ ์—ญ์‹œ lazyํ•˜๊ฒŒ ํ•˜๊ฒ ๋‹ค๋Š” ๊ฑฐ๋‹ค. fork ์ฝœ๋กœ ์ธํ•ด ์ฒ˜์Œ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์„ ๋ณต์‚ฌํ•  ๋•Œ, ๋ถ€๋ชจ์™€ ์ž์‹์˜ ํŽ˜์ด์ง€๋Š” ๊ฐ™์€ ํ”„๋ ˆ์ž„๊ณผ ๋งคํ•‘๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์–ด๋Š ํ•œ์ชฝ์ด ํ”„๋ ˆ์ž„์„ ์ˆ˜์ •ํ•˜๋ ค๊ณ  ํ•  ๋•Œ ๋น„๋กœ์†Œ ์ƒˆ๋กœ์šด ํ”„๋ ˆ์ž„์ด ํ• ๋‹น๋˜์–ด ๋ณต์ œ๋œ๋‹ค. ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด write-protect page์˜ fault๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  1. spt copy์—์„œ ๋ถ€๋ชจ์™€ ์ž์‹์˜ ํŽ˜์ด์ง€๋ฅผ read-only๋กœ ์„ค์ •ํ•œ๋‹ค.
  2. ๋งŒ์•ฝ read-only ํŽ˜์ด์ง€์— ๋Œ€ํ•œ write fault๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ๋ณต์ œํ•œ๋‹ค.

๐Ÿšจ ์ฃผ์˜: ์•„์ง ๊ฐœ์„ ํ•ด์•ผ ํ•  ์ ์ด ๋งŽ์€ ์˜ค๋ฅ˜์ฝ”๋“œ์ž„


/* Hash action function which copies a single page. */
static void spt_copy_page(struct hash_elem *e, void *aux) {
  struct supplemental_page_table *dsc_spt =
      (struct supplemental_page_table *)aux;
  struct hash *dsc_hash = &dsc_spt->hash;
  struct page *src_page = hash_entry(e, struct page, table_elem);

  struct page *dsc_page = calloc(1, sizeof(struct page));
  if (dsc_page == NULL) {
    printf("Child page allocation failed\n");
    return;
  }

  /* Copy spt entries. */
  memcpy(dsc_page, src_page, sizeof(struct page));

  /* Disconnect from parent's hash table. */
  memset(&dsc_page->table_elem, 0, sizeof(struct hash_elem));
  memset(&dsc_page->frame_elem, 0, sizeof(struct list_elem));
  hash_insert(dsc_hash, &dsc_page->table_elem);

  /* Set new values. */
  dsc_page->next_swap = NULL;
  dsc_page->thread = thread_current();

  switch (page_get_type(src_page)) {
    case VM_ANON:
      if (!pg_present(src_page)) {
        if (!pg_init(src_page)) {
          /* Uninitialized anon pages are segment pages. */
          struct file_info *src_aux = src_page->uninit.aux;
          struct file_info *dsc_aux = calloc(1, sizeof(struct file_info));
          if (dsc_aux == NULL) {
            printf("Child uninit page aux allocation failed\n");
            return;
          }
          memcpy(dsc_aux, src_aux, sizeof(struct file_info));
          dsc_page->uninit.aux = dsc_aux;
          return;
        }

        /* Swapped out anon page. */
        size_t slot = dsc_page->anon.slot;
        anon_swap_table_push(slot, dsc_page);

      } else {
        /* Present anon page. */
        vm_map_frame(dsc_page, src_page->frame);
      }
      break;

    case VM_FILE:
      if (!pg_present(src_page)) {
        /* Uninitialized file-backed page. */
        if (!pg_init(src_page)) {
          struct file_page *src_aux = src_page->uninit.aux;
          struct file_page *dsc_aux = calloc(1, sizeof(struct file_page));
          if (dsc_aux == NULL) {
            printf("Child uninit page aux allocation failed\n");
            return;
          }

          memcpy(dsc_aux, src_aux, sizeof(struct file_page));
          dsc_page->uninit.aux = dsc_aux;
          spt_copy_file(src_page, dsc_page);

          /* Return immediately. */
          return;
        }

        /* Swapped out file-backed page. */
        size_t slot = dsc_page->file.slot;
        file_swap_table_push(slot, dsc_page);

      } else {
        /* Present file-backed page. */
        vm_map_frame(dsc_page, src_page->frame);
      }

      /* Copy file if the page is head-page. */
      spt_copy_file(src_page, dsc_page);
      break;

    default:
      break;
  }

  /* Set write-protect. */
  src_page->flags = src_page->flags & ~PTE_W;
  dsc_page->flags = dsc_page->flags & ~PTE_W;

  /* Set copy-on-write flag. */
  src_page->flags = src_page->flags | PG_COW;
  dsc_page->flags = dsc_page->flags | PG_COW;

  /* Install in pml4 if swap-in pages. */
  if (pg_present(src_page)) {
    vm_install_page(src_page, src_page->thread);
    vm_install_page(dsc_page, dsc_page->thread);
  }
}

๊ธธ๋‹ค.. case๋ฌธ 2๊ฐœ๋Š” ํ”„๋กœ์ ํŠธ 3์˜ ์ฃผ์š” ๋‘ ๊ฐœ ๋ฉ”๋ชจ๋ฆฌ ํƒ€์ž…์ธ anon๊ณผ file๋กœ ๋‚˜๋ˆ„์–ด์ ธ ์žˆ๊ณ , ๊ฐ๊ฐ์˜ case์•ˆ์—์„œ 1. uninit์ธ ๊ฒฝ์šฐ, 2. swap-out์ธ ๊ฒฝ์šฐ, 3. swap-in ์ธ ๊ฒฝ์šฐ๋กœ ๋‚˜๋‰œ๋‹ค. in๋˜์–ด ์žˆ๋Š” ํŽ˜์ด์ง€์˜ ๊ฒฝ์šฐ์—๋Š” installํ•˜๋ฉด์„œ ๋น„ํŠธ๋ฅผ & ~PTE_W์œผ๋กœ ์„ค์ •ํ•ด ์ค€๋‹ค.

/* Page Fault Handler: Return true on success */
bool vm_try_handle_fault(struct intr_frame *f, void *addr, bool user,
                         bool write, bool not_present) {

  ...

  /* If page is write-protect, return handle_wp. */
  if (write && !pg_writable(page)) {
    return vm_handle_wp(page);
  }

  ...
}

/* Handle the fault on write_protected page */
// TODO: uninit page๋„ handleํ•˜๊ฒŒ ?
bool vm_handle_wp(struct page *page) {
  if (!pg_copy_on_write(page)) {
    /* If cow-flag is not on,
     * this page is write-protect page. */
    return false;
  }

  /* Unlink from old frame. */
  struct frame *old_frame = page->frame;
  vm_unmap_frame(page);

  /* If this page was the last,
   * Re-link with frame and return. */
  if (list_empty(&old_frame->pages)) {
    page->flags = page->flags | PTE_W;
    page->flags = page->flags & ~PG_COW;

    vm_map_frame(page, old_frame);
    return vm_install_page(page, page->thread);
  }

  /* Link with new frame. */
  struct frame *new_frame;
  /* Copy-on-write pages are all present. */
  if (pg_present(page)) {
    /* Mark as no copy-on-write page. */
    page->flags = page->flags | PTE_W;
    page->flags = page->flags & ~PG_COW;

    if (!vm_do_claim_page(page)) {
      /* If swap-in fails, return false.*/
      return false;
    }

    /* Page is now swapped-in. Copy old frame. */
    new_frame = page->frame;
    memcpy(new_frame->kva, old_frame->kva, PGSIZE);
    return true;
  }
  return false;
}

PG_COW๋กœ ์„ค์ •๋œ ํŽ˜์ด์ง€๋Š” ์œ„์™€ ๊ฐ™์ด vm_try_handle_fault ์ฒ˜๋ฆฌ ์ค‘ ๋ณ„๋„๋กœ ํ•ธ๋“ค๋งํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ง€๊ธˆ ๋ณด๋‹ˆ swap-out๋œ ํŽ˜์ด์ง€๋„ ๋น„ํŠธ ์„ค์ •์„ ํ•ด ์ฃผ์–ด์•ผ ํ•  ๊ฒƒ ๊ฐ™๋‹ค.. copy-on-write์˜ ๊ฒฝ์šฐ extra ์ตœ๊ฐ•์ž๋‹ต๊ฒŒ ๋‚œ์ด๋„๊ฐ€ ๋†’์€ ๊ฒƒ ๊ฐ™๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ง€๊ธˆ๊นŒ์ง€์˜ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋ฐ”๊ฟ”์•ผ ํ•ด์„œ ๋ง‰๋ฐ”์ง€์— ์ฝ”๋“œ๋ฅผ ์™„์„ฑํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์› ๋‹ค. (๋ฌผ๋ก  ์ด๊ฒƒ๊นŒ์ง€ ์•ˆ ํ•ด๋„ ๋Œ€์ถฉ cow-simple ํ…Œ์ŠคํŠธ๋งŒ ํ†ต๊ณผํ•˜๊ฒŒ๋” ์ฒ˜๋ฆฌํ•ด๋‘˜ ์ˆ˜๋Š” ์žˆ๋‹ค.) ํ•œ ๊ฐ€์ง€ ๋” ์–ด๋ ค์› ๋˜ ๋ถ€๋ถ„์€: read-only ์„ค์ •์œผ๋กœ ์ธํ•ด fault์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉด์„œ ์ด๊ณณ์ €๊ณณ์— ๊ฑธ์–ด ๋‘์—ˆ๋˜ ๋ฝ๊ณผ ์„ธ๋งˆํฌ์–ด๋กœ ์ธํ•œ ๋™๊ธฐํ™” ๋ฌธ์ œ๊ฐ€ ํ„ฐ์ง€๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค๋Š” ์ ์ด๋‹ค. ์ฝ”๋“œ์˜ ํ๋ฆ„์„ ์ •ํ™•ํ•˜๊ฒŒ ์•Œ๊ณ  ์žˆ์–ด์•ผ ๊ฐ€๊ธ‰์  ์ข์€ ๋ฒ”์œ„์—์„œ, ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ๋ฝ์„ ๊ฑธ์–ด ์ž์› ์ ‘๊ทผ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. ๊ธฐํšŒ๊ฐ€ ๋œ๋‹ค๋ฉด ํž˜ ๋‹ฟ๋Š” ๋ฐ๊นŒ์ง€ ์กฐ๊ธˆ ๋” ๋ณด์™„ํ•˜๊ณ  ์‹ถ์€ ์ ์ด๋‹ค.