Давно хотел рассмотреть процесс загрузки Mac OS X, давайте сделаем это вместе. Предупрежу - это моя попытка разобраться, и не рассматривайте на эту статью, как истину в последней инстанции. Это первая часть, далее я буду рассматривать более детально начальную и завершающую стадии - BootROM/EFI и launchd.

В процессе написания я использовал такую информацию:

Процесс загрузки Intel Mac (Snow Leopard):

1. При включении питания запускается BootROM firmware.

1.1. Выполняется POST (Power-On Self Test), инициализирующий некоторые системные интерфейсы, и проверяющий, что в системе установлено достаточное количество памяти и она находится в нормальном состоянии.

1.2. Запускается EFI (Extensible Firmware Interface), инициализируя остальное базовое системное "железо", и производящий выбор операционной системы.

2. Управление передаётся загрузчику boot.efi , находящемуся на разделе выбранной Mac OS X. Его основная задача - подготовить всё для загрузки ядра. boot.efi находится в /System/Library/CoreServices/ (и копия - в /usr/standalone/i386/).

2.1. Дисплей закрашивается светло-серым цветом.

2.2. Загрузчик пытается загрузить прелинкованную версию ядра (самой ядро - /mach_kernel), включающую все драйвера, необходимые для загрузки. Из-за этого время загрузки значительно сокращается. Каталог /System/Library/Caches/com.apple.kext.caches/Startup/, файлы вида kernelcache_i386.2B109974 (здесь указана архитектура и 8 символов контрольной суммы, вычисляемой по алгоритму Adler-32).

2.3. Появляется лого Apple и появляется вращающийся курсор.

2.4. Если прелинкованная версия ядра отсутствует, устарела (например, время модификации драйвера новее, чем это ядро) или повреждена, загрузчик пытается загрузить все драйвера из кеша mkext /System/Library/Caches/com.apple.kext.caches/Startup/Extensions.mkext. Описание кеша находится в /usr/standalone/bootcaches.plist.

2.5. Если кеш отсутствует, устарел или повреждён, загрузчик ищет в каталоге /System/Library/Extensions драйвера и расширения ядра, и загружает те, у которых OSBundleRequired установлено в значение, соответствующее типу загрузки (например, локальная, сетевая, etc). Детальнее можно посмотреть в "Loading Kernel Extensions at Boot Time". Например,

$ grep -1 OSBundleRequired /System/Library/Extensions/AppleBacklight.kext/Contents/Info.plist | tail -2
	</dict>
	OSBundleRequired
	Safe Boot

$ grep -1 OSBundleRequired /System/Library/Extensions/AppleRAIDCard.kext/Contents/Info.plist
	</dict>
	OSBundleRequired
	Local-Root
    Варианты OSBundleRequired:
  • Root. This KEXT is required to mount root, regardless of where root comes from – for example, platform drivers and families, PCI, or USB.
  • Network-Root. This KEXT is required to mount root on a remote volume—for example, the network family, Ethernet drivers, or NFS.
  • Local-Root. This KEXT is required to mount root on a local volume – for example, the storage family, disk drivers, or file systems.
  • Console. This KEXT is required to provide character console support (single-user mode) – for example, keyboard drivers or the ADB family.
  • Safe Boot. This KEXT is required even during safe-boot (unnecessary extensions disabled)—for example, mouse drivers or graphics drivers.

2.6. Когда ядро и все необходимые драйвера загружены (не запущены, а именно "loaded" в память), загрузчик запускает процедуру инициализации ядра. На этой стадии загружено достаточное количество драйверов для того, чтобы было найдено устройство, на котором расположена корневая файловая система (более понятно - root device).

2.7. Ядро инициализирует структуры данных Mach и BSD, а затем I/O Kit (коллекцию системных фреймворков и библиотек, поддерживающих остальные драйвера устройств). I/O Kit линкует драйвера в ядро, используя дерево устройств для определения, какие именно драйвера линковать. Это дерево строилось на стадии EFI. Посмотреть его уже из загруженной системы можно так:

$ ioreg -S -p IODeviceTree -l 0 -w
+-o Root  
  | {
  |   "IOKitBuildVersion" = "Darwin Kernel Version 10.0.0: Fri Jul 31 22:47:34 PDT 2009; root:xnu-1456.1.25~1/RELEASE_I$
  |   "IOMaximumMappedIOByteCount" = 536870912
  |   "OSPrelinkPersonalityCount" = 571
  |   "OS Build Version" = "10B504"
  |   "OSKernelCPUSubtype" = 3
  |   "OSKernelCPUType" = 7
  |   "OSPrelinkKextCount" = 139
  |   "IORegistryPlanes" = {"IOACPIPlane"="IOACPIPlane","IOPower"="IOPower","IODeviceTree"="IODeviceTree","IOService"="$
  |   "IONDRVFramebufferGeneration" = <0400000004000000>
  |   "IOConsoleUsers" = ({"kCGSSessionConsoleSetKey"=0,"kCGSSessionOnConsoleKey"=Yes,"kSCSecuritySessionID"=3172743,"k$
  |   "IOKitDiagnostics" = {"Container allocation"=3295268,"Instance allocation"=6354490,"Pageable allocation"=20925644$
  | }
...
  +-o PCI0@0  
  | | {
  | |   "compatible" = <"PNP0A03">
  | |   "IODTPersist" = 
  | |   "_STA" = 15
  | |   "IOPCIConfigured" = Yes
  | |   "acpi-address-spaces" = <0200000000000000000000000000000000000000000000000000000000000000ff000000000000000000$
  | |   "#size-cells" = <02000000>
  | |   "acpi-pci-routing-table" = <2800000000000000ffff030000000000000000005c5f53425f2e504349302e4c534d4200000000002$
  | |   "#address-cells" = <03000000>
  | |   "_ADR" = 0
  | |   "device-properties" = {"acpi-device"="IOACPIPlatformDevice is not serializable","acpi-path"="IOACPIPlane:/_SB$
  | |   "acpi-path" = "IOACPIPlane:/_SB/PCI0@0"
  | |   "name" = <"PNP0A08">
  | |   "acpi-device" = "IOACPIPlatformDevice is not serializable"
  | | }
...</small>
</pre>

2.8. Как только найдено root device, ядро его монтирует в "/".

3. Загружаются системные сервисы и ведётся подготовка системы для использования пользователями. До версии Mac OS X 10.4 этим занимались привычные для BSD Unix процессы mach_init и init (они запускали в том числе разнообразные системные скрипты /etc/rc), но с версии 10.4 они были заменены на launchd. В дополнение к инициализации системы launchd обеспечивает вызов демонов в нужном порядке. Наподобие inetd, launchd вызывает демонов по необходимости (они могут останавливаться через определённое время неактивности и перезапускаться по необходимости).

$ cat /System/Library/LaunchDaemons/com.apple.loginwindow.plist

<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">


        Label
        com.apple.loginwindow
        ProgramArguments
        
		/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow
		console
        
        KeepAlive
        


4. В завершение launchd запускает loginwindow (окно входа в систему).