jun-wiki

View My GitHub Profile

Posts (Latest 10 updated) :
Read all
Contents:
  1. 시스템콜
    1. open
    2. close
    3. filesize
    4. seek
    5. tell
    6. create
    7. remove
    8. 헬퍼
      1. copy_in_string
      2. fd_alloc
      3. fd_ensure_table
      4. fd_get

오늘의 TIL

시스템콜 중 간단한 거 구현할 생각이다

사실 안간단하다


시스템콜

open

이거 하나 만드는데도 종일 걸렸다

filesys_open 함수 쓰면 통과되긴 하는데

전부 통과되지가 않고 bad-ptr나 missing 같은 실패시의 케이스가 원하는 대답 안나온다

이거 해결하면서 하는게 개빡이다

static int system_open(const char *file) {
  char kname[NAME_MAX + 1];
  if (!copy_in_string(kname, file, sizeof kname))
    return -1;

  lock_acquire(&filesys_lock);
  struct file *f = filesys_open(kname);
  lock_release(&filesys_lock);
  if (f == NULL) return -1;

  int fd = fd_alloc(f);
  if (fd < 0) {
    lock_acquire(&filesys_lock);
    file_close(f);
    lock_release(&filesys_lock);
    return -1;
  }
  return fd;
}

자그마치 3개!의 헬퍼함수의 콤-비네이션이다

맨 위부터 살펴보자


static int system_open(const char *file) {
  char kname[NAME_MAX + 1];
  if (!copy_in_string(kname, file, sizeof kname))
    return -1;

일단 값받아서 파일 검사 들어간다

copy_in_string이 하는 역할이 중요하다

저걸로 문자열 잘 읽어 온다 실패시 -1 리턴 해주고 말이다


  lock_acquire(&filesys_lock);
  struct file *f = filesys_open(kname);
  lock_release(&filesys_lock);
  if (f == NULL) return -1;

그리고 락 걸어 안전하게 파일 시스템에 접근해 오픈해서 f에 저장한다
만약 NULL이라면 (오픈 실패) -1 리턴해준다


  int fd = fd_alloc(f);
  if (fd < 0) {
    lock_acquire(&filesys_lock);
    file_close(f);
    lock_release(&filesys_lock);
    return -1;
  }
  return fd;
}

fd_alloc이 핵심이다

기본적으로 유저로 하여금 직접 커널 건드리게 하면 안되니까

중간 매개체로 사용할 fd 핸들러 쥐어주는 역할이다

그리고 fd 값 이상하면 닫고 -1 반환


close

열었으면 닫아야 한다

그 중 닫아주는 역할을 맡고 있다

static void system_close(int fd) {
  struct thread *t = thread_current();

  if (fd < 0 || fd >= t->fd_cap) return;   // 범위 밖
  struct file *f = t->fd_table[fd];
  if (f == NULL) return;

  t->fd_table[fd] = NULL;                  // 먼저 테이블에서 제거
  lock_acquire(&filesys_lock);
  file_close(f);
  lock_release(&filesys_lock);
}

얜 간단하다 fd 범위 올바른지랑 실존하는지 확인하고

테이블에서 제거 후 file_close호출해 닫으면 끝!


filesize

더 쉽다

static int
system_filesize(int fd) {
  if (fd == 1 || fd == 0) return -1;
  struct file *f = fd_get(fd);
  if (f == NULL) return -1;

  lock_acquire(&filesys_lock);
  off_t len = file_length(f);
  lock_release(&filesys_lock);
  return (int)len;
}

fd 올바른지 확인하고

file_length 호출하면 끝이다

한가지 유의할 점은 처음에 fd_get으로 fd 획득하는거다


seek

위에랑 똑같은 흐름이다

차이점은 void라 리턴값 없다

static void
system_seek(int fd, unsigned position) {
  if (fd == 1 || fd == 0) return;
  struct file *f = fd_get(fd);
  if (f == NULL) return;

  lock_acquire(&filesys_lock);
  file_seek(f, position);
  lock_release(&filesys_lock);
}

tell

얘는 아예 똑같은 흐름이다

static unsigned
system_tell(int fd) {
  if (fd == 1 || fd == 0) return -1;
  struct file *f = fd_get(fd);
  if (f == NULL) return -1;
  
  lock_acquire(&filesys_lock);
  off_t pose = file_tell(f);
  lock_release(&filesys_lock);
  return pose;
}


create

흐름 거의 똑같은데

처음에 제대로 문자열 받기위해 헬퍼 함수 쓴다

copy_in_string이라는 문자열 받는 함수다

static bool
system_create(const char *file, unsigned initial_size) {
  char kname[NAME_MAX + 1];

  bool ok = copy_in_string(kname, file, sizeof kname);
  if (!ok) {
    return false;
  }
  
  lock_acquire(&filesys_lock);
  bool crt = filesys_create(kname, initial_size);
  lock_release(&filesys_lock);
  return crt;
}


