[App 개발] Mac OS X 커널의 내부 (2)
본문
"XNU" (X is Not Unix) 라는 이름의 Mac OS X 의 커널은 세 가지 요소 (Mach, BSD, I/O-Kit) 로 구성되어 있습니다.
- Mach
(GNU/HURD 를 제외하고) 아직까지 Mach 코드를 사용하고 있는 현존하는 유일한 운영체제인 Mac OS X 는 기존 코드로부터 상당히 많은 진화를 이루었습니다만 기본적인 아키텍쳐는 변하지 않았습니다. Mach (커널 소스 트리중 osfmk - OSF microkernel 의 약자) 는 어드레스 영역을 "task" 로 호칭하며, 하나의 태스크는 0 개부터 수 개의 쓰레드를 갖습니다. 특별한 규약이 없기 때문에 태스크에 대한 정보는 많지 않습니다. 유닉스 형식의 현재 작업중인 디렉토리나 환경설정 같은 것이 없습니다. 그러나 다른 운영체제와 비교해서 상당히 놀랄만한 메모리 관리 코드를 갖고 있습니다. Mach 의 가장 특징적인 요소로 Mach Messaging 이 있습니다. 태스크는 프로세스간 통신(IPC) 단말에 해당하는 다수의 "포트" 를 갖고 있습니다. 태스크는 포트를 통해서 메세지를 전송하게 되며, Mach 는 메세지의 보안, 큐 관리, 네트웍 관리, 바이트 스와핑 등을 담당합니다. 프로그래밍 편의상 Mach Interface Generator (MIG) 는 정의된 인터페이스로부터 스텁을 만들어서 두 프로세스가 서로 동일한 함수 호출을 하게끔 하는데, 내부적으로는 모두 Mach 메세지로 번역됩니다.
- BSD
커널의 BSD 는 Mach 태스크 상에서 유닉스 프로세스를 구현하며, Mach 예외처리와 Mach IPC 상에서 유닉스를 구동합니다. 여기서 TCP/IP 네트워킹과 마찬가지로 유닉스 파일시스템이 구현됩니다. BSD 형식의 파일시스템에 가상 파일시스템 컴포넌트가 설치되는 것처럼, I/O-Kit 이 /dev 인프라스트럭쳐에 설치됩니다. BSD 는 UNIX/BSD/POSIX 관련 운영체제들이 지원하는 것과 동일한 구문들, 예를 들어 open() 이나 fork() 등을 syscall 인터페이스를 통하여 지원합니다.
XNU 내에는 메세지 전송 API 를 가지는 Mach 와 POSIX 를 지원하는 BSD, 이렇게 두 개의 커널을 가지고 있으므로, syscall 도 두 종류가 있습니다. 둘 다 모두 인터럽트 0x80/sysenter/sc 엔트리 포인트를 쓰지만, 음수 syscall 은 Mach 로, 양수 syscall 은 BSD 로 전송됩니다. 응용프로그램은 인터럽트 0x80/sysenter/sc 를 직접 사용하지 않고 (Windows NT 도 마찬가지), 그 대신 libSystem 을 호출하는데, 이것이 OS X 의 libc 에 해당합니다.
- I/O-Kit
NEXTSTEP 이 OpenStep 이라는 이름으로 다를 기종에 포팅될 당시 "DriverKit" 이라는 새로운 드라이버 모델이 만들어집니다. 이것은 Objective C 언어로 구현되었으며 따라서 객체지향적이며 디바이스 드라이버의 상속 구조를 허용하였습니다. 예를 들면, IDE 버스의 입출력을 구현한 일반 IDE/ATA 디바이스 드라이버가 있으면, 일반 IDE 드라이버의 서브클래스로 하드디스크 드라이버와 CD-ROM 드라이버가 구현되고, 일반 CD-ROM 드라이버를 상속받아서 특정한 CD-ROM 기기를 지원하는 다른 CD-ROM 드라이버를 구현합니다. 이러한 구조는 반복되는 코드를 효과적으로 줄여줍니다. 리눅스와 같은 다른 운영체제와 달리, 새로운 디바이스 드라이버 코드를 구현할 때, 기존 드라이버 코드 중에서 유사한 코드를 베껴다가 만드는 것이 아니라, 기존 드라이버의 바이너리 코드로부터 서브클래스를 만들어 새로운 코드를 첨가하는 것입니다. I/O-Kit 은 DriverKit 을 간소화된 C++ 로 효율적으로 새롭게 구현하였습니다. I/O-Kit 은 유저 모드 내에서 몇몇 드라이버 클래스를 제공합니다.
- KEXTs
I/O-Kit 드라이버들은 KEXT (Kernel Extension) 로 실시간 동적 링크됩니다. KEXT 는 I/O-Kit 컴포넌트의 링크 뿐만 아니라 커널의 일부와도 링크됩니다. 이렇게 해서 파일시스템과 네트웍 KEXT (NKE) 구현이 가능하게 됩니다. 주로 /System/Library/Extensions 에 위치하는 모든 KEXT 들은 실제 바이너리와 dependency 및 링크되는 커널에 대한 정보를 담고있는 XML 설명서가 한 디렉토리에 담긴 번들로서 구성됩니다.
- 그밖의 흥미로운 요소
다음 장에는 Mac OS X 커널 안팎의 흥미로운 내용을 기술합니다.
- Booting
PowerPC 계열의 맥에서는 오픈펌웨어를 사용하였지만, 인텔 맥에서는 EFI (Extensible Firmware Interface) 를 이용합니다. 이 두 종류의 펌웨어 모두 아직도 PC 에 사용중인 16비트 BIOS 보다 훨씬 강력합니다. EFI 에서 USB 부팅 및 GPT 파티션과 FAT32 파일시스템을 지원한다는 것을 제외하면 OpenFirmware 와 EFI 는 매우 유사한 기능을 가지고 있습니다. 둘 다 파이어와이어 부팅을 지원하며, APM (Apple Partition Map) 파티션과 HFS 파일시스템, 그리고 펌웨어 레벨 드라이버들을 지원합니다. OpenFirmware 의 부트로더는 BootX 이며, EFI 의 부트로더는 boot.efi 입니다. 둘 다 HFS 를 읽을 수 있으며, 따라서 루트 파티션으로부터 커널을 읽어들일 수 있습니다. 만약 현 시스템 환경에 맞게 미리 링크된 KEXT들을 담고 있는 KEXT.cache 가 있고 /System/Library/Extensions 에 위치한 파일들보다 새것이면서 구동중인 커널보다 새것이라면, 부트로더는 캐시를 읽어들이게 됩니다. 그렇지 않으면 펌웨어에서 부트로더로 전달된 device tree 에 담겨진 장치들과 대조해 가면서 필요한 모든 KEXT들을 읽어들이게 됩니다. 그리고 다음번 부팅 속도를 빠르게 하기 위하여 새로운 KEXT 캐시가 디스크에 저장됩니다. 이 과정은 리눅스의 initrd 와 비슷하지만 좀 더 융통성있는 방법입니다.
- Mach-O
Mac OS X 는 바이너리 (실행파일, 라이브러리, KEXT 등) 용 파일 포맷으로 ELF 를 사용하지 않고, 그 대신 Mach-O 라는, 대략 비슷하지만 한 가지 흥미로운 내용이 추가된 포맷을 사용합니다. 이것은 fat 혹은 universal 바이너리라고 하는, 한 가지 이상의 아키텍쳐에 해당하는 코드를 담을 수 있는 기능입니다. 예를 들어 OS X 10.5 레오파드에서 /usr/lib/libSystem.dylib 에는 PowerPC, PowerPC 64, i386, x86_64 용 코드를 모두 담고 있습니다. 이렇게 해서 Mac OS X 10.5 레오파드 인스톨 DVD 한 장으로 네 종류의 다른 아키텍쳐에서 부팅이 가능하게 되고, lib/lib64 혹은 SYSTEM/SYSTEM32/SYSTEM64 같이 동일한 코드에 대하여 각기 다른 아키텍쳐에 대응하는 식의 구조가 필요없게 됩니다. 커널의 Mach-O 로더인 grade_binary() 함수에서 어떤 바이너리를 읽어들일지 결정하게 됩니다. 만약 시스템이 i386 이고 Mach-O 파일에는 PowerPC 코드만 들어있는 경우 코드 진행을 Rosetta 로 옮깁니다.
- Rosetta
Rosetta 는 32비트 PowerPC 코드를 i386 프로세서에서 구동하는 Transitive 사의 QuickTransit 기술을 이용한 호환성 기능입니다. PowerPC 코드를 i386 고유 코드로 실시간 재컴파일 및 에뮬레이션과 네이티브 코드의 인터페이스 관리를 담당합니다. 인터페이스는 i386 과 PPC 코드의 바이트 치환을 의미하는데, 왜냐하면 i386 은 Little Endian 그리고 PPC 는 Big Endian 이기 때문입니다. 효율 측면으로 보면 응용프로그램만 에뮬레이션 하고 네이티브 코드의 라이브러리를 링크하는 것이 바람직하나, 네이티브 코드와 에뮬레이션된 코드와의 인터페이스는 매우 어려운 작업이므로 불가능에 가깝습니다. 호환성을 증대시키는 쉬운 방법은 응용프로그램 및 라이브러리를 모두 에뮬레이션하는 것이며, 바이트 치환은 네이티브 커널과의 syscall 에서만 수행하는 것입니다. 이 방식의 부작용이라면, 단 한 개의 PowerPC 응용프로그램을 돌리기 위해서라도 인텔 시스템 내에 모든 PPC 시스템 라이브러리를 보유하고 있어야 한다는 점입니다.
이 놀라운 기술을 직접 실험해보려면 /usr/libexec/oah/translate 를 수동으로 실행하여 PowerPC 코드를 강제로 에뮬레이션하면, 인텔 코드가 있는 프로그램이라도 PPC 로 실행됩니다.
최신글이 없습니다.
최신글이 없습니다.
댓글목록 1
이성훈님의 댓글
잘 봤습니다.