PintOS PJT2 - User Program (argumenet passing)

김수환·2024년 11월 12일

PintOS

목록 보기
4/15

https://velog.io/@rivolt0421/Pintos-2.-UserPrograms

프로세스는 "머신(machine)에 대한 추상화" 이다.

조금 더 구체적으로는 CPU / Memory / Storage를 가진 컴퓨터의 추상화

각 응용프로그램이 이러한 머신의 자원을 독점해 사용하고 있는 것 같은 환상(편한 착각)을 제공하는 추상화

프로세스라는 추상화를 통해 protection 과 isolation 이라는 목적을 달성할 수 있다.

목적 달성 방법 : 유저 프로그램이 머신 자원에 직접적으로 접근하는 것을 막는다.
➡️ 유저 모드 / 커널 모드의 구분, 사적 주소공간(각 프로그램의 주소공간을 구분)

Argument Passing
저 프로그램을 실행할 수 있어야 하기 때문에, 유저 프로그램을 load하고, command line으로 받은 argument 들을 프로그램에 전달하는 과정을 다뤘다.

process.c 추가된 부분

process_create_initd ()

목표: command line을 parsing해서 file_name을 찾는다.

... process.c 추가된 부분
process_create_initd ()
	/* Create a new thread to execute FILE_NAME. */
    /** Project2: for Test Case - 직접 프로그램을 실행할 때에는 이 함수를 사용하지 않지만 make check에서
     *  이 함수를 통해 process_create를 실행하기 때문에 이 부분을 수정해주지 않으면 Test Case의 Thread_name이
     *  커맨드 라인 전체로 바뀌게 되어 Pass할 수 없다.
     */
    
    char *ptr;
    strtok_r(file_name, " ", &ptr);

    /** --------------------------------------------------------------------------------------------- */

    /* FILE_NAME을 실행할 새 스레드를 만듭니다. */

    tid = thread_create (file_name, PRI_DEFAULT, initd, fn_copy);
...

코드 해석 및 추가된 코드 분석

기존 코드 설명

  1. 파일 이름 복사

    
    fn_copy = palloc_get_page (0);
    if (fn_copy == NULL)
        return TID_ERROR;
    strlcpy (fn_copy, file_name, PGSIZE);
    
    • file_name 문자열의 사본을 생성하여 fn_copy에 저장합니다.
    • 이유: file_name은 호출자에 의해 공유될 수 있으므로, load() 함수가 동시에 접근하면 Race Condition이 발생할 수 있습니다. 이를 방지하기 위해 사본을 생성합니다.
  2. 새로운 스레드 생성

    
    tid = thread_create (file_name, PRI_DEFAULT, initd, fn_copy);
    
    • thread_create 함수는 새로운 스레드를 생성하고 file_name을 스레드 이름으로 설정합니다.
    • initd는 새로 생성된 스레드가 실행할 함수입니다.
    • 생성된 스레드의 ID를 반환하며, 실패 시 메모리를 해제하고 오류를 반환합니다.

추가된 코드


char *ptr;
strtok_r(file_name, " ", &ptr);

이 코드가 필요한 이유

  1. Test Case 문제
    • 기존 코드에서는 file_name 전체가 스레드 이름으로 사용됩니다. 예를 들어, 아래와 같이 호출된 경우:
      
      process_create_initd("program arg1 arg2");
      
      • file_name으로 전달된 전체 문자열 "program arg1 arg2"가 스레드 이름으로 설정됩니다.
    • Test Case에서 스레드 이름은 실행할 프로그램의 이름(첫 번째 단어)만 필요로 합니다. 예를 들어, "program"만 스레드 이름으로 설정되어야 합니다.
  2. strtok_r로 해결
    • strtok_r를 사용하여 file_name에서 첫 번째 단어를 추출합니다. 결과적으로 스레드 이름은 프로그램 이름인 "program"으로 설정됩니다.
    • thread_create는 이제 "program"을 스레드 이름으로 사용하므로 Test Case의 조건을 만족하게 됩니다.

process_exec ()

목표: 인자로 들어오는 f_name을 parsing하고 user stack에 매개변수들을 push한다.

  • _if ?
  • if_ ?
    주의
