[Pintos] - User Program(System call) - 1

Junyeong Fred Kim·2022년 1월 5일
0

운영체제

목록 보기
11/27

시스템 콜 핸들러 구현

🎯 과제 목표

시스템 콜 핸들러 및 시스템 콜 구현

📢 과제 설명

  • Pintos는 시스템 콜 핸들러가 구현되어 있지 않아 시스템 콜이 호출될 수 없으므로 응용 프로그램이 정상적으로 동작하지 않는다.
  • 사용자는 Pintos의 시스템 콜 매커니즘을 이해하고 시스템 콜 핸들러를 구현한다.
  • 시스템 콜을 구현하고 시스템 콜 핸들러를 통해 호출 한다.

시스템 콜

  • 시스템 콜은 운영체제가 제공하는 서비스에 대한 프로그래밍 인터페이스이다.
  • 사용자 모드 프로그램이 커널 기능을 사용할 수 있도록 함.
  • 시스템 콜은 커널 모드에서 실행되고, 처리 후 사용자 모드로 복귀된다.


시스템 콜 호출 과정 (리눅스)


시스템 콜 호출 과정 (Pintos)


시스템 콜 핸들러 구현 요구사항

시스템 콜 핸들러 구현

  • 시스템 콜 핸들러에서 시스템 콜 번호에 해당하는 시스템 콜 호출
  • 시스템 콜 핸들러에서 유저 스택 포인터(esp) 주소와 인자가 가리키는 주소(포인터)가 유저 영역인지 확인
    (pintos는 유저 영역을 벗어난 주소를 참조할 경우 페이지 폴트가 발생한다.)
  • 유저 스택에 있는 인자들을 커널에 저장
  • 시스템 콜의 함수의 리턴 값은 인터럽트 프레임의 eax에 저장

시스템 콜 구현

시스템 콜을 구현하기 전, 64bit system call table을 먼저, 알아볼 필요가 있다.

64bit system call handle

include/lib/syscall-nr.h

/* System call numbers. */
enum {
	/* Projects 2 and later. */
	SYS_HALT,                   /* Halt the operating system. */
	SYS_EXIT,                   /* Terminate this process. */
	SYS_FORK,                   /* Clone current process. */
	SYS_EXEC,                   /* Switch current process. */
	SYS_WAIT,                   /* Wait for a child process to die. */
	SYS_CREATE,                 /* Create a file. */
	SYS_REMOVE,                 /* Delete a file. */
	SYS_OPEN,                   /* Open a file. */
	SYS_FILESIZE,               /* Obtain a file's size. */
	SYS_READ,                   /* Read from a file. */
	SYS_WRITE,                  /* Write to a file. */
	SYS_SEEK,                   /* Change position in a file. */
	SYS_TELL,                   /* Report current position in a file. */
	SYS_CLOSE,                  /* Close a file. */

	/* Project 3 and optionally project 4. */
	SYS_MMAP,                   /* Map a file into memory. */
	SYS_MUNMAP,                 /* Remove a memory mapping. */

	/* Project 4 only. */
	SYS_CHDIR,                  /* Change the current directory. */
	SYS_MKDIR,                  /* Create a directory. */
	SYS_READDIR,                /* Reads a directory entry. */
	SYS_ISDIR,                  /* Tests if a fd represents a directory. */
	SYS_INUMBER,                /* Returns the inode number for a fd. */
	SYS_SYMLINK,                /* Returns the inode number for a fd. */

	/* Extra for Project 2 */
	SYS_DUP2,                   /* Duplicate the file descriptor */

	SYS_MOUNT,
	SYS_UMOUNT,
};

쉬운 것(?).. 쉬운 것은 없다... 그나마 간단한 것 부터 해보겠다.


📟 syscall_handler(struct intr_frame *f UNUSED);

✏️ 구현

// userprog/syscall.c

