[Pintos-KAIST] Project 2 : File Descriptor

μœ μ„ Β·2024λ…„ 5μ›” 9일
0

Pintos - KAIST

λͺ©λ‘ 보기
8/16
post-thumbnail

πŸ‘©πŸ»β€πŸ’» GITHUB λ ˆν¬μ§€ν† λ¦¬
πŸ‘©πŸ»β€πŸ’» GITHUB File Descriptor 이슈

과제 μ„€λͺ…

ν•€ν† μŠ€μ—λŠ” 파일 λ””μŠ€ν¬λ¦½ν„° 뢀뢄이 λˆ„λ½λ˜μ–΄μžˆλ‹€. 파일 μž…μΆœλ ₯을 μœ„ν•΄μ„œλŠ” 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ κ΅¬ν˜„μ΄ ν•„μš”ν•˜λ‹€.

  • κ΅¬ν˜„ν•  μ‹œμŠ€ν…œ 콜
    open () : νŒŒμΌμ„ μ—΄ λ•Œ μ‚¬μš©ν•˜λŠ” μ‹œμŠ€ν…œ 콜
    filesize () : 파일의 크기λ₯Ό μ•Œλ €μ£ΌλŠ” μ‹œμŠ€ν…œ 콜
    read () : μ—΄λ¦° 파일의 데이터λ₯Ό μ½λŠ” μ‹œμŠ€ν…œ 콜
    write () : μ—΄λ¦° 파일의 데이터λ₯Ό 기둝 μ‹œμŠ€ν…œ 콜
    seek () : μ—΄λ¦° 파일의 μœ„μΉ˜(offset)λ₯Ό μ΄λ™ν•˜λŠ” μ‹œμŠ€ν…œ 콜
    tell () : μ—΄λ¦° 파일의 μœ„μΉ˜(offset)λ₯Ό μ•Œλ €μ£ΌλŠ” μ‹œμŠ€ν…œ 콜
    close () : μ—΄λ¦° νŒŒμΌμ„ λ‹«λŠ” μ‹œμŠ€ν…œ 콜

μˆ˜μ • 및 μΆ”κ°€ ν•¨μˆ˜

  • tid_t thread_create (const char *name, int priority, thread_func *function, void *aux) : function ν•¨μˆ˜λ₯Ό μˆ˜ν–‰ν•˜λŠ” μŠ€λ ˆλ“œ 생성
  • void process_exit (void) : ν”„λ‘œμ„ΈμŠ€ μ’…λ£Œ μ‹œ ν˜ΈμΆœλ˜μ–΄ ν”„λ‘œμ„ΈμŠ€μ˜ μžμ›μ„ ν•΄μ œ
  • int process_add_file (struct file *f) : 파일 객체에 λŒ€ν•œ 파일 λ””μŠ€ν¬λ¦½ν„° 생성
  • struct file *process_get_file (int fd) : ν”„λ‘œμ„ΈμŠ€μ˜ 파일 λ””μŠ€ν¬λ¦½ν„° ν…Œμ΄λΈ”μ„ κ²€μƒ‰ν•˜μ—¬ 파일 객체의 μ£Όμ†Œλ₯Ό 리턴
  • void process_close_file (int fd) : 파일 λ””μŠ€ν¬λ¦½ν„°μ— ν•΄λ‹Ήν•˜λŠ” νŒŒμΌμ„ λ‹«κ³  ν•΄λ‹Ή μ—”νŠΈλ¦¬ μ΄ˆκΈ°ν™”

κ΅¬ν˜„

1. thread 자료ꡬ쑰 μˆ˜μ •

1.1. νŽ˜μ΄μ§€ ν• λ‹Ή

  • include/threads/thread.h
/** project2-System Call */
#define FDT_PAGES     3                     // test `multi-oom` ν…ŒμŠ€νŠΈμš©
#define FDCOUNT_LIMIT FDT_PAGES * (1 << 9)  // μ—”νŠΈλ¦¬κ°€ 512개 인 이유: νŽ˜μ΄μ§€ 크기 4kb / 파일 포인터 8byte

1.2. fd_idx, fdt μ„ μ–Έ

  • include/threads/thread.h
	/** project2-System Call */
	int exit_status;

	int fd_idx;              // 파일 λ””μŠ€ν¬λ¦½ν„° 인덱슀
    struct file **fdt;       // 파일 λ””μŠ€ν¬λ¦½ν„° ν…Œμ΄λΈ”

