iOS system and kernel (XNU) architecture

iOS system architecture

image.png

Divided into four layers from top to bottom:

  • User Experience Layer
  • The application framework layer will App开发be used
  • Core framework layer.
  • The Darwin layer belongs to the iOS system 核心and belongs to the kernel state.

The kernel of Darwin is XNU, which has UNIXmade many improvements and innovations on the basis of XNU.

XNU kernel architecture

image.png

  • XNU consists of Mach, BSD, and IOKit(驱动API).
  • Mach and BSD are responsible for different tasks of the system shown in the figure

Mach

Mach is the degree to which 微内核microkernels can improve a system 模块化by providing 内存保护a message-passing mechanism.

BSD

BSD is an adaptation of Mach again 封装, 宏内核providing a more modern and easier-to-use kernel interface

宏内核Also called a single core, it has higher performance and still keeps running under high load conditions 高效.

BSD is POSIX compliant

POSIXIEEE has developed standards to ensure that software runs on various UNIX systems , and iOS has BSDbecome compatible with POSIX 类 UNIX 系统.

For example, BSD will build a UNIX process model and create a POSIX-compatible thread model pthread.

process

Mach

A process is represented as a Task in Mach, Mach Taskwhich is the environment and container for thread execution.

The user state triggers a trap through the mach_msg_trap() function, and switches to the Mach kernel state, which is completed by the mach_msg() function in Mach 进程间通信.

Mach uses the mach_msg_trap() function to trigger a trap for processing 异常消息,

BSD

进程在BSD中表示为Process,BSD Process扩展了 Mach Task增加进程 ID信号信息等,。

BSD 在Mach异常消息机制的基础上建立了信号处理机制,用户态产生的信号会先被 Mach 转换成异常,BSD 将异常再转换成信号

线程

Mach Thread 表示一个线程,是 Mach 里的最小执行单位。Mach Thread 有自己的状态,包括机器状态、线程栈、调度优先级、调度策略、内核 Port、异常 Port。

Mach Thread 也可以扩展为 Uthread,通过 `BSD Process`` 处理。

IOKit

IOKit 是硬件驱动程序的运行环境,包含电源、内存、CPU 等信息。

IOKit 底层 libkern 使用 C++ 子集 Embedded C++ 编写了驱动程序基类,比如 OSObject、OSArray、OSString 等,新驱动可以继承这些基类来写。

XNU 加载 App

iOS 的可执行文件和动态库都是 Mach-O 格式,所以加载 APP 实际上就是加载 Mach-O 文件。

苹果公司已经将 darwin_xnu 开源,放到Github了, 地址是 darwin-xnu

整个 fork 进程,加载解析 Mach-O 文件的过程可以在 XNU 的源代码中查看,代码路径是 darwin-xnu/bsd/kern/kern_exec.c,代码如下:

int __mac_execve(proc_t p, struct __mac_execve_args *uap, int32_t *retval)
{
    // 字段设置
    int is_64 = IS_64BIT_PROCESS(p);
    struct vfs_context context;
    struct uthread  *uthread; // 线程
    task_t new_task = NULL;   // Mach Task(进程)
    
    context.vc_thread = current_thread();
    context.vc_ucred = kauth_cred_proc_ref(p);
    
    // 分配大块内存,不用堆栈是因为 Mach-O 结构很大。
    char *bufp = kheap_alloc(KHEAP_TEMP,
	    sizeof(*imgp) + sizeof(*vap) + sizeof(*origvap), Z_WAITOK | Z_ZERO);
    image_params *imgp = (struct image_params *) bufp; // Mach-O参数
    
    // 初始化 imgp 结构里的公共数据
    imgp->ip_user_fname = uap->fname; // 可执行程序的文件名
	imgp->ip_user_argv = uap->argp; // 参数列表
	imgp->ip_user_envv = uap->envp; // 环境列表
    
    uthread = get_bsdthread_info(current_thread()); // 初始化线程
    if (uthread->uu_flag & UT_VFORK) {
        imgp->ip_flags |= IMGPF_VFORK_EXEC;
        in_vfexec = TRUE;
    } else {
        // 程序如果是启动态,就需要 fork 新进程
        imgp->ip_flags |= IMGPF_EXEC;
        // fork 新进程和线程
        imgp->ip_new_thread = fork_create_child(current_task(),
                    NULL, p, FALSE, p->p_flag & P_LP64, TRUE);
 
        new_task = get_threadtask(imgp->ip_new_thread);
        context.vc_thread = imgp->ip_new_thread;
    }
    
    // 加载解析 Mach-O
    error = exec_activate_image(imgp);

    if (!error && !in_vfexec) {
        p = proc_exec_switch_task(p, current_task(), new_task, imgp->ip_new_thread);
    
        should_release_proc_ref = TRUE;
    }
 
    kauth_cred_unref(&context.vc_ucred);
    
    if (!error) {
        task_bank_init(get_threadtask(imgp->ip_new_thread));
        proc_transend(p, 0);
 
        thread_affinity_exec(current_thread());
 
        // 继承进程处理
        if (!in_vfexec) {
            proc_inherit_task_role(get_threadtask(imgp->ip_new_thread), current_task());
        }
 
        // 设置进程的主线程
        thread_t main_thread = imgp->ip_new_thread;
        task_set_main_thread_qos(new_task, main_thread);
    }
    ...
}

Since the Mach-O file is very large, the __mac_execve function will first allocate a large 堆内存imgp for Mach-O, and then initialize the public data in the imgp. After the memory is processed, a new process and thread are forked through the fork_create_child() function. After the new process forks, it will analyze and load the Mach-O file into the memory imgp through the exec_activate_image() function. Finally, use the task_set_main_thread_qos() function to set the qos of the newly forked process 主线程.

exec_mach_imgact()Load the Mach-O file through the load_machfile() function, and load it into memory through mapping according to the load command information obtained after analyzing Mach-O. Also use the activate_exec_state() function to process and parse the structural information and settings after Mach-O is loaded 执行 App 的入口点.

After setting the entry point, the load_dylinker() function will be used to parse and load dyld, and then change the entry point address to the entry address of dyld. After this step, 内核the loading of the Mach-O file is partially completed. All that is left is to load the App 用户态layer dyld.

The complete process of XNU loading App

  1. fork new process;

  2. Allocate memory for Mach-O;

  3. Parse Mach-O;

  4. Read Mach-O header information;

  5. Traverse the load command information and map Mach-O to memory;

  6. Start the user mode dyld, the follow-up process has nothing to do with the kernel mode darwin_xnu, the dyld process can refer to iOS startup optimization - dyld4 process introduction .

Guess you like

Origin juejin.im/post/7255902957035438138