DevLog πŸ“¨

[DevLog][PintOS] PRJ2 User Program/Out of Memory Test

cece00 2023. 10. 10. 09:33

Memory Leak in C

ν•€ν† μŠ€μ˜ Project2μ—λŠ” OOM ν…ŒμŠ€νŠΈκ°€ μžˆλŠ”λ°, ν• λ‹Ή κ°€λŠ₯ν•œ μžμ›μ„ λͺ¨λ‘ μ†Œμ§„ν•  λ•ŒκΉŒμ§€ ν”„λ‘œμ„ΈμŠ€λ₯Ό forkν•˜λŠ” λ¬΄μ‹œλ¬΄μ‹œν•œ ν…ŒμŠ€νŠΈμ΄λ‹€. ν”„λ‘œμ„ΈμŠ€λ₯Ό μƒμ„±ν•˜κ³  μ’…λ£Œμ‹œν‚€λŠ” 과정을 10회 λ°˜λ³΅ν•˜λ―€λ‘œμ„œ fork κ°€λŠ₯ν•œ ν”„λ‘œμ„ΈμŠ€μ˜ μˆ˜κ°€ 맀번 같은지, 즉 μ’…λ£Œ κ³Όμ •μ—μ„œ λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”μ§€ μ²΄ν¬ν•œλ‹€. μš΄μ˜μ²΄μ œμ™€ μ‹œμŠ€ν…œ 콜의 μš”κ΅¬μ‚¬ν•­μ— λŒ€ν•΄ μ•Œκ³ , κ΅¬ν˜„ν•˜λ©΄ passλ˜μ—ˆλ˜ ν…ŒμŠ€νŠΈμ™€λŠ” 달리, 이 ν…ŒμŠ€νŠΈλŠ” μ‹€μ œλ‘œ μ½”λ“œκ°€ μ•ˆμ „ν•˜κ²Œ (λˆ„μˆ˜ 없이) μ§œμ—¬μ Έ μžˆλŠ”μ§€λ₯Ό ν™•μΈν•˜κΈ° λ•Œλ¬Έμ— 무엇보닀도 λ…Όλ¦¬μ μœΌλ‘œ.. μ½”λ“œλ₯Ό 잘 μ§œλŠ” μ—­λŸ‰μ΄ μ€‘μš”ν–ˆλ˜ 것 κ°™λ‹€.

1) λ©”λͺ¨λ¦¬ ν• λ‹Ή ν•¨μˆ˜λ₯Ό μ“°λ©΄, 잘 ν•΄μ œν•΄ 주자.

tid_t thread_create(...) {
  struct thread_child *child;
  struct thread *t;
  tid_t tid;

  ASSERT(function != NULL);

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

  ...

  /* Add to parent's child list. */
  child = calloc(1, sizeof(struct thread_child));
  if (child == NULL) {
    palloc_free_page(t); // πŸ”₯ μ—¬κΈ°
    return TID_ERROR;
  }
  ...

λ‹Ήμ—°ν•œ λ§μ΄μ§€λ§Œ, μ• μ΄ˆμ— 이 뢀뢄을 μ‹ κ²½μ“°λ©΄μ„œ μ½”λ“œλ₯Ό μ§œμ§€ μ•ŠμœΌλ©΄ λˆ„μˆ˜ 지점을 μ°ΎκΈ° μ–΄λ €μ›Œμ§€λŠ” κ²½μš°κ°€ λ§Žμ•˜λ‹€. 18번 λΌμΈμ—μ„œ palloc_free_page(t)λ₯Ό 해주지 μ•ŠμœΌλ©΄ λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ λ°œμƒν•œλ‹€. 였λ₯˜κ°€ λ°œμƒν–ˆμ„ λ•Œ μ˜ˆμ™Έ 처리λ₯Ό ν•˜λ©΄μ„œ 이전에 ν• λ‹Ήλ°›μ•˜λ˜ λ©”λͺ¨λ¦¬λ₯Ό 잘 ν•΄μ œν•΄ μ£Όμ–΄μ•Ό ν•œλ‹€. ν•¨μˆ˜κ°€ λ¦¬ν„΄ν•˜λŠ” λͺ¨λ“  μΌ€μ΄μŠ€μ—μ„œ ν•΄μ œν•΄ μ£ΌλŠ” 것을 μžŠμ§€ 말자.

2) λ©”λͺ¨λ¦¬ ν• λ‹Ή μ—¬λΆ€λ₯Ό 확인할 수 μžˆλ„λ‘ ν•¨μˆ˜λ₯Ό 짜자.