// process.c
int 
process_exec (void *f_name) {
    char *file_name = f_name;
    bool success;

	/* We cannot use the intr_frame in the thread structure.
	 * This is because when current thread rescheduled,
	 * it stores the execution information to the member. */
    /* 스레드 구조에서는 intr_frame을 사용할 수 없습니다.
     * 현재 쓰레드가 재스케줄 되면 실행 정보를 멤버에게 저장하기 때문입니다. */

    struct intr_frame if_;
    if_.ds = if_.es = if_.ss = SEL_UDSEG;
    if_.cs = SEL_UCSEG;
    if_.eflags = FLAG_IF | FLAG_MBS;

    /* We first kill the current context */
    process_cleanup ();

    /** #Project 2: Command Line Parsing - 문자열 분리 */
    char *ptr, *arg;
    int argc = 0;
    char *argv[64];

    for (arg = strtok_r(file_name, " ", &ptr); arg != NULL; arg = strtok_r(NULL, " ", &ptr))
        argv[argc++] = arg;

    /* And then load the binary */
    success = load (file_name, &if_);

    /* If load failed, quit. */
    if (!success)
        return -1;

    argument_stack(argv, argc, &if_);

    palloc_free_page(file_name);

    /** #Project 2: Command Line Parsing - 디버깅용 툴 */
    // hex_dump(if_.rsp, if_.rsp, USER_STACK - if_.rsp, true);

    /* Start switched process. */
    do_iret(&if_);
    NOT_REACHED();
}
// 추가된 부분

    /** #Project 2: Command Line Parsing - 문자열 분리 */
    char *ptr, *arg;
    int argc = 0;
    char *argv[64];

    for (arg = strtok_r(file_name, " ", &ptr); arg != NULL; arg = strtok_r(NULL, " ", &ptr))
        argv[argc++] = arg;



    argument_stack(argv, argc, &if_);

코드 분석 및 추가된 부분 해석

추가된 부분은 Command Line Parsing 기능을 추가하기 위해 삽입되었습니다. 프로그램 실행 시 전달된 명령어와 인자를 파싱하여 문자열로 분리하고, 이를 배열에 저장하는 역할을 합니다.


추가된 코드 설명

1. 변수 선언

c
코드 복사
char *ptr, *arg;
int argc = 0;
char *argv[64];
  • ptr: strtok_r에서 문자열의 나머지 부분을 저장하는 포인터.
  • arg: 문자열 분리 결과, 현재 토큰을 저장.
  • argc: 인자의 개수를 세는 변수.
  • argv[64]: 명령어와 인자를 저장하는 배열. 최대 64개의 인자를 저장 가능.

2. 명령어와 인자 파싱

c
코드 복사
for (arg = strtok_r(file_name, " ", &ptr); arg != NULL; arg = strtok_r(NULL, " ", &ptr))
    argv[argc++] = arg;
  • strtok_r를 사용하여 file_name 문자열을 공백(" ")을 기준으로 분리.
    • 첫 번째 호출: file_namestrtok_r로 전달하여 첫 번째 토큰을 추출.
    • 이후 호출: NULL을 첫 번째 인자로 전달하여 나머지 문자열에서 다음 토큰을 추출.
  • 추출된 각 토큰(명령어와 인자)을 argv 배열에 저장.
  • 인자가 없을 때까지 반복하며, argc를 증가시켜 인자 개수를 기록.

파싱 결과 예시:

명령어가 아래와 같이 전달되었다고 가정:

c
코드 복사
process_exec("program arg1 arg2 arg3")

파싱 후 변수 값:

  • argc = 4 (총 4개의 토큰)
  • argv = {"program", "arg1", "arg2", "arg3"}

추가된 코드가 필요한 이유

1. Command Line Parsing 기능 추가

  • 이전 코드는 file_name 문자열 전체를 하나의 실행 파일 이름으로 처리했습니다.
    • 예를 들어, "program arg1 arg2"를 실행할 때, "program arg1 arg2" 전체를 실행 파일 이름으로 간주했습니다.
  • 그러나, 유저 프로그램은 명령어와 인자를 분리하여야 적절히 동작합니다.
    • 예: "program"은 실행 파일 이름, "arg1 arg2"는 프로그램에 전달될 인자.
  • 이 코드를 추가함으로써, 실행 명령어와 인자를 정확히 구분하여 처리할 수 있게 되었습니다.