void
syscall_handler (struct intr_frame *f UNUSED) {
	switch (f->R.rax)
	{
	case SYS_HALT:
		halt();
		break;
	case SYS_EXIT:
		exit(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;
	default:
		exit(-1);
		break;
	}
}

물론 구현해야할 함수가 더 많지만, 이번 글에서 다룰 것들만 case에 추가해서 구현해보겠다.

설명

  • struct intr_frame안에 struct gp_registers R;로 선언되어있다. 구조체 안의 구조체로 f->R.형식을 사용한다.

  • 시스템 호출 핸들러 syscall_handler()가 제어권을 얻으면 시스템 호출 번호는 rax에 있고 인수는 %rdi, %rsi, %rdx, %r10, %r8%r9 순서로 전달된다.


📟 void halt (void) NO_RETURN;

✏️ 구현

// userprog/syscall.c

#include threas/init.h // power_off를 include하기 위해

void halt (void)
{
    power_off(); //
}

pintos를 종료시키는 함수, power_off()를 한다.


📟 void exit (int status) NO_RETURN;

✏️ 구현

// userprog/syscall.c

void exit(int status)
{
    /* 실행중인 스레드 구조체를 가져옴 */
    /* 프로세스 종료 메시지 출력, 출력 양식: “프로세스이름: exit(종료상태)” */
    /* 스레드 종료 */
    
    struct thread *curr = thread_current ();
    curr -> exit_status = status;

    printf("%s: exit(%d)\n", thread_name (), status);
    thread_exit ();
}

현재 thread(process)를 종료시키는 명령이다. status가 0인 경우가 정상종료 되는 경우다.


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

✏️ 구현

// userprog/syscall.c

bool create(const char *file, unsigned initial_size)
{
    /* 파일 이름과 크기에 해당하는 파일 생성 */
    /* 파일 생성 성공 시 true 반환, 실패 시 false 반환 */
    check_address (file);
    return filesys_create(file, initial_size);
}

check_address로 file을 확인한 후, filesys_create()를 통해서 새로운 file을 만든다.

🙋 help func.

bool filesys_create(const char *name, off_t initial_size)

  • 파일 이름과 파일 사이즈를 인자 값으로 받아 파일을 생성하는 함수

📟 bool remove (const char *file);

✏️ 구현

// userprog/syscall.c

bool remove(const char *file)
{
    /* 파일 이름에 해당하는 파일을 제거 */
    /* 파일 제거 성공 시 true 반환, 실패 시 false 반환 */
    check_address (file);
    return filesys_remove (file);
}

check_address로 file을 확인한 후, filesys_remove를 한다.

🙋 help func.

bool filesys_remove(const char *name)

  • 파일 이름에 해당하는 파일을 제거하는 함수

📟 void check_address(const uint64_t *uaddr);

✏️ 구현

// userprog/syscall.c

void check_address(const uint64_t *uaddr)
{
      struct thread *curr = thread_current ();
      if (uaddr == NULL || !(is_user_vaddr(uaddr)) || pml4_get_page(curr->pml4, uaddr) == NULL) {
        exit(-1);		
      }
}

주소 값이 유저 영역에서 사용하는 주소 값인지 확인 하는 함수.
Pintos에서는 시스템 콜이 접근할 수 있는 주소를 0x8048000~0xc0000000으로 제한함.
유저 영역을 벗어난 영역일 경우 프로세스 종료(exit(-1))


pml4관련 코드 파악하기 - mmu.c

pml4?

It's short for Page Map Level 4. A bit of explanation can be found here. Basically it's just the way AMD decided to label page tables.

  • pml4_create:
    • 커널 가상 주소 공간을 위한 새로운 pml4를 생성, 유저 가상 주소 공간은 해당되지 않는다.
  • pml4_get_page
    • pml4에서 사용자 가상 주소 UADDR에 해당하는 실제 주소를 찾는다. 해당 물리적 주소에 해당하는 커널 가상 주소 또는 UADDR이 매핑되지 않은 경우 널 포인터를 반환
  • pml4e_walk
    • 가상 주소 vaddr에 대한 페이지 테이블 엔트리의 주소를 반환한다.
    • 페이지 테이블이 없는 경우 create 값에 따라 행동이 결정
      • create가 true인 경우는 새로운 페이지 테이블을 만들고 포인터를 반환한다.
      • false인 경우에는 Null pointer를 반환한다.
  • pml4_destroy
    • 페이지들을 전부 해제하고 pml4e를 제거해준다.
    • PML4 (vaddr) >= 1, it's kernel space by define.

📌 중간 확인

yeah ~

pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/halt:halt -- -q   -f run halt < /dev/null 2> tests/userprog/halt.errors > tests/userprog/halt.output
perl -I../.. ../../tests/userprog/halt.ck tests/userprog/halt tests/userprog/halt.result
pass tests/userprog/halt
pintos -v -k -T 60 -m 20   --fs-disk=10 -p tests/userprog/exit:exit -- -q   -f run exit < /dev/null 2> tests/userprog/exit.errors > tests/userprog/exit.output
perl -I../.. ../../tests/userprog/exit.ck tests/userprog/exit tests/userprog/exit.result
pass tests/userprog/exit
profile
기억보다 기록

0개의 댓글