/* General process initializer for initd and other process. */
static bool process_init(void) {
  struct thread *curr = thread_current();

  /* Set as user task. */
  curr->mode = USER_TASK;

  /* Create file descriptor table. */
  curr->fdt = palloc_get_page(PAL_ZERO);
  if (curr->fdt == NULL) return false;  // πŸ”₯ μ—¬κΈ°
  return true;
}

λ‹€μŒκ³Ό 같이, ν•¨μˆ˜ λ‚΄μ—μ„œ λ©”λͺ¨λ¦¬ 할당을 ν•  경우, ν• λ‹Ή 성곡/μ‹€νŒ¨μ— 따라 bool λ˜λŠ” int λ°˜ν™˜κ°’μ„ λ°˜ν™˜ν•΄ μ£Όμ–΄μ„œ ν•¨μˆ˜λ₯Ό λΆ€λ₯΄λŠ” μͺ½μ—μ„œ ν• λ‹Ή 여뢀에 λŒ€ν•΄ μ•Œ 수 μžˆλ„λ‘ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” 것이 μ’‹λ‹€. malloc, palloc, calloc λ“±μ˜ ν•¨μˆ˜λ₯Ό μ“Έ λ•ŒλŠ” λ°˜λ“œμ‹œ μ‹€νŒ¨ κ°€λŠ₯성을 염두에 두어야 ν•œλ‹€. (이전에 Cλ₯Ό 처음 λ°°μš°λ©΄μ„œ, 'malloc으둜 할당받은 포인터에 λŒ€ν•œ NULL checkλ₯Ό μŠ΅κ΄€μ μœΌλ‘œ 해라' 라고만 λ°°μ› λŠ”λ°, κ·Έ μ΄μœ μ™€ ν•„μš”μ„±μ„ μ΄ν•΄ν•˜κΈ°λ³΄λ‹€λŠ” κΈ°κ³„μ μœΌλ‘œ 적용만 ν•˜κ³  μžˆμ—ˆλ‹€. Pintos ν”„λ‘œμ νŠΈλ₯Ό ν•˜λ©΄μ„œ μ‹€μ œ ν• λ‹Ή μ‹€νŒ¨μ˜ 상황을 μ˜ˆμ™Έμ²˜λ¦¬ν•˜λŠ” 것이 정말 λ„ˆλ¬΄λ„ˆλ¬΄λ‚˜.. μ€‘μš”ν•˜λ‹€λŠ” 것을 체감할 수 μžˆμ—ˆλ‹€.)

3) μ˜ˆμ™Έ 처리의 μ‹€ν–‰ 지점을 잘 λ°°μΉ˜ν•˜μž.