1.3. thread_create() μˆ˜μ •

  • threads/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 ();

	#ifdef USERPROG
	/** project2-System Call */
    t->fdt = palloc_get_multiple(PAL_ZERO, FDT_PAGES);
    if (t->fdt == NULL)
        return TID_ERROR;

    t->exit_status = 0;  // exit_status μ΄ˆκΈ°ν™”

    t->fd_idx = 3;
    t->fdt[0] = 0;  // stdin μ˜ˆμ•½λœ 자리 (dummy)
    t->fdt[1] = 1;  // stdout μ˜ˆμ•½λœ 자리 (dummy)
    t->fdt[2] = 2;  // stderr μ˜ˆμ•½λœ 자리 (dummy)

   .
   .
   .

	return tid;
}

2. File Descriptor κ΅¬ν˜„

2.1. κ΅¬ν˜„ν•  ν•¨μˆ˜ μ„ μ–Έ

  • include/userprog/process.h
/** project2-System Call */
int process_add_file(struct file *f);
struct file *process_get_file(int fd);
int process_close_file(int fd);

2.2. process_add_file() ν•¨μˆ˜ κ΅¬ν˜„

  • userprog/process.c
// ν˜„μž¬ μŠ€λ ˆλ“œ fdt에 파일 μΆ”κ°€
int 
process_add_file(struct file *f) 
{
    struct thread *curr = thread_current();
    struct file **fdt = curr->fdt;

    if (curr->fd_idx >= FDCOUNT_LIMIT)
        return -1;

    fdt[curr->fd_idx++] = f;

    return curr->fd_idx - 1;
}

2.3. process_get_file() ν•¨μˆ˜ κ΅¬ν˜„

  • userprog/process.c
// ν˜„μž¬ μŠ€λ ˆλ“œμ˜ fd번째 파일 정보 μ–»κΈ°
struct file 
*process_get_file(int fd) 
{
    struct thread *curr = thread_current();

    if (fd >= FDCOUNT_LIMIT)
        return NULL;

    return curr->fdt[fd];
}

2.4. process_close_file() ν•¨μˆ˜ κ΅¬ν˜„

  • userprog/process.c
// ν˜„μž¬ μŠ€λ ˆλ“œμ˜ fdtμ—μ„œ 파일 μ‚­μ œ
int 
process_close_file(int fd) 
{
    struct thread *curr = thread_current();

    if (fd >= FDCOUNT_LIMIT)
        return -1;

    curr->fdt[fd] = NULL;
    return 0;
}

2.5. process_exit() ν•¨μˆ˜ μˆ˜μ •

  • userprog/process.c
void
process_exit (void) {
	struct thread *curr = thread_current ();
	/* TODO: Your code goes here.
	 * TODO: Implement process termination message (see
	 * TODO: project2/process_termination.html).
	 * TODO: We recommend you to implement process resource cleanup here. */

	    for (int fd = 0; fd < curr->fd_idx; fd++)  // FDT λΉ„μš°κΈ°
        close(fd);

    file_close(curr->runn_file);  // ν˜„μž¬ ν”„λ‘œμ„ΈμŠ€κ°€ 싀행쀑인 파일 μ’…λ£Œ

    palloc_free_multiple(curr->fdt, FDT_PAGES);

    process_cleanup();

    sema_up(&curr->wait_sema);  // μžμ‹ ν”„λ‘œμ„ΈμŠ€κ°€ μ’…λ£Œλ  λ•ŒκΉŒμ§€ λŒ€κΈ°ν•˜λŠ” λΆ€λͺ¨μ—κ²Œ signal

    sema_down(&curr->exit_sema);  // λΆ€λͺ¨ ν”„λ‘œμ„ΈμŠ€κ°€ μ’…λ£Œλ  λ–„κΉŒμ§€ λŒ€κΈ°

}

3. lock μ‚¬μš©

  • Read, writeμ‹œ νŒŒμΌμ— λŒ€ν•œ λ™μ‹œμ ‘κ·Όμ΄ 일어날 수 μžˆμœΌλ―€λ‘œ Lock μ‚¬μš©
    • syscall.h 에 global lock으둜 μ„ μ–Έ (struct lock filesys_lock)
    • syscall_init()μ—μ„œ lock μ΄ˆκΈ°ν™” (lock_init() 호좜)
    • 각 μ‹œμŠ€ν…œ μ½œμ—μ„œ lock νšλ“ ν›„ μ‹œμŠ€ν…œ 콜 처리, μ‹œμŠ€ν…œ 콜 μ™„λ£Œ μ‹œ lock λ°˜λ‚©
  • userprog/syscall.c
  /** project2-System Call */