2. 유저 스택 초기화 준비

  • 분리된 명령어와 인자는 이후 argument_stack() 함수에서 유저 스택에 적절히 저장됩니다.
    • 이 과정을 통해 유저 프로그램은 argcargv를 통해 명령어와 인자에 접근할 수 있습니다.

관련 코드 흐름

  1. 문자열 분리

    • strtok_r를 사용하여 명령어와 인자를 분리해 argv 배열에 저장.
    • argc에 인자 개수를 기록.
  2. 유저 스택 초기화

    c
    코드 복사
    argument_stack(argv, argc, &if_);
    
    • argvargc를 사용하여 유저 스택에 명령어와 인자를 저장.
    • 이를 통해 실행된 프로그램은 main(int argc, char *argv[])의 형태로 인자 접근 가능.
  3. 실행

    • 분리된 인자를 반영하여 프로그램을 실행.

argument_stack ()

목표: process_exec() 함수에서 parsing한 프로그램 이름과 인자를 스택에 저장하기 위해 사용할 함수를 새로 선언한다.

/** #Project 2: Command Line Parsing - 유저 스택에 파싱된 토큰을 저장하는 함수 */
void argument_stack(char **argv, int argc, struct intr_frame *if_) {
    char *arg_addr[100];
    int argv_len;

    for (int i = argc - 1; i >= 0; i--) {
        argv_len = strlen(argv[i]) + 1;
        if_->rsp -= argv_len;
        memcpy(if_->rsp, argv[i], argv_len);
        arg_addr[i] = if_->rsp;
    }

    while (if_->rsp % 8)
        *(uint8_t *)(--if_->rsp) = 0;

    if_->rsp -= 8;
    memset(if_->rsp, 0, sizeof(char *));

    for (int i = argc - 1; i >= 0; i--) {
        if_->rsp -= 8;
        memcpy(if_->rsp, &arg_addr[i], sizeof(char *));
    }

    if_->rsp = if_->rsp - 8;
    memset(if_->rsp, 0, sizeof(void *));

    if_->R.rdi = argc;
    if_->R.rsi = if_->rsp + 8;
}

이 코드는 유저 스택에 명령어와 인자를 저장하는 스택 초기화 함수로, argvargc 정보를 이용하여 스택에 파싱된 데이터를 배치하고 프로그램 실행에 필요한 상태를 설정합니다.


주요 작업 흐름

  1. argv 문자열을 스택에 저장
    • 명령어와 인자 데이터를 스택에 복사하고 각 인자의 주소를 저장합니다.
  2. 스택 정렬 (Alignment)
    • 스택이 8바이트 정렬을 만족하도록 패딩을 추가합니다.
  3. 포인터 배열 저장
    • 각 인자의 주소를 스택에 저장하여, 프로그램이 argv 배열을 참조할 수 있도록 설정합니다.
  4. argcargv 전달
    • 유저 프로그램이 시작될 때 사용할 argc(인자 개수)와 argv(인자 주소 배열)를 설정합니다.

코드 단계별 분석

1. 인자 데이터를 스택에 저장

c
코드 복사
for (int i = argc - 1; i >= 0; i--) {
    argv_len = strlen(argv[i]) + 1;       // 인자의 길이를 계산 (널 문자를 포함)
    if_->rsp -= argv_len;                // 스택 포인터를 인자의 길이만큼 이동
    memcpy(if_->rsp, argv[i], argv_len); // 스택에 인자를 복사
    arg_addr[i] = if_->rsp;              // 복사한 인자의 주소를 저장
}
  • 작동 방식:
    • argv 배열의 마지막 인자부터 첫 번째 인자까지 거꾸로 스택에 저장.
    • 저장된 각 인자의 주소를 arg_addr 배열에 기록.
  • 예시 (스택 구조):
    css
    코드 복사
    [ arg3 ] <- rsp
    [ arg2 ]
    [ arg1 ]
    [ program ]
    

2. 스택 정렬 (8바이트 패딩 추가)

c
코드 복사
while (if_->rsp % 8)
    *(uint8_t *)(--if_->rsp) = 0;
  • 작동 방식:
    • 스택 포인터가 8바이트 정렬을 만족하지 않으면, 0을 추가하며 조정.
  • 목적:
    • x86-64 아키텍처에서 함수 호출 규약을 만족하기 위해 스택 포인터를 8바이트 정렬.

3. NULL 포인터 추가

