jun-wiki

View My GitHub Profile

Posts (Latest 10 updated) :
Read all
Contents:
  1. 시스템콜
    1. read
    2. write
  2. 헬퍼
    1. copy in
    2. copy out

이전에 이어서

시스템콜 구현이다

하다 죽을 뻔했다

CRLF 시ㅂ


시스템콜

오늘 아마 이거 2개하고 끝일 거 같다

오늘 컨디션도 안좋기에 더 하기는 어려울듯?

꒰ ᐢ ◞‸◟ᐢ꒱
(외적, 내적 전부)


read

사나이로 태어나 글이라면 자기 이름 석자 읽고 쓸줄 알면 충분하다지만

컴퓨터는 예외다

주는 거 다 읽어야지 ㅇㅇ

static int
system_read(int fd, void *buffer, unsigned size) {
  if (size == 0) return 0;

  if (fd == 0) {                                // 키보드
    for (unsigned i = 0; i < size; i++) {
      uint8_t key = (uint8_t)input_getc();
      copy_out((uint8_t*)buffer + i, &key, 1);
    }
    return (int)size;
  }

  if (fd == 1) return -1;

  struct file *f = fd_get(fd);
  if (!f) return -1;

  void *read_page = palloc_get_page(PAL_ZERO);
  if (read_page == NULL) return -1;

  int total = 0;

  lock_acquire(&filesys_lock);
  while (total < (int)size) {
    size_t chunk = size - total;
    if (chunk > PGSIZE) chunk = PGSIZE;

    off_t n = file_read(f, read_page, (off_t)chunk);
    if (n <= 0) break;

    copy_out((uint8_t*)buffer + total, read_page, (size_t)n);
    total += (int)n;
  }
  lock_release(&filesys_lock);
  palloc_free_page(read_page);
  return total;
}

상딩히 길다

초기 fd 값이 0이면 키보드 입력을 받는 걸로 바꾸고

아닐 경우 데이터 읽어 유저 버퍼에 넣어준다

데이터 읽는 곳은 파일이나 콘솔이다

참고로 덩어리 단위로 읽어서 페이지 바뀌었을때도 고려한다

copy_out은 중간의 매개체 역할이다

직접 데이터 참조하거나 하지 않게 말이다


write

유저 프로그램 메모리 데이터를 꺼내는 역할이다

콘솔/파일에서 꺼내주는게 아닌 유저 프로그램에서 꺼내줬다는 것은

넣어줄 곳은 콘솔/파일 이라는 거다

중간에 매개체로 쓸 버퍼 만드는데에 copy_in사용한다

static long
system_write (int fd, const void *buf, unsigned size) {
  if (size == 0) return 0;
  if (buf == NULL) system_exit(-1);

  void *kpage = palloc_get_page(0);
  if (!kpage) return -1;

  long total = 0;

  if (fd == 1) {              // STDOUT
    while ((unsigned)total < size) {
      size_t chunk = size - (unsigned)total;
      if (chunk > PGSIZE) chunk = PGSIZE;

      copy_in(kpage, (const uint8_t *)buf + total, chunk);
      putbuf((const char *)kpage, chunk);

      total += (long)chunk;
    }
    palloc_free_page(kpage);
    return total;
  }

  if (fd == 0) { palloc_free_page(kpage); return -1; }

  struct file *f = fd_get(fd);
  if (!f) { palloc_free_page(kpage); return -1; }

  lock_acquire(&filesys_lock);
  while ((unsigned)total < size) {
    size_t chunk = size - (unsigned)total;
    if (chunk > PGSIZE) chunk = PGSIZE;

    copy_in(kpage, (const uint8_t *)buf + total, chunk);
    off_t n = file_write(f, kpage, chunk);
    if (n <= 0) break;
    total += (long)n;
    if ((size_t)n < chunk) break;
  }
  lock_release(&filesys_lock);
  palloc_free_page(kpage);
  return total;
}




헬퍼

헬퍼도 쓰인다

코어타임하고 안 사실인데

테스트 케이스 개헐렁해서

그냥 이런거 안해도 통과된다고 한다

유저 주소를 커널에서 직접 믿고 쓰면 안되기에

