카테고리 없음

[PintOS] Project 2 - User Programs

장영 2023. 10. 11. 14:00

System Call

  • 시스템 콜은 운영체제가 제공하는 서비스에 대한 프로그래밍 인터페이스
  • 사용자 모드 프로그램이 커널 기능을 사용할 수 있게함
  • 시스템 콜의 핵심은 시스템 콜 호출 시, 하드웨어 인터럽트가 발생하여 실행모드의 우선순위가 특수모드로 상향 조정되는 것임

System call 호출 과정

  • 핀토스의 시스템 콜은 intr_handler 함수를 통해 호출이 된다. 시스템 콜은 인터럽트의 한 종류이다.

System Call Handler

  • 유저 프로그램이 exec를 호출하면 바로 커널의 systemcall이 발동되는게 아니라 lib/user/syscall.c에 있는 exec()가 호출되고, 이 함수는 define 되어 있는 syscall1을 부른다. 그 syscall1은 위에서 #define syscall1로 정의되어 있다.
  • #define syscall1을 잘 보면 int $0x30을 호출한다. 이 명령어를 통해 userprog/syscall.c에 있는 (kernel) syscall handler를 호출하고, rax를 통해서 내가 어떤 system call을 썼는지와 그 system call을 실행하는 데 필요한 매개변수를 전달한다.
  • f->rax가 명령어가 있는 주소를 가리킨다.

File Descriptor

  • 이번 핀토스에서 구현해야 할 systemcall들은 file에 관련된 함수들이다. 파일을 열고 닫고 읽고 쓰려면 file descriptor을 사용해야한다. fd[3]부터 빈칸에 새로운 파일이 들어왔을 때, 할당해준다.
  • 명세서에 적혀 있듯이 128개의 file descriptor를 thread 구조체에 추가하고 초기화 시켰다. 새로운 file을 열 때, 이미 stdin, out, error로 할당이 되어 있기에 3부터 빈칸을 찾아 file descriptor를 할당해 주었다. 또한 close시 현재 사용중인 file desciptor을 null로 초기화 시킨다. 마지막으로 exit시 현 process의 thread에 대해 열려있는 모든 file descripter를 close 해주는 작업도 추가했다.
  • Struct file *fd[128]thread 구조체에 포함시키고 init_thread시 모든 fdNULL로 초기화 시켰다. Open시에, fd[3]부터 돌면서 빈 index를 찾아 새로 연 파일을 할당해주었다. 0,1,2stdin, stdout, stderror이 할당되어 있기 때문에 3부터 시작했다. Read, write할 때, fd2보다 크면 파일에서 읽거나 쓸 수 있도록 구현했다. 또한, exit시에 current thread의 모든 fd close해주는 기능도 추가했다. close함수 구현 시, 같은 fd를 두번 닫는 경우를 대비해 close하는 fd에 대해 NULL을 대입했다.