c
코드 복사
if_->rsp -= 8;
memset(if_->rsp, 0, sizeof(char *));
  • 작동 방식:
    • 스택에 NULL 포인터를 추가.
  • 목적:
    • argv 배열의 끝을 나타내는 NULL 포인터를 삽입하여 프로그램이 이를 확인하도록 함.

4. 각 인자의 주소 저장

c
코드 복사
for (int i = argc - 1; i >= 0; i--) {
    if_->rsp -= 8;
    memcpy(if_->rsp, &arg_addr[i], sizeof(char *));
}
  • 작동 방식:
    • arg_addr 배열의 주소를 거꾸로 스택에 저장.
  • 결과 (스택 구조):
    css
    코드 복사
    [ &arg3 ] <- rsp
    [ &arg2 ]
    [ &arg1 ]
    [ &program ]
    

5. argc와 NULL 포인터 추가

c
코드 복사
if_->rsp -= 8;
memset(if_->rsp, 0, sizeof(void *));
  • 작동 방식:
    • NULL 포인터를 삽입해 안전한 메모리 구조를 보장.

6. argcargv 설정

c
코드 복사
if_->R.rdi = argc;
if_->R.rsi = if_->rsp + 8;
  • 작동 방식:
    • argc(인자 개수)을 rdi 레지스터에 저장.
    • argv(인자 주소 배열의 시작 주소)를 rsi 레지스터에 저장.
  • 목적:
    • 유저 프로그램이 시작될 때 main(int argc, char *argv[]) 형태로 호출되도록 설정.

전체 동작 요약

  1. 인자 데이터를 스택에 저장:
    • argv 배열의 데이터를 스택에 복사하고 주소를 기록.
  2. 스택 정렬:
    • 8바이트 정렬을 유지.
  3. 포인터 배열 저장:
    • 각 인자의 주소를 스택에 저장하여 argv 배열을 구성.
  4. 유저 프로그램 상태 설정:
    • argcargv를 레지스터에 설정하여 프로그램 실행 준비 완료.

예시 (명령어: "program arg1 arg2")

  1. 파싱 결과:

    c
    코드 복사
    argv = {"program", "arg1", "arg2"}
    argc = 3
    
  2. 스택 구조:

    css
    코드 복사
    [ NULL ]
    [ &arg2 ]
    [ &arg1 ]
    [ &program ]
    [ "arg2" ]
    [ "arg1" ]
    [ "program" ]
    
  3. 레지스터 상태:

    • rdi = 3 (argc)
    • rsi = rsp + 8 (argv 배열의 시작 주소)

process_wait ()

// process.c
// process_wait () 구현
int 
process_wait (tid_t child_tid UNUSED) {
	/* XXX: Hint) The pintos exit if process_wait (initd), we recommend you
	 * XXX:       to add infinite loop here before
	 * XXX:       implementing the process_wait. */
    /* XXX: Hint) process_wait(initd)인 경우 pintos가 종료됩니다. process_wait를 구현하기
       전에 여기에 무한 루프를 추가하는 것이 좋습니다. */
    thread_t *child = get_child_process(child_tid);
    if (child == NULL)
        return -1;

    sema_down(&child->wait_sema);  // 자식 프로세스가 종료될 때 까지 대기.

    int exit_status = child->exit_status;
    list_remove(&child->child_elem);

    sema_up(&child->exit_sema);  // 자식 프로세스가 죽을 수 있도록 signal

    return exit_status;
}

process_exit ()

// process.c
// process_exit () 구현
void 
process_exit (void) {
    thread_t *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);  // 부모 프로세스가 종료될 떄까지 대기
}

load ()

// process.c
// load () 안에 선언
    /** #Project 2: System Call - 파일 실행 명시 및 접근 금지 설정  */
    t->runn_file = file;
    file_deny_write(file); /** #Project 2: Denying Writes to Executables */



// load () 안에 기존 코드 주석 처리
    // file_close (file);

syscall.c 코드 작성

/** #Project 2: System Call */
#include <string.h>
#include "filesys/file.h"
#include "filesys/filesys.h"
#include "threads/mmu.h"
#include "threads/palloc.h"
#include "userprog/process.h"
/** -----------------------  */


/** #Project 2: 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);

    /** #Project 2: System Call - read & write 용 lock 초기화 */
    lock_init(&filesys_lock);
}

