[Pintos-KAIST] Project 2 : Extend File Descriptor

์œ ์„ ยท2024๋…„ 5์›” 11์ผ
0

Pintos - KAIST

๋ชฉ๋ก ๋ณด๊ธฐ
10/16
post-thumbnail

๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป GITHUB ๋ ˆํฌ์ง€ํ† ๋ฆฌ
๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป GITHUB Extend File Descriptor ์ด์Šˆ

๊ณผ์ œ ์„ค๋ช…

๐Ÿ’ก ์—ฌ๋Ÿฌ๋ถ„์˜ ํ•€ํ† ์Šค๊ฐ€ ๋ฆฌ๋ˆ…์Šค์˜ stdin, stdout ๋ฅผ ๋‹ซ๋Š” ๊ธฐ๋Šฅ๊ณผ dup2 ์‹œ์Šคํ…œ ์ฝœ์„ ์ง€์›ํ•˜๋„๋ก ๋งŒ๋“ค์–ด๋ณด์„ธ์š”.

  • ํ˜„์žฌ ๊ตฌํ˜„๋œ ํ•€ํ† ์Šค์—์„œ๋Š” stdin๊ณผ stdout ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ๋‹ซ๋Š” ๊ฒƒ์ด ๊ธˆ์ง€๋˜์–ด์žˆ๋‹ค. ์ด๋ฒˆ Extend File Descriptor ๊ณผ์ œ์—์„œ ์—ฌ๋Ÿฌ๋ถ„์˜ ํ•€ํ† ์Šค๊ฐ€ ๋ฆฌ๋ˆ…์Šค์ฒ˜๋Ÿผ ์œ ์ €๊ฐ€ stdin๊ณผ stdout์„ ๋‹ซ์„ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผํ•œ๋‹ค. ์ฆ‰, ํ”„๋กœ์„ธ์Šค๊ฐ€ stdin ๋ฅผ ๋‹ซ์œผ๋ฉด ์ ˆ๋Œ€ input์„ ์ฝ์„ ์ˆ˜ ์—†๊ณ , stdout์„ ๋‹ซ์œผ๋ฉด ์–ด๋–ค ๊ฒƒ๋„ ์ถœ๋ ฅํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

  • ๋˜ํ•œ dup2()๋ผ๋Š” ์‹œ์Šคํ…œ ์ฝœ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š”๋ฐ, dup2() ์‹œ์Šคํ…œ ์ฝœ์€ ์ธ์ž๋กœ ๋ฐ›์€ oldfd ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ์˜ ๋ณต์‚ฌ๋ณธ์„ ์ƒ์„ฑํ•˜๊ณ , ์ด ๋ณต์‚ฌ๋ณธ์˜ ํŒŒ์ผ๋””์Šคํฌ๋ฆฝํ„ฐ ๊ฐ’์€ ์ธ์ž๋กœ ๋ฐ›์€ newfd ๊ฐ’์ด ๋˜๊ฒŒ ํ•œ๋‹ค. dup2() ์‹œ์Šคํ…œ ์ฝœ์ด ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ๋ณต์‚ฌํ•ด์„œ ์ƒˆ ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์„ฑ๊ณตํ•œ๋‹ค๋ฉด newfd๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค. ๋งŒ์•ฝ newfd ๊ฐ’์ด ์ด์ „์— ์ด๋ฏธ ์—ด๋ ค์žˆ์—ˆ๋‹ค๋ฉด, newfd๋Š” ์žฌ์‚ฌ์šฉ๋˜๊ธฐ ์ „์— ์กฐ์šฉํžˆ ๋‹ซํžŒ๋‹ค.

  • ์•„๋ž˜ ์‚ฌํ•ญ๋“ค์„ ๊ธฐ์–ตํ•ด์•ผํ•œ๋‹ค.

    • ๋งŒ์•ฝ oldfd ๊ฐ€ ์œ ํšจํ•œ ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด, dup2() ์ฝœ์€ ์‹คํŒจํ•˜์—ฌ 1์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , newfd ๋Š” ๋‹ซํžˆ์ง€ ์•Š๋Š”๋‹ค.
    • ๋งŒ์•ฝ oldfd ๊ฐ€ ์œ ํšจํ•œ ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ์ด๊ณ , newfd๋Š” oldfd์™€ ๊ฐ™์€ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด dup2()๊ฐ€ ํ• ์ผ์€ ๋”ฐ๋กœ ์—†๊ณ  (*์ด๋ฏธ ๊ฐ™์œผ๋ฏ€๋กœ) newfd ๊ฐ’์„ ๋ฆฌํ„ดํ•œ๋‹ค.
  • ์ด ์‹œ์Šคํ…œ์ฝœ๋กœ๋ถ€ํ„ฐ ์„ฑ๊ณต์ ์œผ๋กœ ๊ฐ’์„ ๋ฐ˜ํ™˜๋ฐ›์€ ํ›„์—, oldfd์™€ newfd๋Š” ํ˜ธํ™˜ํ•ด์„œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ์ด ๋‘˜์€ ์„œ๋กœ ๋‹ค๋ฅธ ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ์ด๊ธดํ•˜์ง€๋งŒ, ๋˜‘๊ฐ™์€ ์—ด๋ฆฐ ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ์˜๋ฏธํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ™์€ file offset๊ณผ status flags ๋ฅผ ๊ณต์œ ํ•˜๊ณ  ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋งŒ์•ฝ์— ๋‹ค๋ฅธ ๋””์Šคํฌ๋ฆฝํ„ฐ๊ฐ€ seek ์„ ์‚ฌ์šฉํ•ด์„œ file offset์ด ์ˆ˜์ •๋˜์—ˆ๋‹ค๋ฉด, ๋‹ค๋ฅธ ์Šคํฌ๋ฆฝํ„ฐ์—์„œ๋„ ์ด ๊ฐ’์€ ๋˜‘๊ฐ™์ด ์ˆ˜์ •๋œ๋‹ค.

