오랜만에 올리는 블로그 글의 주제는 OSX 의 exception handling 순서이다.
OSX kernel 구조
애플 개발자 문서 링크 – Kernel Architecture Overview
Signal handling 순서를 설명하기 전에 알아둘 사전 지식, OSX 의 커널 구조를 알아보자.
OSX 커널은 BSD와 Mach 의 디자인을 둘 다 따르고 있다.
맥 os 의 발전사를 깊이 알지 못하는 필자로선 이런 디자인이 캘베로스나 히드라의 ‘한 몸 – 두 머리’처럼 꽤나 기괴한 설계로 보인다. 운전석과 조수석에 각각 핸들이 달려있는 자동차를 상상해보자. 서로 핸들을 반대 방향으로 꺾을 경우, 이 자동차는 어디로 가야하는가?
다행히(?) OSX 에서 BSD와 Mach 사이에 이런 문제는 발생하지 않는다. 두 커널은 담당하는 레이어가 분리되어 있다. 아래 발췌해 온 문서를 참고해 보자.
Mach
Mach manages processor resources such as CPU usage and memory, handles scheduling, provides memory protection, and provides a messaging-centered infrastructure to the rest of the operating-system layers. The Mach component provides
- untyped interprocess communication (IPC)
- remote procedure calls (RPC)
- scheduler support for symmetric multiprocessing (SMP)
- support for real-time services
- virtual memory support
- support for pagers
- modular architecture
General information about Mach may be found in the chapter Mach Overview. Information about scheduling can be found in the chapter Mach Scheduling and Thread Interfaces. Information about the VM system can be found in Memory and Virtual Memory.
BSD
Above the Mach layer, the BSD layer provides “OS personality” APIs and services. The BSD layer is based on the BSD kernel, primarily FreeBSD. The BSD component provides
- file systems
- networking (except for the hardware device level)
- UNIX security model
syscall
support- the BSD process model, including process IDs and signals
- FreeBSD kernel APIs
- many of the POSIX APIs
- kernel support for pthreads (POSIX threads)
The BSD component is described in more detail in the chapter BSD Overview.
주저리 주저리 설명되어 있지만 거칠게 요약하면 Mach 커널은 리소스에 대한 보다 추상화된 영역을 관리하는 반면, POSIX 커널은 상대적으로 하드웨어에 밀접한 영역을 관리한다고 볼 수 있다.
Exception?
Exception 이란 ‘프로그램에서 정의하지 않은 동작을 처리하기 위한 추상화’ 이다. 텍스트 뷰어 프로그램을 예로 들면, 파일을 열고 안의 내용을 볼 수 있게 화면에 띄워주는 기능은 프로그램에서 정의한 동작이다. 허나 파일을 열려고 시도했는데 해당 파일이 지워져서 찾을 수 없다면? 더 이상 프로그램은 정의된 동작을 수행할 수가 없다. 이러한 ‘파일을 찾을 수 없는 상태’를 추상화하여 이를 제어하는 메커니즘을 제공하는 것이 Exception handling 이다.
Signal?
Signal 이란 OS에서 발생한 이벤트를 프로세스에게 전달하기 위한 software interrupt 이다. Signal은 경찰관이 부는 호루라기 소리와 같다. 운전자가 술을 먹고 중앙선을 침범하였을 때, 운전자를 멈춰세우고 면허를 취소시키기 위해 호루라기를 부는 것이다. 이때 보통의 운전자들은 경찰의 지시에 따라 모든 것을 체념하고 갓길에 차를 댄 후 면허증을 반납한다. 헌데 가끔씩 있다. 경찰의 부름에 제멋대로 GTA 를 찍는 돌+I 들이 말이다. 이와 유사하게, Signal이 발생했을 때 프로세스는 default action(종료, 덤프 생성 등)을 수행한다. 허나 default action을 따르지 않고 Signal handler 를 등록하여 동작을 바꿀 수도 있다.
Handling 순서
Exception 과 Signal 모두 하드웨어 리소스에 대한 프로세스의 잘못된 접근에 의해 발생할 수 있다. OSX의 경우, Mach exception handler 와 POSIX signal handler 를 둘 다 제공한다. OSX 프로그램에서 Exception handler 와 Signal handler 를 둘 다 정의한 경우 프로그램은 아래의 순서로 동작한다.
- 제일 먼저 Mach exception 이 발생한다.
- 발생한 Mach exception handler 가 있는지 찾아본다. 있으면 handler 수행 후 종료.
- exception handler 가 없으면 해당하는 POSIX signal 이 발생한다.
- 발생한 POSIX signal handler 가 있는지 찾아본다. 있으면 handler 수행 후 종료.
- Signal handler 가 없으면 EXC_CRASH exception 이 발생한다.
- Apple crash report server 로 관련 crash 를 전송한다.
한국에서는 시스템 프로그래밍을 할 기회가 많지 않은데, 하물며 OSX 계열의 시스템 api 라면 더더욱 관련 경험을 얻기가 쉽지 않다. 필자 역시도 일이 아니었더라면 OSX의 커널 아키텍처는 아마도 평생 찾아볼 일이 없었을 것이다. 혹시라도 누군가 OSX 의 하드웨어 exception을 처리하면서 어려움을 겪고 있다면 이 글이 도움이 되길 바란다. 참고로 iOS 도 구조가 유사하다.