struct lock filesys_lock;  // 파일 읽기/μ“°κΈ° 용 lock
.
.
.
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);
    
    /** project2-System Call */
    // read & write 용 lock μ΄ˆκΈ°ν™”
    lock_init(&filesys_lock);
}
.
.
.

4. μ‹œμŠ€ν…œ 콜 κ΅¬ν˜„

4.1. κ΅¬ν˜„ν•  μ‹œμŠ€ν…œ 콜 μ„ μ–Έ

  • include/userprog/syscall.h
int open(const char *file);
int filesize(int fd);
int read(int fd, void *buffer, unsigned length);
int write(int fd, const void *buffer, unsigned length);
void seek(int fd, unsigned position);
int tell(int fd);
void close(int fd);

4.1. open() κ΅¬ν˜„

  • userprog/syscall.c
  • νŒŒμΌμ„ μ—΄ λ•Œ μ‚¬μš©ν•˜λŠ” μ‹œμŠ€ν…œ 콜
    • 파일이 없을 경우 μ‹€νŒ¨
    • 성곡 μ‹œ fdλ₯Ό μƒμ„±ν•˜κ³  λ°˜ν™˜, μ‹€νŒ¨ μ‹œ-1 λ°˜ν™˜
  • File : 파일의 이름 및 경둜 정보
int 
open(const char *file) 
{
    check_address(file);
    struct file *newfile = filesys_open(file);

    if (newfile == NULL)
        return -1;

    int fd = process_add_file(newfile);

    if (fd == -1)
        file_close(newfile);

    return fd;
}

4.2. filesize() κ΅¬ν˜„

  • userprog/syscall.c
  • 파일의 크기λ₯Ό μ•Œλ €μ£ΌλŠ” μ‹œμŠ€ν…œ 콜
  • 성곡 μ‹œ 파일의 크기λ₯Ό λ°˜ν™˜, μ‹€νŒ¨ μ‹œ-1 λ°˜ν™˜
int 
filesize(int fd) {
    struct file *file = process_get_file(fd);

    if (file == NULL)
        return -1;

    return file_length(file);
}

4.3. read() κ΅¬ν˜„

  • userprog/syscall.c
  • μ—΄λ¦° 파일의 데이터λ₯Ό μ½λŠ” μ‹œμŠ€ν…œ 콜
  • 성곡 μ‹œ 읽은 λ°”μ΄νŠΈ 수λ₯Ό λ°˜ν™˜, μ‹€νŒ¨ μ‹œ-1 λ°˜ν™˜
  • buffer : 읽은 데이터λ₯Ό μ €μž₯ν•  λ²„νΌμ˜ μ£Όμ†Œ κ°’
  • size : 읽을 데이터 크기
  • fd 값이 0일 λ•Œ ν‚€λ³΄λ“œμ˜ 데이터λ₯Ό 읽어 버퍼에 μ €μž₯. (input_getc() 이용)
int 
read(int fd, void *buffer, unsigned length) 
{
    check_address(buffer);

    if (fd == 0) {  // 0(stdin) -> keyboard둜 직접 μž…λ ₯
        int i = 0;  // μ“°λ ˆκΈ° κ°’ return 방지
        char c;
        unsigned char *buf = buffer;

        for (; i < length; i++) {
            c = input_getc();
            *buf++ = c;
            if (c == '\0')
                break;
        }

        return i;
    }
    // κ·Έ μ™Έμ˜ 경우
    if (fd < 3)  // stdout, stderrλ₯Ό 읽으렀고 ν•  경우 & fdκ°€ 음수일 경우
        return -1;

    struct file *file = process_get_file(fd);
    off_t bytes = -1;

    if (file == NULL)  // 파일이 λΉ„μ–΄μžˆμ„ 경우
        return -1;

    lock_acquire(&filesys_lock);
    bytes = file_read(file, buffer, length);
    lock_release(&filesys_lock);

    return bytes;
}