๊ณผ์ œ ๋ชฉํ‘œ

  • stdin์— ๋Œ€ํ•œ fd๋ฅผ ๋‹ซ์•„์ฃผ๋ฉด, ๊ทธ ์–ด๋–ค input๋„ ์ฝ์–ด๋“ค์ด์ง€ ์•Š๋Š”๋‹ค.
  • stdout์— ๋Œ€ํ•œ fd๋ฅผ ๋‹ซ์•„์ฃผ๋ฉด, ๊ทธ ์–ด๋–ค output๋„ ํ”„๋กœ์„ธ์Šค์— ์˜ํ•ด ์ถœ๋ ฅ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • dup2 ๋ฅผ ๊ตฌํ˜„ํ•ด ์ฃผ์–ด์ง„ stdin/stdout/file ์— ๋Œ€ํ•œ fd๋ฅผ fd_table ๋‚ด์— ๋ณต์‚ฌํ•ด์ค€๋‹ค.

ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•

userprog/Make.var๋ฅผ ์—ด์–ด๋ณด๋ฉด, ์•„๋ž˜ ์„ธ ์ค„์ด ์ฃผ์„์ฒ˜๋ฆฌ ๋˜์–ด์žˆ๋Š”๋ฐ, ์ฃผ์„์„ ํ•ด์ œํ•ด์ฃผ๋ฉด dup2 TC ๋‘๊ฐœ๊ฐ€ ์ถ”๊ฐ€๋œ๋‹ค.

๊ตฌํ˜„

1. ์ž๋ฃŒ๊ตฌ์กฐ ์ถ”๊ฐ€ ๋ฐ ๋ณ€์ˆ˜ ์ถ”๊ฐ€

1.1. STDIN, STDOUT, STDERR ์ •์˜

  • include/userprog/process.h
#define STDIN 1
#define STDOUT 2
#define STDERR 3

1.2. dup_count ์„ ์–ธ ๋ฐ ์ดˆ๊ธฐํ™”

  • filesys/file.c ํŒŒ์ผ์— ์žˆ๋˜ file ๊ตฌ์กฐ์ฒด -> include/filesys/file.h๋กœ ์ด๋™
  • dup_count ์„ ์–ธ
  • stdbool.h include
#include <stdbool.h>

struct inode;

/* An open file. */
struct file {
	struct inode *inode;        /* File's inode. */
	off_t pos;                  /* Current position. */
	bool deny_write;            /* Has file_deny_write() been called? */

    /** Project 2-Extend File Descriptor */
    int dup_count;

};
  • filesys/file.c
  • file_open() ํ•จ์ˆ˜์— dup_count ์ดˆ๊ธฐํ™”
