[PintOS] System Call

0

pintos-project

목록 보기
6/8
post-thumbnail


pintos요약

📚 TO DO


System call은 운영체제가 제공하는 서비스에 대한 프로그래밍 인터페이스이며 유저모드 프로그램이 커널 기능을 사용할 수 있도록 시스템콜을 구현하는 것이다.

💡 SystemCall을 사용하는 이유?
가장 큰 이유는 유저 어플리케이션이 운영체제의 치명적인 데이터를 수정/삭제 하는 권한을 막기 위해서임. 직접적인 하드웨어 요청이나 기타 시스템 요청은 OS가 제공하는 System call을 통해 호출하도록 제공됨
만약 유저 어플리케이션이 System call을 호출하여 사용하면, 해당 어플리케이션은 커널모드로 잠시 전환되는 작업을 거치게 된다.

  1. 시스템 콜 번호에 해당하는 시스템 콜 호출
  2. 시스템 콜 핸들러(함수이름임)에서 RSP 주소와 인자가 가르키는 주소(포인터) 가 유저 영역인지 확인 (System call 과제를 하기 전에 주소 체크하는 함수를 만들었을 텐데 그 함수를 시스템 콜마다 쓰면된다)
  3. 유저 스택에 있는 인자들을 커널에 저장
  4. 시스템 콜 함수의 리턴 값은 RAX에 저장. (void인 경우는 할필요없음)

🤙 System Call 과정

  1. 사용자프로그램이 시스템콜을 호출한다. lib/user/syscall.c
  2. syscall.c가 해당 시스템콜의 넘버, 인자들, 해당 프로그램의 인터럽트 프레임을 정해진 순서대로 레지스터에 채워준다. 또한 시스템 호출 번호를 특정 CPU레지스터에 복사
  3. syscall명령을 CPU에게 내림. 소프트웨어 인터럽트가 발생되면서 CPU가 커널모드로 전환됨.
  4. 인수에 지정된 주소의 값을 수정하거나 사용자 메모리와 커널 메모리 간에 데이터를 전송하는 것과 같은 필요한 작업을 수행하는 서비스 루틴 은 실행 상태를 system_call 루틴으로 되돌림
  5. 핸들러는 커널 스택에서 레지스터 값을 복원하고, 스택에 시스템콜 리턴값을 RAX 레지스터에 배치함. 동시에 프로세서를 사용자 모드로 되돌림
  6. 커널은 시스템콜 루틴을 호출함. 시스템콜 테이블을 사용하여 시스템콜 서비스 루틴을 호춣고, 인자가 있는 경우 유효성 검사를 한다.

시스템콜에 대한 번호는 /include/lib/syscall-nr.h 에 들어있는데 한번 봐보자.

이 번호들 중에서 프로젝트2 부분과 SYS_DUP2를 구현하면 된다.

한양대 PDF에 있는 get_argument()는 하지 않아도됨.


HALT

PintOS를 종료하는 함수다. 어떻게 끄라는거야?라고 생각할 수 있지만
기본적으로 구현되어있는 함수를 불러오자.


EXIT

  • 현재 프로세스를 종료시키는 System Call
  • 종료시 "프로세스 이름 : exit(status)" 출력하기.
  • 정상적으로 종료시 status = 0

EXEC

file_name 카피 해주는 이유
copy를 해주지 않으면 process_exec에서 process_cleanup을 할 때 해당 file_name의 문자열의 물리적 메모리와의 매핑 정보를 담은 Page Table도 같이 지워지기 때문이다.

exec 실행 -> process_exec -> load 로 진행되어 파싱된 0번째 인덱스(파일) 을 실행해주고 컨텍스트 스위칭해주는 것이 exec시스템 콜인듯.


CREATE

  • 파일을 생성하는 콜
  • 성공일경우 true, 실패는 false 리턴
  • file : 생성할 파일의 이름 및 경로 정보
  • initial_size : 생성할 파일의 크기

이것도 원래 있던 함수를 불러오면 끝이난다.


REMOVE

  • 파일 삭제 시스템콜
  • file : 제거할 파일의 이름 및 경로 정보
  • 성공시 true, 실패일 경우 false 반환


FORK