int dup2(int oldfd, int newfd) {
  if (!is_valid_fd(oldfd)) return -1;
  if (!is_valid_fd(newfd)) return -1;

  struct file **fdt = thread_current()->fdt;

  if (oldfd == newfd) return newfd;
  if (fdt[oldfd] == NULL) return -1;

  /* If newfd already has a file, close. */
  if (fdt[newfd]) {
    close(newfd);
  }

  /* If standard i/o, just copy value. */
  if (is_file_std(fdt[oldfd])) {
    fdt[newfd] = fdt[oldfd];
    return newfd;
  }
  ...

μœ„ ν•¨μˆ˜μ—μ„œ standard I/O μ‹λ³„μžμ˜ 값을 λ³΅μ‚¬ν•΄μ£ΌλŠ” λΆ€λΆ„κ³Ό, fdt[newfd]κ°€ μ—΄λ € μžˆλŠ” 파일이라면 λ¨Όμ € λ‹«μ•„ μ£ΌλŠ” 뢀뢄이 μˆœμ„œκ°€ λ°”λ€Œμ–΄ μžˆλ‹€λ©΄ λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ λ°œμƒν•œλ‹€. μ¦‰μ‹œ λ¦¬ν„΄ν•˜λ„λ‘ μ˜ˆμ™Έ 처리λ₯Ό ν•  경우 νŠΉνžˆλ‚˜ 리턴 전에 μžμ›μ΄ λͺ¨λ‘ ν• λ‹Ή ν•΄μ œλ˜μ—ˆλŠ”μ§€ ν™•μΈν•˜λŠ” 것이 μ€‘μš”ν•˜λ‹€.

μ„Έλ§ˆν¬μ–΄, 락과 같은 동기화 λ©”μ»€λ‹ˆμ¦˜ μ—­μ‹œ μœ μ‚¬ν•˜λ‹€. 정상적인 μ‹œλ‚˜λ¦¬μ˜€λΏλ§Œ μ•„λ‹ˆλΌ μ˜ˆμ™Έ 처리 λ‘œμ§μ—μ„œλ„ 락을 잘 releaseν•  수 μžˆλ„λ‘ ν•΄μ•Ό ν•œλ‹€. 특히, ν”„λ‘œμ„ΈμŠ€κ°€ 비정상적인 였λ₯˜ (ex. segmentation fault)둜 μ’…λ£Œλ  경우, κ·Έ 전에 μ†Œμœ ν•˜κ³  있던 λ©”λͺ¨λ¦¬, 락, μ„Έλ§ˆν¬μ–΄ 같은 μžμ›μ„ λͺ¨λ‘ λ°˜ν™˜ν•˜λ„λ‘ μž‘μ„±ν•΄μ•Ό μ•ˆμ „ν•˜λ‹€.

πŸ˜‡ 무엇을 λŠκΌˆλ‚˜μš”

μ΄μ „μ˜ 디버깅 λ°©λ²•μœΌλ‘œλŠ” λˆ„μˆ˜ 지점을 μ°ΎκΈ° νž˜λ“€λ‹€λŠ” 점이 κ°€μž₯ λ‚œκ°ν•œ λΆ€λΆ„μ΄μ—ˆλ‹€. Faultκ°€ λ‚˜κ±°λ‚˜, μ˜λ„ν–ˆλ˜ μ‹€ν–‰ μ§€μ κΉŒμ§€ μ½”λ“œκ°€ μ‹€ν–‰λ˜μ§€ μ•Šμ€ 경우 logλ₯Ό μ°μ–΄λ³΄λ©΄μ„œ λ””λ²„κΉ…ν–ˆκ³ , 보닀 λ³΅μž‘ν•œ 둜직의 경우 gdbλ₯Ό ν™œμš©ν•΄ ν•œ 쀄 ν•œ 쀄 값을 검사할 수 μžˆμ—ˆλ‹€. ν•˜μ§€λ§Œ λ©”λͺ¨λ¦¬ λˆ„μˆ˜μ˜ 경우, μ½”λ“œλ₯Ό 눈으둜 훑어보며 λ‘œμ§μ„ 체크해야 ν’€λ¦¬λŠ” 뢀뢄이 μžˆμ—ˆλ˜ 것 κ°™λ‹€. (특히 Out of Memory ν…ŒμŠ€νŠΈλŠ” λ©”λͺ¨λ¦¬κ°€ ν—ˆμš©ν•˜λŠ”λ§ŒνΌ forkλ₯Ό ν•˜κΈ° λ•Œλ¬Έμ—, λ””λ²„κ±°λ‘œ λ”°λΌκ°€κΈ°μ—λŠ” 무리가 μžˆμ—ˆλ‹€.) μ½”λ“œμ—μ„œ 잘λͺ»λœ λ‘œμ§μ„ λ°œκ²¬ν•˜κ³  μˆ˜μ •ν•˜λŠ” 것에 μ‹œκ°„μ΄ 생각보닀 훨씬 많이 κ±Έλ ΈλŠ”λ°, 결과적으둜 μž₯μž₯ 5μ‹œκ°„(+a)의 디버깅 끝에 λ°œκ²¬ν•œ λˆ„μˆ˜ 지점은 λ‹€μŒκ³Ό κ°™μ•˜λ‹€.

/* Open a new file. */
int open(const char *file_name) {
  if (file_name == NULL) return -1;
  struct thread *curr = thread_current();

  int fd = allocate_fd();

  struct file *file = filesys_open(file_name);
  if (!is_valid_fd(fd)) return -1; // πŸ”₯ μ—¬κΈ°

  if (file == NULL) {
    free_fd(fd);
    return -1;
  }
  curr->fdt[fd] = file;

  return fd;
}

ν‘œμ‹œλœ 라인의 μ½”λ“œλ₯Ό ν•œ μ€„λ§Œ μœ„λ‘œ 올리면 λˆ„μˆ˜κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€. file descriptor table이 λͺ¨λ‘ ν• λ‹Ήλœ 경우 allocate_fd()λŠ” -1을 λ°˜ν™˜ν•˜λŠ”λ°, 이 κ²½μš°μ—λŠ” μ •μƒμ μœΌλ‘œ νŒŒμΌμ„ 열지 λͺ»ν–ˆμœΌλ―€λ‘œ -1을 λ¦¬ν„΄ν•˜λ„λ‘ μ½”λ“œλ₯Ό μž‘μ„±ν–ˆλ‹€. λ¬Έμ œλŠ” κ·Έ 전에 μ—΄μ–΄ λ‘” νŒŒμΌμ„ λ‹«λŠ” 뢀뢄이 μ—†λ‹€λŠ” 것이닀. λ”°λΌμ„œ fd값에 λŒ€ν•œ validation을 λ¨Όμ € ν•˜κ³ , νŒŒμΌμ„ 열도둝 ν•΄μ•Ό ν•œλ‹€. 일반적으둜 ν•˜λ‚˜μ˜ μžμ›μ„ ν• λ‹Ήν–ˆμœΌλ©΄, μ¦‰μ‹œ ν• λ‹Ή 여뢀에 따라 μ˜ˆμ™Έμ²˜λ¦¬λ₯Ό ν•˜λ„λ‘ μ½”λ“œλ₯Ό μ§œλŠ” 것이 쒋을 것 κ°™λ‹€.

μ΄μ „μ˜ ν…ŒμŠ€νŠΈμ—μ„œλŠ” 'λŒμ•„κ°€λŠ” μ½”λ“œ'λ₯Ό ν‰κ°€ν–ˆλ‹€λ©΄, Out of Memory ν…ŒμŠ€νŠΈλŠ” μ½”λ“œμ˜ μ•ˆμ „μ„±μ„ ν‰κ°€ν•œλ‹€λŠ” μ μ—μ„œ κΉŒλ‹€λ‘œμ› λ˜ 것 κ°™λ‹€. μžμ› ν• λ‹Ή/ν•΄μ œλ₯Ό 잘 κ³ λ €ν•˜λ©° μ½”λ“œλ₯Ό μ§œλŠ” 것이 이미 μž‘μ„±λœ μ½”λ“œμ—μ„œ λˆ„μˆ˜ 지점을 λ””λ²„κΉ…ν•˜λŠ” 것보닀 훨씬 μ‰½λ‹€λŠ” μ μ—μ„œ, μ• μ΄ˆμ— μ½”λ“œλ₯Ό 잘 μ§œλŠ” 것이 μ’‹λ‹€. 특히 1. ν•¨μˆ˜κ°€ λ¦¬ν„΄λ˜λŠ” λͺ¨λ“  μƒν™©μ—μ„œ ν• λ‹Ήλœ μžμ›μ˜ ν•΄μ œκ°€ 이루어져야 ν•  수 μžˆλ‹€λŠ” 것과 2. ν• λ‹Ήμ˜ 성곡/μ‹€νŒ¨ μ—¬λΆ€λ₯Ό λ°˜λ“œμ‹œ μ²΄ν¬ν•˜κ³  λ‘œμ§μ„ μ§œμ•Ό ν•œλ‹€λŠ” 것을 κΌ­ κΈ°μ–΅ν•˜μž. 할당에 μ‹€νŒ¨ν•  경우 둜그λ₯Ό λ‚¨κΈ°λŠ” 것도 방법인 것 κ°™λ‹€.


oom ν…ŒμŠ€νŠΈλ₯Ό λ§ˆμ§€λ§‰μœΌλ‘œ.. μ–΄μ°Œμ €μ°Œ Project2의 ν…ŒμŠ€νŠΈλ₯Ό all pass ν–ˆλ‹€.. test caseκ°€ λΆ€μ‹€ν•œ κ²½μš°κ°€ λ§Žμ€ 것 κ°™μ§€λ§Œ μ–΄μ¨Œλ“  톡과..