System Calls

  • Exit: thread_exit을 호출하기 전, thread의 모든 fd에 close하는 작업을 추가했다. 파일을 닫지 않은 채로 두면 다른 process가 접근할 수 없기 때문이다.
  • Read: fd가 2보다 크면 파일에서 읽는다는 의미이기에 else if를 추가해 조건을 추가했다. 이 경우, 받은 fd에 값이 들어있지 않으면 exit(-1), 값이 있다면 파일을 file_read함수로 읽고 읽은 byte만큼 반환한다.
  • Write: read와 마찬가지로 fd가 2보다 클때의 조건을 추가했다. write역시 받은 fd에 값이 없으면 exit(-1)을 한다. 만약 fd에 있는 파일에 누군가 접근 중이라면 접근이 종료될 때까지 기다렸다가 접근할 수 있게 한다. 그 후, file_write함수를 사용해 파일에 직접 쓰고 쓴 byte만큼 반환한다.
  • Create: 받은 파일명으로 받은 크기만큼의 파일을 filesys_create 함수를 통해 생성한다. 파일명이 NULL일경우, exit(-1)을 호출한다. 성공 여부를 bool 타입으로 반환한다.
  • Remove: 받은 파일명으로 된 파일을 filesys_remove를 통해 제거한다. Create와 같이 NULL filename이면 exit(-1)을 호출하고 반환 값은 성공여부이다.
  • Open: 받은 파일명으로 된 파일을 연다. 파일명이 없으면 exit(-1)을 호출한다. 그 파일의 주소가 user인지 확인 후, filesys_open함수를 사용해 f라는 file structure에 할당한다.  그 파일이 존재하지 않으면 -1을 반환한다. Fd[3]부터 돌아가며 NULL이 할당되어있는 index를 찾는다. NULL이면 아직 아무것도 할당되지 않은 fd이므로 그 index에 파일 f를 할당한다. 할당 전, 현 thread의 이름과 파일명이 겹치면 파일이 열려있다는 의미이므로 파일에 대한 접근이 끝날 때 까지 기다린다. 성공 시, 할당된 fd를 int형으로 반환한다.
  • Filesize: 받은 fd에 파일이 없으면 exit(-1)을 호출한다. File_length함수를 사용해 fd에 할당된 파일의 크기를 반환한다.
  • Seek: 받은 fd에 파일이 없으면 exit(-1)을 호출한다. File_seek함수를 사용해 받은 인자 position으로 fd의 커서를 움직인다. 즉, 다음에 읽을 위치를 position으로 옮기는 기능이다.
  • Tell: 받은 fd에 파일이 없으면 exit(-1)을 호출한다. File_tell함수를 사용해 fd에 할당된 파일의 커서 위치를 반환한다.
  • Close: 받은 fd에 파일이 없으면 exit(-1)을 호출한다. File_close함수를 사용해 fd에 할당된 파일을 닫는다. File_close에는 닫은 fd를 초기화 시켜주는 작업이 포함되어 있지 않으므로 파일을 닫고 난 후, fd를 NULL로 초기화 시켜준다.

Synchronization in file system

  • 한 파일에 대해 하나의 process만 접근해 쓸 수 있다. 그러므로 그 파일에 대한 fd의 deny_write를 확인해줘야 한다. 가장 먼저, file open시에 thread의 이름과 파일명을 검사한다. 같으면 현재 thread에서 그 파일을 사용하고 있다는 의미이므로 file_deny_write를 사용해 그 파일에 대한 접근이 끝날때까지 기다린다. 그 후, fd에 파일을 할당해준다. 또한, write시에 쓰려는 파일에 대해 deny_write를 체크한다. 만약 set 되어있으면 file_deny_write를 통해 파일에 대한 접근이 끝날때까지 기다린다. 그 후, write를 수행한다.
  • File에 대한 작업을 수행할 때, 여러 프로세스가 접근하게 되면 예상치 못한 반환 값을 얻을 수 있다. 그러므로 lock을 걸어줘야 한다. Syscall_init lock_init을 통해 lock을 실행시킨다. Lock이 필요한 file에 접근하는 모든 system call, exec, create, open, remove, filesize, close, seek, tell에 대해 system call을 호출하기전에 lock_acquirelock을 걸어주고 return 하기전에 lock_release로 풀어준다. Lock은 이미 걸려있는 상태에서 다시 걸게되면 kernel panic이 발생하기에 lock_release로 풀어주는 작업이 매우 중요하다.
  • 마지막으로 부모와 자식 간의 semaphore이다. 모든 process는 생성된 후, load를 통해 메모리에 올라간다. 하지만 load되기전, 부모 process가 사라지면 orphan process가 되기 때문에 자식 precessload될 때 까지 부모 processlock을 걸어줘야 한다. Thread structure에 새로운 semaphore early_lockstructure *thread parents를 생성 후, early_lockinit_thread시에 0으로 생성한다. 그리고 이미 돌아가고 있는 thread를 현재 threadparents로 지정한다. Process executethread_create로 새로운 thread를 생성하는데 이 작업이 실행된 후, sema_down을 통해 lock을 걸어준다. start_process에서 load가 완료된 후, sema_up을 통해 현재 threadparentslock을 풀어준다. Sema_up 호출시 current_thread가 아닌 current_thread->parents lock을 풀어야 한다. Start_process는 자식 thread에서 수행되는 함수이기 때문이다. 또한, 종료된 자식 processlist에서 제거하는 작업도 수행해야 한다.