Research on the startup process of Android Init process (Android 10.0)

Table of contents

Linux kernel boot

Android init process starts

FirstStageMain

SetupSelinux

SecondStageMain

Summary and harvest

thanks and reference


The main content of this article is the first part of the startup process in the Android source code, including the startup part of the Linux kernel and the startup part of the Android init process.

Linux kernel boot

Why do I mention Linux startup first? On the one hand, the Linux kernel is the foundation of the Android platform. On the other hand, I have recently come into contact with some basic knowledge of Linux, so I hope to record all the things I have learned.

The role of the kernel is actually to control computer hardware resources and provide a program operating environment, such as: executing programs, file operations, memory management, and device drivers, etc. The external interfaces provided by the kernel are also called system calls.

Since the kernel is so important and provides the services required by various programs to run, it is definitely necessary to start the kernel before starting Android. How to start the specific kernel, let's take a look at what happens when we press the power button.

After the computer is powered on, it will first go to ROM (read-only memory), where some initialization programs are solidified. This program is also called BIOS. The specific steps are as follows:

Read BIOS (Basic Input Output System, placed in ROM):

  • Hardware self-test, that is, to check whether the computer hardware meets the basic conditions for operation;
  • Check the startup sequence in this program. Of course, this can be adjusted by yourself. At this time, follow the startup sequence to find where the startup program of the next stage is;

Master Boot Record (the first place in the BIOS that gives control to the boot sequence):

  • Read the first 512 bytes of the first sector of the device. If it ends with a specific character, it means that the device can be used for booting. If not, transfer control to the next device according to the boot sequence in the BIOS just now. The first 512 bytes are also called Master Boot Record (MBR);
  • The 512 bytes in the MBR can't fit too much, so it mainly tells the computer where to find the operating system (on the hard disk);
  • At this time, find the corresponding location on the hard disk through the MBR partition table;

Start the operating system through the boot loader:

  • Linux uses Grub2, which is a boot manager that loads various imgs;
  • The operating system kernel is loaded into memory;
  • After that, the initial process (0 / 1 / 2) will be created, and the process No. 1 will load other content in the user mode later;

And if you are familiar with Linux, you will know that the entry function started by Linux is start_kernel (in init/main.c), what important things are done in it:

  • No. 0 process is created (it will evolve into an idle process later);
  • System call initialization;
  • Memory management system initialization;
  • Scheduling system initialization;
  • Other initializations:
    • Process No. 1 is created (user mode);
    • No. 2 process creation (kernel state);

Android init process starts

The No. 1 process mentioned above is also called the init process, and when the No. 1 init process is created, it will execute main.cpp under system/core/init in the Android source code, and it will call different methods according to different parameters:

int main(int argc, char** argv) {
    // 略一部分
    // ueventd 主要用来创建设备节点
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }
    if (argc > 1) {
        // 略一部分
        // selinux_setup
        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }
        // second_stage 
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv);
}
复制代码

By reading system/core/init/README.md, we can know that the main function will be executed multiple times, and the startup sequence is FirstStageMain -> SetupSelinux -> SecondStageMain.

So let's take a look at what these three parts have done:

FirstStageMain

// 文件位置:system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) { 
    //  ...
    //  其实上面省略的基本是挂载文件系统、创建目录、创建文件等操作
    //  比如挂载的有:tmpfs、devpts、proc、sysfs、selinuxfs 等
    //  把标准输入、标准输出、标准错误重定向到 /dev/null
    SetStdioToDevNull(argv);
    //  初始化本阶段内核日志
    InitKernelLogging(argv);
    //  ...
    //  比如获取 “/” 的 stat(根目录的文件信息结构),还会判断是否强制正常启动,然后切换 root 目录
    //  这里做了几件事:初始化设备、创建逻辑分区、挂载分区
    DoFirstStageMount();
    //  ...
    //  再次启动 main 函数,只不过这次传入的参数是 selinux_setup
    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args));
}
复制代码

The first stage is more about file system mounting, directory and file creation, why mount, so that they can be used, and after these are completed, call the main function again to enter the SetupSelinux stage.

SetupSelinux

// 文件位置:system/core/init/selinux.cpp
int SetupSelinux(char** argv) {
    //  初始化本阶段内核日志
    InitKernelLogging(argv);
    //  初始化 SELinux,加载 SELinux 策略
    SelinuxSetupKernelLogging();
    SelinuxInitialize();
    //  再次调用 main 函数,并传入 second_stage 进入第二阶段
    //  并且这次启动就已经在 SELinux 上下文中运行
    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));
}
复制代码

The main thing to do at this stage is to initialize SELinux, so what is SELinux? In fact, it is security-enhanced Linux, so that access control can be enforced for all processes, so that Android can better protect and limit system services, control access to application data and system logs, and reduce the impact of malware.

However, SELinux is not initialized once, and the next step is to call the main function again to enter the final SecondStageMain stage.

SecondStageMain