struct file *
file_open (struct inode *inode) {
	struct file *file = calloc (1, sizeof *file);
	if (inode != NULL && file != NULL) {
		file->inode = inode;
		file->pos = 0;
		file->deny_write = false;

		/** Project 2-Extend File Descriptor */
		file->dup_count = 0;
		
		return file;
	} else {
		inode_close (inode);
		free (file);
		return NULL;
	}
}

1.3. fdt์— stdin, stdout, stderr ์„ค์ •

  • threads/thread.c
  • thread_create()
    t->fdt[0] = STDIN;   
    t->fdt[1] = STDOUT; 
    t->fdt[2] = STDERR;  

2. dup2() ๊ตฌํ˜„

2.1. ์‹œ์Šคํ…œ ์ฝœ ํ•ธ๋“ค๋Ÿฌ์— dup2 ์ถ”๊ฐ€

  • userprog/syscall.c
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);
}

/* The main system call interface */
void
syscall_handler (struct intr_frame *f UNUSED) {
	// TODO: Your implementation goes here.
	/** project2-System Call */
int sys_number = f->R.rax;

    // Argument ์ˆœ์„œ
    // %rdi %rsi %rdx %r10 %r8 %r9

    switch (sys_number) {
        case SYS_HALT:
            halt();
            break;
        case SYS_EXIT:
            exit(f->R.rdi);
            break;
        case SYS_FORK:
            f->R.rax = fork(f->R.rdi);
            break;
        case SYS_EXEC:
            f->R.rax = exec(f->R.rdi);
            break;
        case SYS_WAIT:
            f->R.rax = process_wait(f->R.rdi);
            break;
        case SYS_CREATE:
            f->R.rax = create(f->R.rdi, f->R.rsi);
            break;
        case SYS_REMOVE:
            f->R.rax = remove(f->R.rdi);
            break;
        case SYS_OPEN:
            f->R.rax = open(f->R.rdi);
            break;
        case SYS_FILESIZE:
            f->R.rax = filesize(f->R.rdi);
            break;
        case SYS_READ:
            f->R.rax = read(f->R.rdi, f->R.rsi, f->R.rdx);
            break;
        case SYS_WRITE:
            f->R.rax = write(f->R.rdi, f->R.rsi, f->R.rdx);
            break;
        case SYS_SEEK:
            seek(f->R.rdi, f->R.rsi);
            break;
        case SYS_TELL:
            f->R.rax = tell(f->R.rdi);
            break;
        case SYS_CLOSE:
            close(f->R.rdi);
            break;
        case SYS_DUP2:
            f->R.rax = dup2(f->R.rdi, f->R.rsi);
            break;
        default:
            exit(-1);
    }
}

2.2. dup2 ์„ ์–ธ ๋ฐ ๊ตฌํ˜„

  • include/userprog/syscall.h
/** Project 2-Extend File Descriptor */
int dup2(int oldfd, int newfd);
  • userprog/syscall.c
/** Project 2-Extend File Descriptor */
int dup2(int oldfd, int newfd) {
    if (oldfd < 0 || newfd < 0)
        return -1;

    struct file *oldfile = process_get_file(oldfd);

    if (oldfile == NULL)
        return -1;

    if (oldfd == newfd)
        return newfd;

    struct file *newfile = process_get_file(newfd);

    if (oldfile == newfile)
        return newfd;

    close(newfd);

    newfd = process_insert_file(newfd, oldfile);

    return newfd;
}

2.3. process_insert_file() ์„ ์–ธ ๋ฐ ๊ตฌํ˜„

  • include/userprog/process.h
/** Project 2-Extend File Descriptor */
process_insert_file(int fd, struct file *f);
  • userprog/process.c
/** Project 2-Extend File Descriptor */
process_insert_file(int fd, struct file *f) {
    struct thread *curr = thread_current();
    struct file **fdt = curr->fdt;

    if (fd < 0 || fd >= FDCOUNT_LIMIT)
        return -1;

    if (f > STDERR)
        f->dup_count++;

    fdt[fd] = f;

    return fd;
}

3. read() ์ถ”๊ฐ€ ๊ตฌํ˜„

  • userprog/syscall.c