void 
syscall_handler (struct intr_frame *f UNUSED) {
    // TODO: Your implementation goes here.
    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);
    }
}

void check_address(void *addr) {
    if (is_kernel_vaddr(addr) || addr == NULL || pml4_get_page(thread_current()->pml4, addr) == NULL)
        exit(-1);
}

void halt(void) {
    power_off();
}

void exit(int status) {
    thread_t *curr = thread_current();
    curr->exit_status = status;

    /** #Project 2: Process Termination Messages */
    printf("%s: exit(%d)\n", curr->name, curr->exit_status);

    thread_exit();
}

pid_t fork(const char *thread_name) {
    check_address(thread_name);

    return process_fork(thread_name, NULL);
}

int exec(const char *cmd_line) {
    check_address(cmd_line);

    off_t size = strlen(cmd_line) + 1;
    char *cmd_copy = palloc_get_page(PAL_ZERO);

    if (cmd_copy == NULL)
        return -1;

    memcpy(cmd_copy, cmd_line, size);

    if (process_exec(cmd_copy) == -1)
        return -1;

    return 0;  // process_exec 성공시 리턴 값 없음 (do_iret)
}

int wait(pid_t tid) {
    return process_wait(tid);
}

bool create(const char *file, unsigned initial_size) {
    check_address(file);

    return filesys_create(file, initial_size);
}

bool remove(const char *file) {
    check_address(file);

    return filesys_remove(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;
}

int filesize(int fd) {
    struct file *file = process_get_file(fd);

    if (file == NULL)
        return -1;

    return file_length(file);
}

int read(int fd, void *buffer, unsigned length) {
    thread_t *curr = thread_current();
    check_address(buffer);

    struct file *file = process_get_file(fd);

    if (file == 1) {  // 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 (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;
}

int write(int fd, const void *buffer, unsigned length) {
    check_address(buffer);

    thread_t *curr = thread_current();
    off_t bytes = -1;

    struct file *file = process_get_file(fd);

    if (file == STDIN || file == NULL)  // stdin에 쓰려고 할 경우
        return -1;

    if (file == STDOUT) {  // 1(stdout) -> console로 출력
        putbuf(buffer, length);
        return length;
    }

    if (file == STDERR) {  // 2(stderr) -> console로 출력
        putbuf(buffer, length);
        return length;
    }

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

    return bytes;
}

void seek(int fd, unsigned position) {
    if (fd < 0)
        return;

    struct file *file = process_get_file(fd);

    if (file == NULL || (file >= STDIN && file <= STDERR))
        return;

    file_seek(file, position);
}

int tell(int fd) {
    struct file *file = process_get_file(fd);

    if (file == NULL || (file >= STDIN && file <= STDERR))
        return -1;

    return file_tell(file);
}

void close(int fd) {
    thread_t *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--;
}

/** #Project 2: Extend File Descriptor (Extra) */
int dup2(int oldfd, int newfd) {
    struct file *oldfile = process_get_file(oldfd);
    struct file *newfile = process_get_file(newfd);

    if (oldfile == NULL)
        return -1;

    if (oldfd == newfd)
        return newfd;

    if (oldfile == newfile)
        return newfd;

    close(newfd);

    newfd = process_insert_file(newfd, oldfile);

    return newfd;
}

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;

#ifdef VM
    /* For project 3 and later. */
    if (vm_try_handle_fault (f, fault_addr, user, write, not_present))
        return;
#endif

    /* Count page faults. */
    page_fault_cnt++;

    exit(-1); /** Test Case 가 Hardware 수준에서 페이지 폴트를 호출하기 때문에 Test Case 통과를 위해서 exception을 수정해야함. */

    /* If the fault is true fault, show info and exit. */
    printf ("Page fault at %p: %s error %s page in %s context.\n", 
            fault_addr, 
            not_present ? "not present" : "rights violation", 
            write ? "writing" : "reading",
            user ? "user" : "kernel");
    kill (f);
}

ref.
https://e-juhee.tistory.com/entry/KAIST-%EC%A0%95%EA%B8%80-9%EC%A3%BC%EC%B0%A8-System-Calls%EC%9D%B4-%ED%98%B8%EC%B6%9C%EB%90%98%EB%8A%94-%EA%B3%BC%EC%A0%95

profile
juniorDev

0개의 댓글