//  文件位置:system/core/init/init.cpp
//  不那么重要的地方就不贴代码了
int SecondStageMain(int argc, char** argv) {
    //  又调用了这两个方法
    SetStdioToDevNull(argv);
    //  初始化本阶段内核日志
    InitKernelLogging(argv);
    //  ...
    //  正在引导后台固件加载程序
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
    //  系统属性初始化
    property_init();
    //  系统属性设置相关,而且下面还有很多地方都在 property_set
    //  ...
    //  清理环境
    //  将 SELinux 设置为第二阶段 
    //  创建 Epoll
    Epoll epoll;
    //  注册信号处理
    InstallSignalFdHandler(&epoll);
    //  加载默认的系统属性
    property_load_boot_defaults(load_debug_prop);
    //  启动属性服务
    StartPropertyService(&epoll);
    //  重头戏,解析 init.rc 和其他 rc
    // am 和 sm 就是用来接收解析出来的数据
    //  里面基本上是要执行的 action 和要启动的 service
    LoadBootScripts(am, sm);
    //  往 am 里面添加待执行的 Action 和 Trigger
    while (true) {
        //  执行 Action
        am.ExecuteOneCommand();
        //  还有就是重启死掉的子进程
        auto next_process_action_time = HandleProcessActions();
    }
}
复制代码

This is the most important part of the entire startup phase. I think there are four more important points, which are property services, registration signal processing, init.rc parsing, and the infinite loop at the end of the method.

attribute service

What is attribute service? I think it is more like various system information about this mobile phone. It is used by all our programs in the form of key / value. The following content is the attribute value obtained after my emulator enters the adb shell. The following Part of what I kept from the output:

generic_x86:/ $ getprop
...
[dalvik.vm.heapsize]: [512m]
...
[dalvik.vm.usejit]: [true]
[dalvik.vm.usejitprofiles]: [true]
...
[init.svc.adbd]: [running]
...
[init.svc.gpu]: [running]
...
[init.svc.surfaceflinger]: [running]
...
[init.svc.zygote]: [running]
...
[ro.product.brand]: [google]
[ro.product.cpu.abi]: [x86]
...
[ro.serialno]: [EMULATOR29X2X1X0]
[ro.setupwizard.mode]: [DISABLED]
[ro.system.build.date]: [Sat Sep 21 05:19:49 UTC 2019]
...
//  zygote 启动该启动哪个
[ro.zygote]: [zygote32]
[ro.zygote.disable_gl_preload]: [1]
[security.perf_harden]: [1]
[selinux.restorecon_recursive]: [/data/misc_ce/0]
...
[wifi.interface]: [wlan0]
复制代码

Attribute service-related codes actually do three things in the SecondStageMain stage: create shared memory, load various attribute values, and create sockets for attribute services. Here's a snippet about those parts:

property_init {
    //  创建目录 /dev/__properties__
    //  会从别的地方加载并解析属性,然后写到 /dev/__properties__/property_info 里
    //  在 __system_property_area_init 的调用链跟踪中,发现最终是通过 mmap 创建共享内存
}

property_load_boot_defaults {
    //  代码中很多这样的代码
    load_properties_from_file("/system/build.prop", nullptr, &properties);
    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
    load_properties_from_file("/product/build.prop", nullptr, &properties);
    load_properties_from_file("/product_services/build.prop", nullptr, &properties);
    load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
    //  会调用 PropertySet 设置这些属性值
}

StartPropertyService {
    //  创建 Sockte
    //  这个 Socket 就是用来处理系统属性的,所有进程都通过它来修改共享内存里面的系统属性
    property_set_fd = CreateSocket(...);
    //  开始注册监听,handle_property_set_fd 是回调处理函数
    epoll->RegisterHandler(property_set_fd, handle_property_set_fd);
}
复制代码

It is not that difficult to understand the code, but you may ask why use shared memory? What is the role of Socket?

First of all, shared memory is an efficient inter-process communication method. It is enough to have a copy of these attribute values ​​in the memory. It is not necessary for each process to copy a copy to its own space, and because it is shared, everyone can able to visit. But if anyone can read and write at any time (except for the read-only part of the attribute), there will still be problems, and there may be content inconsistencies, so everyone does not directly operate on the shared memory, but through the socket of the attribute service To operate on it, this avoids all processes directly operating on that piece of shared memory.

Register Signal Handler

In the SecondStageMain stage, the signal processing function is actually registered so that it can respond to the underlying signal. The corresponding function is:

InstallSignalFdHandler {
    //  ...
    //  注册信号处理函数
    epoll->RegisterHandler(signal_fd, HandleSignalFd);
}

HandleSignalFd {
    //  ...
    //  ReapAnyOutstandingChildren 会对死掉的进程进行重启
    SIGCHLD -> ReapAnyOutstandingChildren
    SIGTERM -> HandleSigtermSignal
    default -> 打印日志
}

//  子进程异常退出后要标记需要重新启动
ReapAnyOutstandingChildren {
    //  ...
    ReapOneProcess {
         // ...
        service.Reap {
            //  ...
            //  设置要重启的标志位,但这里并不是真的启动
            flags_ &= (~SVC_RESTART);
            flags_ |= SVC_RESTARTING;
            onrestart_.ExecuteAllCommands();
        }
    }
}
复制代码

