init进程启动流程

967 阅读3分钟

Flowchart0.png

启动前准备

上电后首先通过汇编指令去加载uboot引导程序,然后由uboot从分区中加载内核镜像等,并启动内核。

而大致是在vmlinux的入口startup_32(head.S)中为pid号为0的原始进程设置了执行环境,然后原始进程开始执行start_kernel()完成Linux内核的初始化工作。包括初始化页表,初始化中断向量表,初始化系统时间等。继而调用 fork(),创建第一个用户进程:

kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

这个进程就是著名的pid为1的init进程,它会继续完成剩下的初始化工作,然后execve(/sbin/init), 成为系统中的其他所有进程的祖先

当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。此外,还启动了Kernel的swapper进程(pid = 0)和kthreadd进程(pid = 2)。下面分别介绍下它们:

  1. swapper进程:又称为idle进程,系统初始化过程Kernel由无到有开创的第一个进程, 用于初始化进程管理、内存管理,加载Binder Driver、Display、Camera Driver等相关工作。idle进程是Linux系统第一个进程,是init进程和kthreadd进程的父进程
  2. init进程是Linux系统第一个用户进程,是Android系统应用程序的始祖,我们的app都是直接或间接以它为父
  3. kthreadd进程:Linux系统的内核进程,是所有内核进程的鼻祖,会创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程

init和kthreadd由idle创建:

USER      PID   PPID  VSIZE  RSS   WCHAN    PC          NAME
root      1     0     8216   2056         0 c7fffc10 S  /init
root      2     0     0      0            0 00000000 S  kthreadd

init进程又创建了以下核心服务进程


USER         PID   PPID  VSIZE  RSS   WCHAN            PC  NAME
root         783   1     3024   1428           0 c7fffc10 S /sbin/ueventd #日志记录
root         1080  1     1124684 75268         0 c7f28c10 S zygote
system       1073  1     5868   2124           0 c7f28c10 S /system/bin/servicemanager
root         1074  1     48060  6116           0 c7f28c10 S /system/bin/surfaceflinger
audioserver  1081  1     41576  6788           0 c7f28c10 S /system/bin/audioserver
cameraserver 1082  1     36680  6140           0 c7f28c10 S /system/bin/cameraserver
media        1089  1     72824  12608          0 c7f28c10 S /system/bin/mediaserver

init 启动

init进程的启动会首先从init进程的入口函数开始,init进程的入口函数main位于system/core/init/main.cpp中,代码如下所示

int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#endif
    // Boost prio which will be restored later
    setpriority(PRIO_PROCESS, 0, -20);
    if (!strcmp(basename(argv[0]), "ueventd")) {    // 根据参数,判断是否需要启动 ueventd
        return ueventd_main(argc, argv);    // ueventd 主要是负责设备节点的创建、权限设定等一些列工作
    }

    // 当传入的参数个数大于 1 时,执行下面的几个操作
    if (argc > 1) {
        if (!strcmp(argv[1], "subcontext")) {    // 参数为 subcontext,初始化日志系统
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap function_map;

            return SubcontextMain(argc, argv, &function_map);
        }
        if (!strcmp(argv[1], "selinux_setup")) {    // 参数为 "selinux_setup",启动 Selinux 安全策略
            return SetupSelinux(argv);
        }
        if (!strcmp(argv[1], "second_stage")) {    // 参数为 "second_stage",启动 init 进程第二阶段
            return SecondStageMain(argc, argv);
        }
    }
    // 默认启动 init 进程第一阶段
    return FirstStageMain(argc, argv);
}

主要完成以下工作:

  1. 创建和挂载启动所需的文件目录
  2. 对属性服务进行初始化
  3. 设置子进程信号处理函数,如果子进程(zygote进程)异常退出,init进程会调用该函数中设定的信号处理函数来进行处理
  4. 启动属性服务(其中会启动servicemanager(binder服务大管家)、bootanim(开机动画),mediaserver,surfaceflinger等关键服务)
  5. 解析init.rc配置文件, 启动zygote服务

参考文章: segmentfault.com/a/119000002… jsonchao.github.io/2019/02/18/…