/** Project 2-Extend File Descriptor */
int 
read(int fd, void *buffer, unsigned length) 
{
    struct thread *curr = thread_current();
    check_address(buffer);

    struct file *file = process_get_file(fd);

    if (file == STDIN) { 
        int i = 0; 
        char c;
        unsigned char *buf = buffer;

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

    if (file == NULL || file == STDOUT || file == STDERR)  // ๋นˆ ํŒŒ์ผ, stdout, stderr๋ฅผ ์ฝ์œผ๋ ค๊ณ  ํ•  ๊ฒฝ์šฐ
        return -1;

    off_t bytes = -1;

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

    return bytes;
}

4. write() ์ถ”๊ฐ€ ๊ตฌํ˜„

  • userprog/syscall.c
/** Project 2-Extend File Descriptor */
int 
write(int fd, const void *buffer, unsigned length) 
{
    check_address(buffer);

    struct thread *curr = thread_current();
    off_t bytes = -1;

    struct file *file = process_get_file(fd);

    if (file == STDIN || file == NULL)  
        return -1;

    if (file == STDOUT) { 

        putbuf(buffer, length);
        return length;
    }

    if (file == STDERR) { 

        putbuf(buffer, length);
        return length;
    }

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

    return bytes;
}

5. close() ์ถ”๊ฐ€ ๊ตฌํ˜„

  • userprog/syscall.c

/** Project 2-Extend File Descriptor */
void 
close(int fd) 
{
    struct thread *curr = thread_current();
    struct file *file = process_get_file(fd);

    if (file == NULL)
        return;

    process_close_file(fd);

    if (file == STDIN) {
        file = 0;
        return;
    }

    if (file == STDOUT) {
        file = 0;
        return;
    }

    if (file == STDERR) {
        file = 0;
        return;
    }

    if (file->dup_count == 0)
        file_close(file);
    else
        file->dup_count--;
}

6. do_fork() ์ถ”๊ฐ€ ๊ตฌํ˜„

  • userprog/process.c
static void
__do_fork (void *aux) {
	struct intr_frame if_;
	struct thread *parent = (struct thread *) aux;
	struct thread *current = thread_current ();
	
	/* TODO: somehow pass the parent_if. (i.e. process_fork()'s if_) */
	struct intr_frame *parent_if = &parent->parent_if;
	bool succ = true;

	/* 1. Read the cpu context to local stack. */
	memcpy (&if_, parent_if, sizeof (struct intr_frame));
    if_.R.rax = 0;  // ์ž์‹ ํ”„๋กœ์„ธ์Šค์˜ return๊ฐ’ (0)

	/* 2. Duplicate PT */
	current->pml4 = pml4_create();
	if (current->pml4 == NULL)
		goto error;

	process_activate (current);
#ifdef VM
	supplemental_page_table_init (&current->spt);
	if (!supplemental_page_table_copy (&current->spt, &parent->spt))
		goto error;
#else
	if (!pml4_for_each (parent->pml4, duplicate_pte, parent))
		goto error;
#endif

	/* TODO: Your code goes here.
	 * TODO: Hint) To duplicate the file object, use `file_duplicate`
	 * TODO:       in include/filesys/file.h. Note that parent should not return
	 * TODO:       from the fork() until this function successfully duplicates
	 * TODO:       the resources of parent.*/

    if (parent->fd_idx >= FDCOUNT_LIMIT)
        goto error;

	/** Project 2-Extend File Descriptor */

    struct file *file;

    for (int fd = 0; fd < FDCOUNT_LIMIT; fd++) {
        file = parent->fdt[fd];
        if (file == NULL)
            continue;

        if (file > STDERR)
            current->fdt[fd] = file_duplicate(file);
        else
            current->fdt[fd] = file;
	}

    current->fd_idx = parent->fd_idx;
    sema_up(&current->fork_sema);  

    process_init();

    /* Finally, switch to the newly created process. */
    if (succ)
        do_iret(&if_);  // ์ •์ƒ ์ข…๋ฃŒ ์‹œ ์ž์‹ Process๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Ÿฌ ๊ฐ

error:
	
    sema_up(&current->fork_sema);  // ๋ณต์ œ์— ์‹คํŒจํ–ˆ์œผ๋ฏ€๋กœ ํ˜„์žฌ fork์šฉ sema unblock
    exit(TID_ERROR);
}

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ


์ฐธ๊ณ 

profile
Sunny Day!

0๊ฐœ์˜ ๋Œ“๊ธ€