4.4. write() κ΅¬ν˜„

  • userprog/syscall.c
  • μ—΄λ¦° 파일의 데이터λ₯Ό 기둝 μ‹œμŠ€ν…œ 콜
  • 성곡 μ‹œ κΈ°λ‘ν•œ λ°μ΄ν„°μ˜ λ°”μ΄νŠΈ 수λ₯Ό λ°˜ν™˜, μ‹€νŒ¨μ‹œ-1 λ°˜ν™˜
  • buffer : 기둝 ν•  데이터λ₯Ό μ €μž₯ν•œ λ²„νΌμ˜ μ£Όμ†Œ κ°’
  • size : 기둝할 데이터 크기
  • fd 값이 1일 λ•Œ 버퍼에 μ €μž₯된 데이터λ₯Ό 화면에 좜λ ₯. (putbuf() 이용)
int 
write(int fd, const void *buffer, unsigned length) 
{
    check_address(buffer);

    off_t bytes = -1;

    if (fd <= 0)  // stdin에 μ“°λ €κ³  ν•  경우 & fd 음수일 경우
        return -1;

    if (fd < 3) {  // 1(stdout) * 2(stderr) -> console둜 좜λ ₯
        putbuf(buffer, length);
        return length;
    }

    struct file *file = process_get_file(fd);

    if (file == NULL)
        return -1;

    lock_acquire(&filesys_lock);
    bytes = file_write(file, buffer, length);
    lock_release(&filesys_lock);

    return bytes;
}

4.5. seek() κ΅¬ν˜„

  • userprog/syscall.c
  • μ—΄λ¦° 파일의 μœ„μΉ˜(offset)λ₯Ό μ΄λ™ν•˜λŠ” μ‹œμŠ€ν…œ 콜
  • Position : ν˜„μž¬ μœ„μΉ˜(offset)λ₯Ό κΈ°μ€€μœΌλ‘œ 이동할 거리
void 
seek(int fd, unsigned position) 
{
    struct file *file = process_get_file(fd);

    if (fd < 3 || file == NULL)
        return;

    file_seek(file, position);
}

4.6. tell() κ΅¬ν˜„

  • userprog/syscall.c
  • μ—΄λ¦° 파일의 μœ„μΉ˜(offset)λ₯Ό μ•Œλ €μ£ΌλŠ” μ‹œμŠ€ν…œ 콜
  • 성곡 μ‹œ 파일의 μœ„μΉ˜(offset)λ₯Ό λ°˜ν™˜, μ‹€νŒ¨ μ‹œ -1 λ°˜ν™˜
int 
tell(int fd) 
{
    struct file *file = process_get_file(fd);

    if (fd < 3 || file == NULL)
        return -1;

    return file_tell(file);
}

4.7. close() κ΅¬ν˜„

  • userprog/syscall.c
  • μ—΄λ¦° νŒŒμΌμ„ λ‹«λŠ” μ‹œμŠ€ν…œ 콜
  • νŒŒμΌμ„ λ‹«κ³  File Descriptorλ₯Ό 제거
void 
close(int fd) 
{
    struct file *file = process_get_file(fd);

    if (fd < 3 || file == NULL)
        return;

    process_close_file(fd);

    file_close(file);
}

5. page_fault() μˆ˜μ •

  • create-bad-ptr, read-bad-ptr λ“± 15개의 test caseμ—μ„œ Page fault λ°œμƒ

    • Page fault μ—λŸ¬ λ©”μ‹œμ§€ 좜λ ₯으둜 인해 test caseκ°€ fail 처리 됨
    • μ—λŸ¬ λ©”μ‹œμ§€ 좜λ ₯을 λ°©μ§€ν•˜κΈ° μœ„ν•΄ exit(-1) 을 호좜 ν•˜λ„λ‘ μˆ˜μ •
  • userprog/exception.c

static void
page_fault (struct intr_frame *f) {
	bool not_present;  /* True: not-present page, false: writing r/o page. */
	bool write;        /* True: access was write, false: access was read. */
	bool user;         /* True: access by user, false: access by kernel. */
	void *fault_addr;  /* Fault address. */

	/* Obtain faulting address, the virtual address that was
	   accessed to cause the fault.  It may point to code or to
	   data.  It is not necessarily the address of the instruction
	   that caused the fault (that's f->rip). */

	fault_addr = (void *) rcr2();

	/* Turn interrupts back on (they were only off so that we could
	   be assured of reading CR2 before it changed). */
	intr_enable ();


	/* Determine cause. */
	not_present = (f->error_code & PF_P) == 0;
	write = (f->error_code & PF_W) != 0;
	user = (f->error_code & PF_U) != 0;

	/** project2-System Call */
	exit(-1);
   
profile
Sunny Day!

0개의 λŒ“κΈ€