둘을 구분하고 나눠 쓰기 위한 함수들인데…

pintos는 시부레 다 커널 스레드라 이런 거 없이 걍 써도 통과다

내 시간…

=ε/̵͇̿̿/’̿’̿ ̿ (︶︹︺)


copy in

유저 프로그램 메모리의 데이터를 바깥으로 꺼내는 용도다

반환값은 실제 사용 바이트 수, 실패시는 -1이다

유저가 buf를 주는데 이는 유저 가상주소라 그냥 쓰면 안된다

그래서 커널 접근 가능 주소 얻어

이를 임시 버퍼로 써서 데이터 옮기는 역할이다

이렇게 데이터 담긴 버퍼를 다른데서 쓰는 거고 말이다

그리고 pintos에서는 쓸모 없다

static void
copy_in(void *kdst, const void *usrc, size_t n) {
  uint8_t *kd = (uint8_t *)kdst;            // 커널 공간 목적지 포인터 (진행 포인터)
  const uint8_t *u = (const uint8_t *)usrc; // 유저 공간 소스 포인터 (진행 포인터)

  while (n > 0) {
    // 1) 현재 유저 주소 u가 "유저 영역"인지 확인
    if (!is_user_vaddr(u)) system_exit(-1);

    // 2) u가 속한 유저 페이지의 커널 매핑 주소(페이지 시작)를 얻는다
    const void *kp = pml4_get_page(thread_current()->pml4, pg_round_down(u));
    if (kp == NULL) system_exit(-1);

    // 3) 페이지 내 오프셋을 더해 정확한 소스 주소(커널이 접근 가능한)를 만든다
    const uint8_t *ksrc = (const uint8_t *)kp + pg_ofs(u);

    // 4) 이 페이지에서 읽을 수 있는 최대 바이트 수 계산
    size_t chunk = PGSIZE - pg_ofs(u);
    if (chunk > n) chunk = n;

    // 5) 실제 복사: 유저(의 커널 매핑) → 커널
    memcpy(kd, ksrc, chunk);

    // 6) 진행 포인터/잔량 갱신
    kd += chunk;
    u  += chunk;
    n  -= chunk;
  }
}


copy out

얜 반대다

유저가 아닌 커널 버퍼의 데이터를 유저주소로 옮겨준다는 말이다

그거 말고는 비슷하다

static void
copy_out(void *udst, const void *ksrc, size_t n) {
  uint8_t *u = (uint8_t *)udst;           // 유저 공간 목적지 포인터 (진행 포인터)
  const uint8_t *k = (const uint8_t *)ksrc; // 커널 공간 소스 포인터 (진행 포인터)

  while (n > 0) {
    // 1) 현재 유저 주소 u가 "유저 영역"인지 확인 (커널/NULL/커널 예약영역이면 out)
    if (!is_user_vaddr(u)) system_exit(-1);

    // 2) u가 속한 "유저 페이지"를 현재 스레드의 pml4에서 커널이 접근 가능한 VA로 얻음
    //    - pg_round_down(u): u가 속한 페이지의 시작 주소로 내림
    //    - pml4_get_page(...): 그 페이지 프레임을 가리키는 '커널 매핑'을 리턴(없으면 NULL)
    void *kp = pml4_get_page(thread_current()->pml4, pg_round_down(u));
    if (kp == NULL) system_exit(-1);

    // 3) 그 페이지의 '페이지 내 오프셋' 위치로 목적지를 맞춘다
    //    - kdst: "커널이 접근 가능한" 해당 유저 페이지의 정확한 목표 주소
    uint8_t *kdst = (uint8_t *)kp + pg_ofs(u);

    // 4) 이번에 이 페이지에서 쓸 수 있는 최대 바이트 수를 계산
    //    - 현재 u가 페이지 끝에 가까우면 남은 공간만큼만
    size_t chunk = PGSIZE - pg_ofs(u);
    if (chunk > n) chunk = n;

    // 5) 실제 복사: 커널→유저(의 커널 매핑)
    memcpy(kdst, k, chunk);

    // 6) 진행 포인터/잔량 갱신
    u += chunk;
    k += chunk;
    n -= chunk;
  }
}