Bootloader کارش را انجام داده است. Kernel، ramdisk و اطلاعات سخت افزار در RAM قرار گرفته اند و CPU به نقطه شروع Linux Kernel پرش کرده است. از این لحظه Bootloader دیگر مدیر سیستم نیست، Linux کنترل را در دست می گیرد.
Bootloader
↓ jump
Linux Kernel ← شروع این مقاله
↓
/init
↓
Android userspace
اما شروع Kernel هنوز به معنی بالا آمدن Android نیست. بین اولین دستور Linux و ظاهر شدن Android Framework چند مرحله مهم وجود دارد.
Kernel ابتدا خود Linux را زنده می کند
Kernel در شروع باید پایه های سیستم عامل را آماده کند. Memory Management، Scheduler، Interruptها، Timerها و زیرساخت Driverها در همین بخش راه اندازی می شوند.
Device Tree و Firmware interfaces اطلاعات لازم درباره Hardware را در اختیار Kernel قرار می دهند تا Driverها بتوانند Deviceهای موجود را شناسایی کنند. Storage Controller، USB، Display و بسیاری از اجزای دیگر در این مسیر به تدریج قابل استفاده می شوند.
Kernel entry
↓
CPU / memory setup
↓
MMU and page tables
↓
scheduler / interrupts
↓
drivers
↓
initial root filesystem
Driverهای Vendor و GKI
در معماری GKI تلاش شده Kernel اصلی عمومی تر باشد و بخش بیشتری از Driverهای مخصوص Device به شکل Kernel Module جدا شوند. Moduleهایی که برای ادامه Boot ضروری هستند باید خیلی زود Load شوند، برای نمونه Driverی که برای دسترسی به Storage یا Filesystem اولیه لازم است.
بخشی از این Moduleها می توانند داخل vendor ramdisk قرار داشته باشند تا پیش از Mount شدن کامل Android در دسترس باشند.
اولین Process در Userspace، اجرای /init
وقتی Kernel به حد کافی آماده شد Filesystem اولیه ramdisk را در دسترس قرار می دهد و فایل /init را اجرا می کند.
این Process شناسه
PID
شماره 1 را می گیرد.
First-stage init، آماده کردن پارتیشن های واقعی Android
در این مرحله هنوز محیط بسیار محدود است. First-stage init باید چیزهایی را آماده کند که second-stage init و بقیه Android برای ادامه کار به آنها نیاز دارند.
- Load کردن Moduleهای ضروری Early Boot
- خواندن اطلاعات Mount و fstab
- ساخت Mapping مربوط به Dynamic Partitions
- آماده کردن dm-verity و AVB Mappingها
- Mount کردن پارتیشن های لازم مثل system و vendor
/init
↓
first-stage init
├─ load early modules
├─ map logical partitions
├─ prepare dm-verity
└─ mount Android partitions
اگر super و Logical Partitionها وجود داشته باشند، همین بخش باید Device Mapper را طوری آماده کند که system، vendor و product به شکل Block Deviceهای قابل Mount در دسترس قرار بگیرند.
SELinux قبل از ورود کامل به second-stage
بعد از Mountهای اولیه، init باید Policy امنیتی SELinux را آماده و Load کند. مسیر را می توان به شکل ساده این طور دید:
first-stage init
↓
SELinux setup
↓
second-stage init
این مرحله مهم است چون Android نمی خواهد Serviceهای اصلی را در محیطی اجرا کند که Policy امنیتی هنوز اعمال نشده است.
Second-stage init، جایی که Android واقعا شروع به شکل گرفتن می کند
حالا init وارد محیط کامل تری شده است. پارتیشن های اصلی Mount شده اند، SELinux آماده است و فایل های Configuration Android در دسترس قرار دارند.
Second-stage init فایل های init.rc و فایل های RC مربوط به Device و Serviceها را می خواند.
Android init زبان Configuration خودش را دارد و Bash Script نیست.
service example /system/bin/example
class core
user system
on property:demo.ready=1
start example
Actionها با Triggerهایی مثل early-init، init، late-init یا تغییر Property اجرا می شوند. به این ترتیب Boot Android به صورت یک زنجیره از Eventها و Serviceهایی که در زمان مناسب Start می شوند جلو می رود.
Property Service، یک سیستم ساده ولی بسیار مهم
Android از Propertyها برای نگهداری بخش زیادی از State و Configuration استفاده می کند. تغییر یک Property می تواند Trigger جدیدی در init فعال کند و باعث Start یا Stop شدن Serviceها شود.
به همین دلیل هنگام Debug Boot، دیدن Propertyها و Triggerهای init می تواند مشخص کند سیستم تا کدام مرحله رسیده است.
🔍 عمیق تر بشویم، Bootstrap APEXها چه زمانی وارد Boot می شوند
APEX قالب ماژولار Android برای بعضی اجزای System است. چند APEX حیاتی باید خیلی زود در Boot قابل استفاده شوند.
Bootstrap APEX activation بعد از ورود به second-stage init و در فاز بسیار اولیه آن انجام می شود.
سرویس apexd-bootstrap این APEXهای اولیه را Mount می کند تا کتابخانه های لازم برای Serviceهای بعدی در دسترس باشند.
first-stage init
↓
SELinux setup
↓
second-stage init
↓ early-init
apexd-bootstrap
↓
bootstrap APEXes
ueventd و Serviceهای Native، اتصال Linux به دنیای Android
در ادامه init Serviceهای بیشتری را بالا می آورد.
یکی از اجزای مهم ueventd است که رویدادهای Device از Kernel را مدیریت می کند و در ساخت Device Nodeها، Permissionها و بعضی Firmware loadingها نقش دارد.
Serviceهای Native پایه نیز به تدریج شروع می شوند. در مقاله بعد همین مسیر را تا Binder، Zygote، system_server و در نهایت Launcher دنبال می کنیم.
/data و Encryption، Boot سیستم با Unlock کاربر یکی نیست
در دستگاه های جدید بخش های مختلف Data در زمان یکسان قابل دسترسی نمی شوند. File Based Encryption اجازه می دهد بخشی از Data که برای Boot اولیه لازم است قبل از Unlock کاربر قابل استفاده باشد، در حالی که Credential Encrypted Data بعد از باز شدن قفل کاربر در دسترس قرار می گیرد.
پس بالا آمدن Android و باز شدن کامل اطلاعات شخصی کاربر دو اتفاق جدا هستند.
Linux Kernel
↓
/init PID 1
↓
first-stage init
↓
SELinux setup
↓
second-stage init
↓
native Android services
↓
Zygote / system_server
Bootloader فقط Linux را شروع می کند. Kernel سخت افزار و زیرساخت سیستم عامل را آماده می کند، بعد /init به عنوان PID 1 اجرا می شود. First-stage init پارتیشن های واقعی Android را آماده می کند و second-stage init Serviceهای Android را یکی یکی بالا می آورد.
واژه نامه کوتاه
- PID 1
- اولین Process Userspace که Kernel اجرا می کند، در Android همان init است.
- first-stage init
- مرحله اولیه init برای آماده کردن Mountها، Logical Partitionها و Verification.
- second-stage init
- مرحله اصلی Android init برای پردازش rc fileها و Start کردن Serviceها.
- SELinux
- سیستم کنترل دسترسی اجباری که Android برای محدود کردن Processها و Serviceها استفاده می کند.
- APEX
- قالب ماژولار Android برای توزیع و Update بعضی اجزای System.