[LSP] System Call 이란? (2)
안녕하세요. Team. LAMECH 입니다.
지난주에는 System Call의 개념정도를 다뤘습니다. 우는 딸아이가 핑계로 급+대충 마무리를 지었습니다. 상세한 내용을 원하신 분이 계셨다면 죄송합니다. 내용의 부족함을 느끼던 차에 울어 주었던 딸아이에게도 고맙습니다.
위키백과에는 아래와 같이 나와 있네요.
"시스템 호출(system call)은 운영 체제의 커널이 제공하는 서비스에 대해, 응용 프로그램의 요청에 따라 커널에 접근하기 위한 인터페이스이다. 보통 C나 C++과 같은 고급 언어로 작성된 프로그램들은 직접 시스템 호출을 사용할 수 없기 때문에 고급 API를 통해 시스템 호출에 접근하게 하는 방법이다." 뭐... 그렇다고 하네요.
자~ 그럼 아래 그림을 보면서 하나하나 알아가도록 하겠습니다.
위의 도식은 pritnf(3) 함수를 예로 시스템 콜이 어떻게 실행되는지 알아보도록 하겠습니다.
User API Program을 실행하게 되면 User API는 하나의 Process가 됩니다. Run-time 동안 library 함수인 printf(3) 호출하게 되고 이는 내부에 write(2)라는 system call 함수를 재차 호출합니다. 이렇게 호출된 write(2) library 함수(aka system call 함수)를 wrapper 함수라고 하며, 이상의 과정을 system call wrapper routine이라고 합니다.
이 library 함수는 CPU내에 있는 범용 register 중의 하나인 eax register에 write(2) 함수에 할당되어 있는 고유한 번호인 4를 넣습니다. 다음으로 0x80을 인자로 트랩(trap)을 걸게 됩니다. 여기서 int는 C의 자료형이 아니라 Intel CPU에서 트랩을 거는 명령입니다. CPU-mode-bit(chmodk : 0 --> 1)가 변경되면서 트랩이 걸리면 CPU의 수행모드가 사용자수준(user-space)에서 커널수준(kernel-space)로 넘어가게 됩니다. 그리고 커널은 현재 수행중이던 태스크 문맥(context)를 저장하게 됩니다.
커널은 트랩 번호인 0x80(Intel CPU에서...)을 IDT(Interrupt Descriptor Table)에 검색하여 이에 해당하는 system_call()을 엔트리(ENTRY)에서 호출합니다. system_call()은 ~/arch/x86/kernel/entry_32.S 또는 /entry_64.S(ubuntu 기준)에 구현 되어 있습니다.
이 함수는 eax(=4) 값을 index로 sys_call_table을 탐색합니다. 이 table은 ~/arch/x86/kernel/syscall_32.c 또는 /syscall_64.c에 구현되어 있으며, 각 시스템 콜에 할당되어 있는 고유한 번호는 ~/arch/x86/syscalls/syscall_32.tbl 혹은 /syscall_64.tbl에서 확인할 수 있습니다.('~'는 linux source code가 있는 디렉토리 입니다. 저의 경우는 ubuntu를 사용하는데, '~'는 /usr/src/linux-3.4.6 입니다.)
현재 eax register에 들어있는 4값으로 sys_write의 포인터 값을 받아 sys_write()함수를 호출하게 됩니다. sys_write()는 ~/fs에 구현되어 있으며, sys_fork()는 ~/arch/x86/kernel에 구현되어 있습니다.(sys_fork() 함수 내의 do_fork() 함수는 ~/kernel에 있습니다.)
참고로 linux에서 시스템 호출을 서비스 해주는 함수는 전통적으로 sys_라는 접두어가 붙습니다. 사실 한 번쯤은 자기만의 시스템 콜을 만들어 보는 것이 좋은데, kernel compile을 해야된다는 점과 그렇게 만든 시스템 콜은 자기 시스템에서만 가능하기 때문에 여기서는 과감히 생략하도록 하겠습니다.^^;