SW사관학교 정글/WIL

System Call

아크리미츠 2024. 8. 29. 14:42

User Program 주의 주 개념인 System Call을 핀토스에서 어떻게 처리하는 지에 대해 간략하게 알아보았습니다.

 

 

먼저 System Call이 어떻게 동작하는지 대략적으로 그려보았습니다. 유저 모드에서 동작을 하고 있던 프로세스가 read나 write같은 함수에서 system call을 호출 시 syscall entry shell을 통해 현재 CPU의 동작하던 내용을 user stack에 저장한 후 커널로 넘어가게 됩니다. 그 후 syscall_handler에서 system call 함수를 실행하고, ret를 통해 다시 유저 모드로 돌아와 작업을 재개합니다.

 

 

유저 함수에서 어떠한 동작을 하다가 system call을 호출하는 경우가 발생할 수 있습니다. Pintos에서는 저희가 유저 함수를 딱히 구현한 적이 없기 때문에 do fork 함수의 error 처리 부분으로 대체했습니다. 이런 식으로 system call 함수를 호출하게 되면 다음과 같이 syscall 함수로 인자들을 통째로 넘겨주게 됩니다.

 

 

이 때 syscall 함수는 syscall을 호출하는 것이지 실제로 동작하는 함수는 아니라는 것에 유의해야합니다. 이렇게 syscall 함수를 호출하면 정수 레지스터에 인자들을 담아 전송하게 됩니다.

 

 

인자는 그대로 레지스터에 넘어가서 해당 명령을 바로 실행하게 됩니다. 이것은 커널에서 일어나는 일이기 때문에 이를 유저 모드에서 접근할 수 있도록 해줘야 합니다.

 

 

그래서 tss를 r12에 저장하여 rsp를 유저 스택에서 커널 스택으로 이동시킵니다. 이때부터 우리는 커널 모드에 있다고 보면 됩니다. 그 다음, CPU의 레지스터 정보들을 스택에 차례대로 담게 됩니다. 이 모양은 우리가 사용하는 구조체 intr_frame과 동일한 구조인 것을 알 수 있습니다. 덕분에 우리가 fork를 구현할 때 사용할 수 있습니다. 그래서 현재 스레드의 정보를 복제할 때, 커널 스택 최상단에서 intr_frame 구조체 만큼을 뺀 위치부터 데이터를 긁으면 레지스터 정보가 들어있는 것을 intr_frame 구조체 형태로 사용할 수 있습니다.

 

 

 

사용자 응용 프로그램에서 시스템 호출이 일어났을 때 범용 레지스터의 r11에는 레지스터 플래그가 들어가게 됩니다. 프로세스가 시스템 콜을 호출하면 무조건 인터럽트가 꺼지게 됩니다. 이를 시스템 핸들러를 호출하기 전에 레지스터 플래그의 9번째 bit인 인터럽트 플래그를 검사합니다. 프로세스가 시스템 콜을 호출하기 전에 인터럽트가 꺼져있었는지, 켜져있었는지 검사하여 시스템 콜 핸들러를 실행하기 전에 그 상태로 복원해줍니다. 평소에는 꺼져있을 일이 없지만 인터럽트가 꺼져있는 상태에서 시스템 콜을 호출한 후 다시 켜지는 것을 방지하기 위함입니다.

 

우리가 만든 시스템콜을 호출한다면 우리가 만든 시스템 콜 핸들러 함수의 위치를 r12 레지스터에 저장한 후 시스템 콜 핸들러 함수를 호출하여 실제로 시스템 콜을 처리합니다. sys_exit 처럼 프로세스가 끝나는 것이 아니라면 시스템 콜을 수행한 다시 스택에 저장해 놨던 내용을 꺼내서 레지스터에 복원하고 rax에 리턴 값을 저장해 놨던 것을 이용해 기존에 수행하던 동작을 재개하게 됩니다.

'SW사관학교 정글 > WIL' 카테고리의 다른 글

File System  (0) 2024.08.30
Copy on Write (CoW)  (1) 2024.08.29
Multi-Level Feedback Queue Scheduler  (0) 2024.08.27
Pintos의 Test Case는 완벽하지 않다  (0) 2024.08.16
왜 환경 별로 Test 결과가 다를까?  (0) 2024.08.16