fork는 자식프로세스를 복제하고 실행시키는 시스템콜이다.
왜 복제하는지 궁금해져서 찾아보니, 프로세스를 새로 생성해서 자료구조를 새로 싹 만드는건 비효율적이기 때문에, 기존 프로세스를 복제하는 것이라고 한다. 참고블로그

pid_t fork (const char *thread_name);
  • 현재 프로세스의 복제본인 새로운 프로세스를, thread_name으로 갖도록 생성해주는 함수.
  • %RBX, %RSP, %RBP, %R12~%R15 등등의 calle-saved register는 clone해줘야 한다.
  • 자식 프로세스의 pid를 반환해야한다.
  • 자식프로세스의 return 값은 0이어야 한다.
  • 자식은 파일 디스크립터와 virtual memory space를 포함한 중복된 자원을 갖도록 해야한다.
  • 부모 프로세스의 fork는, 자식 프로세스가 성공적으로 clone 되었는지 확인하기 전까지는 절때 return 해선 안된다.
  • 만약 자식 프로세스가 자원을 복사하는데 실패한면, 부모 프로세스의 fork() 호출에서 TID_ERROR를 return해야 한다.
  • 제공된 뼈대는, pml4_for_each()를 활용해서 user memory space 전체와 그에 대응하는 pagetable 구조체를 복사한다. 다만 pml4_for_each에 주어지는 함수에 대한 비어있는 부분들은 추가적으로 구현해주어야 한다.

fork 시스템콜 자체는 process_fork를 불러오면 끝이지만, process_fork를 구현해야함!


SEEK

  • 열린 파일에서 다음으로 읽거나 쓸 파일 내 데이터의 위치(offset)을 입력받은 position으로 옮긴다.


file_seek함수를 불러오자.


TELL

  • 열린 파일 FD에서 읽거나 쓸 다음 바이트의 위치를 반환하는데, FD는 파일의 시작 바이트를 표시한다. 파일의 시작점부터 현재 위치까지의 byte offset 반환.


WAIT

/* Wait for a child process to die. */
int wait(tid_t pid){
	process_wait(pid);
}
  • 자식 프로세스가 종료될때 까지 대기하고 올바르게 종료되었는지 자식의 exit status를 가져오는 함수
  • 만약 자식 프로세스가 여전히 실행 중이면 종료할때 까지 대기
  • 자식 프로세스가 exit()에 전달한 것과 같은 status반환
  • 만약 자식 프로세스가 exit()호출이 아니라, exception등 커널에 의해 종료된 것일면, 부모 프로세스가 호출한 wait(pid)는 -1을 리턴해야함.
  • 만약 종료된 자식 프로세스를 부모 프로세스가 기다리는 것도 가능한 대신, 부모가 자식의 exit status를 검색하거나 자식이 커널에 의해 종료되었음을 알 수 있도록 커널이 허용해줘야 함

부모프로세스는 자식의 wait_sema에 대한 sema_down을 호출해서 자식 프로세스가 실행하고 이후 종료하면서 wait_sema에 대한 sema_up을 해줄때까지 기다림


READ

  • 열린 파일의 데이터를 읽는 시스템 콜
  • fd 리턴(음이 아닌 정수값), 파일이 열리지 않았다면 -1
  • fd 0(STDIN_FILENO)는 표준입력이고, fd 1(STDOUT_FILENO)는 표준 출력이다. open 시스템콜은 이 두 fd를 절대 반환하지 않는데, 아래처럼 명시한 경우만 유효하다. 각 프로세스는 독립된 fd모음을 가지고 있다. fd는 자식 프로세스에 상속된다. 하나의 파일이 여러번 오픈된 경우, 하나의 프로세서건 다른 프로세서에서건, 각 open은 새로운 fd를 반환한다. 한 파일에 대한 fd는 별도의 close 호출으로 독립적으로 닫히고, 파일 위치를 공유하지 않는다.


WRITE

  • write() 함수는 열린 파일의 데이터를 기록하는 시스템 콜이다. 역시 fd값이 1일 때에는 표준 출력이기 때문에 1일시 putbuf()함수를 사용하여 버퍼에 저장된 데이터를 화면에 출력한다.

0개의 댓글