init.rc parsing

init.rc What is it? It is a very important configuration file, and init.rc is the most important file among many rc files, but here I will not talk about the syntax of the rc file, because it has been written in system/core/init/README.md It is very clear that init.rc will be divided into different stages according to on, and triggered by trigger in different stages, and each stage contains instructions to be executed one by one, for example, start is followed by the service to be started, mkdir is to create Table of contents.

Now that it is divided into multiple stages, let's take a look at what the trigger stage looks like:

//  这三个阶段是顺序下去的,这三个阶段的触发顺序是写在 SecondStageMain 代码中的
early-init -> init -> late-init

//  late-init 中再去触发别的阶段
on late-init
    trigger early-fs
    trigger fs
    trigger post-fs
    trigger late-fs
    trigger post-fs-data
    trigger load_persist_props_action
    //  这里就是 zygote-start 启动了
    trigger zygote-start
    trigger firmware_mounts_complete
    trigger early-boot
    trigger boot

复制代码

So let's take a look at what init.rc parsing does in the SecondStageMain stage:

//  把这阶段关于 rc 文件相关的一些重要代码提取出来
int SecondStageMain(int argc, char** argv) {
    //  ...
    //  两个用于存储的容器
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
    //  解析 init.rc
    LoadBootScripts(am, sm);
    //  ...
    //  加入触发 early-init 语句
    am.QueueEventTrigger("early-init");
    //  ...
    //  加入触发 init 语句
    am.QueueEventTrigger("init");
    //  ...
    //  代码中还有很多 QueueBuiltinAction,插入要执行的 Action
    am.QueueBuiltinAction(InitBinder, "InitBinder");
    //  ...
    //  加入触发 late-init 语句
      am.QueueEventTrigger("late-init");
}

LoadBootScripts(action_manager, service_list) {
    Parser parser = CreateParser(action_manager, service_list);
    //  系统属性中去找 ro.boot.init_rc 对应的值
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    //  没找到的话就去当前目录找 init.rc 
    //  当前目录就是 system/core/init/
    if (bootscript.empty()) {
        //  无论没有找到最终解析的任务都是交给 ParseConfig 这个方法去处理 
        parser.ParseConfig("/init.rc");
        //  ... 
    } else {
        parser.ParseConfig(bootscript);
    }
}
复制代码

In fact, the above code writing mainly does is to parse the content in the init.rc file, and add the actions to be executed.

Infinite loop at the end of the method

The main thing to do here is to execute the actions just entered into ActionManager and see if there is a process that needs to be restarted.

while (true) {
    //  ...
    //  执行刚才加入 ActionManager 的动作
    am.ExecuteOneCommand();
    //  ... 
    //  HandleProcessActions 才是真正重启进程的地方
    auto next_process_action_time = HandleProcessActions();
}

HandleProcessActions {
    //  ...
    //  对需要重启的进行重启,前面会有很多判断
    auto result = s->Start();
}
    
复制代码

Up to here, the three stages of init process startup are basically clear.

However, since it is the first time I started to read the AOSP source code, the content discussed in this article is relatively limited, and there are still many details that have not been discussed, such as:

  • More details on the Linux boot process;
  • What are the files that are specifically mounted, and what are their uses;
  • What is the complete read and write process of attribute services?
  • Specifically, how to parse and execute init.rc;
  • zygote startup, etc.;

But the follow-up part, such as zygote, I will try to share it after reading it next time.

Summary and harvest

If you ask me what I have gained from reading these, I think the following three points are my main gains:

  • In some cases (such as insufficient resources in the early stage or backward and forward dependencies), we can dismantle large tasks and reasonably allocate the execution order (including sequence, serial and parallel arrangements, etc.), and then complete a task through the cooperation of multi-stage tasks. Overall implementation goals;
  • When resources are shared, it is best not to allow everyone to directly operate on the resources, but to introduce a middleman. Everyone only interacts with the middleman, and the specific resources are interacted with by the middleman;
  • It is very important for the code to run, but a reasonable monitoring module is also very necessary, so that it can detect problems and respond in time when necessary;

thanks and reference

In addition to the source code itself, the above content also refers to the following links (in no particular order):

How is the computer started?

Linux boot process

07 | From BIOS to bootloader: At the beginning of the business, there is a job for the boss to do it himself

08 | Kernel initialization: If the business grows bigger, a company must be established

Brief analysis of Android startup process (1)

Inside the Linux Boot Process

The past life of No. 0 process under Linux (init_task process) and the present life (idle process)----Management and scheduling of Linux processes (5)

Security-Enhanced Linux in Android

Android system startup - Init

In-depth study of the source code: Android10.0 system startup process (2) init process

Android P (9.0) Init process source code analysis

The init process start of the Android system startup process

Android startup process - 1 2 3

Author: Gzw,
Link: https://juejin.cn/post/6844903965688250382
Source: Rare Earth Nuggets
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, for non-commercial reprint, please indicate the source.

Guess you like

Origin blog.csdn.net/lgglkk/article/details/128274636