iOS系统内核XNU
iOS系统架构
基于ARM架构
- 用户体验层:主要提供用户界面。这一层包含了SpringBoard,Spotlight,Accessibility。
- 应用架构层:开发者会用到,包含了Cocoa Touch。
- 核心架构层:系统核心功能的框架层。包含了各种图形和媒体核心框架,Metal等。
- Darwin层:操作系统的核心,属于操作系统的内核态。包含了系统内核XNU,驱动等
XNU
XNU内部由Mach,BSD,驱动API IOKit组成,这些都依赖于libkern,libsa,Platform Expert。
Mach
Mach作为UNIX内核的替代,主要解决UNIX一切皆文件导致抽象机制不足的问题,为现代操作系统坐了进一步的抽象工作。Mach负责操作系统最基本的工作,包括进程和线程抽象,处理器调度,进程间通信,消息机制,虚拟内存管理,内存保护等。
进程对应到Mach是Mach Task,Mach Task可以看做是线程执行环境的抽象,包含虚拟地址空间,IPC空间,处理器资源,调度控制,线程容器。
进程在BSD里有BSD Process处理,BSD Process扩展了Mach Task,增加了进程ID,信号信息等,BSD Process里面包含了扩展Mach Thread结构的Uthread。
Mach的模块包括进程和线程都是对象,对象之间不能直接调用,只能通过Mach Msg进行通信,也就是 mach_msg()函数。
每个Mach Thread 表示一个线程,是Mach里的最小执行单元。Mach Thread有自己的状态,包括及其状态,线程栈,调度优先级(有128个,数字越大表示优先级越高),调度策略,内核Port,异常Port。
Mach Thread既可以有Mach Task处理,也可以扩展为Uthread,通过BSD Process处理。这是因为XNU采用的是微内核Mach和宏内核BSD的混合内核,具备微内核和宏内核的优点。
- 微内核剋提高系统的模块化程度,提供内存保护的信息传递机制
- 宏内核可以叫单内核,在出现高负荷状态时依然能够让系统保持高效运作
XNU加载App
iOS的可执行文件和动态库都是Mach-O格式,所以加载App实际上就是加载Mach-O文件。
Mach-O header信息结构代码:
1 | struct mach_header_64 { |
其中filetype有
- OBJECT,指的是.O文件或者.a文件
- EXECUTE,指的是IPA拆包后的文件
- DYLIB,指的是.dylib或.framework文件
- DYLINKER,指的是动态链接器
- DSYM,指的是保存有符号信息用于分析闪退信息的文件
加载Mach-O文件,内核会fork进程,并对进程进行一些基本设置,比如为进程分配虚拟内存,为进程创建主线程,代码签名等。用户态dyld会对Mach-O文件做库加载和符号解析。
1 | int __mac_execve(proc_t p, struct __mac_execve_args *uap, int32_t *retval) |
由于Mach-O文件,_mac_execve
函数会先为Mach-O分配一大块内存imgp,接下来会初始化imgp里的公共数据。内存处理完,_mac_execve
函数就会通过fork_create_chhild()
函数fork出一个新的进程。新进程fork后,会通过exec_activate_image()
函数解析Mach-O文件到内存imgp里。最后使用task_set_main_thread_qos()
函数设置新fork出进程的主线程。
exec_activate_image()
函数
1 | struct execsw { |
加载Mach-O文件就是exec_mach_imgact()
函数。exec_mach_imgact()
通过load_machfile()函数加载Mach-O文件,根据解析Mach-O后得到的load command信息,通过映射方式加载到内存中。还使用activate_exec_state()
函数处理解析加载Mach-O后到结构信息,设置执行App的入口点。
设置完入口点后会通过load_dylinker()函数来解析加载dyld,然后将入口点地址改成dyld的入口地址。这一步完后,内核部分就完成了Mach-O文件的加载,剩下的就是用户态层dyld加载App了。
Dyld的入口函数是_dyld_start
,dyld属于用户态进程,不再XNU里,__dyld_start
会加载App相关的动态库,处理完成后返回App的入口地址,然后到App的main函数。