remove

창조했으니 파괴도 해줘야한다

static bool
system_remove(const char *file) {
  char kname[NAME_MAX + 1];
  if (!copy_in_string(kname, file, sizeof kname))
    return false;

  lock_acquire(&filesys_lock);
  bool rem = filesys_remove(kname);
  lock_release(&filesys_lock);
  return rem;
}

이상한거 주는지 copy_in_string으로 검사해주고

문제 없으면 filesys_remove호출해서 제거해준다


헬퍼

copy_in_string

static bool
copy_in_string(char *kdst, const char *usrc, size_t max_len) {
  if (usrc == NULL) system_exit(-1);

  size_t i = 0;
  while (i < max_len) {
    if (!is_user_vaddr(usrc + i)) system_exit(-1);
    const char *k = pml4_get_page(thread_current()->pml4, usrc + i); //매핑된 가상 주소 얻기
    if (k == NULL) system_exit(-1);

    char c = *k;
    kdst[i++] = c;
    if (c == '\0') return true;  // 정상 종료: 제한 내에서 NUL 발견
  }

  kdst[max_len - 1] = '\0';      // 끝문자열 null 처리 (최대 길이 벗어나서 절삭)
  return false;
}

역할은 문자열이 max_len보다 긴지랑 끝이 nul로 잘 끝나는지 검사하고

이를 커널 버퍼에 복사해주는 역할이다

쪼개서 보자


static bool
copy_in_string(char *kdst, const char *usrc, size_t max_len) {
  if (usrc == NULL) system_exit(-1);

포인터 이상하면 종료


  size_t i = 0;
  while (i < max_len) {
    if (!is_user_vaddr(usrc + i)) system_exit(-1);

is_user_vaddr은 유저 영역 주소인지 확인하는 함수로
영역 아니면 종료


    const char *k = pml4_get_page(thread_current()->pml4, usrc + i);
    if (k == NULL) system_exit(-1);

pml4_get_page로 매핑된 가상주소 받아온다
못받으면 당연히 종료


    char c = *k;
    kdst[i++] = c;
    if (c == '\0') return true;
  }

매핑 된거 읽고 커널에 저장한다
만약 nul 문자면 안전하게 다 읽은거니 true 리턴


  kdst[max_len - 1] = '\0';
  return false;
}

여기까지 왔다는 건 최대 길이 벗어났다는 거니..
범위 끝 그냥 nul문자로 매꿔버리고 false 리턴



fd_alloc

시스템콜 open 같은 거 쓸때에는 커널 객체(file)를 직접 유저에게 맡기면 안되기에 쓴다

그 대신 파일 디스크립터를 줘야 하는데 이 파일 디스크립터를 할당해주는게 이녀석의 역할이다

커널 객체 직접 만드는 대신 이를 다룰 핸들러를 만들어주기 말이다

스레드의 fd테이블에서 찾아서 만들어주는데 슬롯 꽉차면 확장한다

물론, 여기선 구현안했다

그거는 나중에 짬나면 할듯?

static int fd_alloc(struct file *f) {
  struct thread *t = thread_current();
  if (!fd_ensure_table()) return -1;

  for (int i = FIRST_FD; i < t->fd_cap; i++) {
    if (t->fd_table[i] == NULL) {
      t->fd_table[i] = f;
      return i;
    }
  }
  return -1;
}

로직 뭐 볼거도 없다

fd_table 스캔하다가 빈곳 발견하면 거기 쓰는거다



fd_ensure_table

저기 위에 보면 !fd_ensure_table이라고 썼는데

말그대로 fd table 보장해준다

없으면 만들어주고 있으면 그냥 쓴다

성공시 true, 실패시 false 반환

static bool fd_ensure_table(void) {
  struct thread *t = thread_current();
  /* 있으면 그냥 쓰기 */
  if (t->fd_table && t->fd_cap > 0) return true;

  /* 초기 테이블 생성 */
  int cap = FD_GROW_STEP;
  struct file **newtab = (struct file **)palloc_get_page(PAL_ZERO);
  if (!newtab) return false;

  t->fd_table = newtab;
  t->fd_cap   = PGSIZE / (int)sizeof(t->fd_table[0]);  // 페이지 한 장 크기만큼 확보
  return true;
}


fd_get

fd가 범위 내에 있는게 맞는지 확인하고

테이블 칸이 NULL 인지 아닌지 확인하고

돌려준다

간단하지만 자주 쓰이는 헬퍼 함수다

static struct file *
fd_get(int fd) {
  struct thread *t = thread_current();
  if (fd < 0 || fd >= t->fd_cap) return NULL;
  return t->fd_table[fd];
}