Android init process startup process

 Android system startup

1, "Introduction to the Android System Startup Process"

2, "Android init process startup process"

3. "Android zygote process startup process"

4. "Android SystemServer Process Startup Process"

5. "Android launcher startup process"

6. "Detailed Explanation of Android Activity Startup Process"

Android System Development Preparation

1, "Android Source Code Download and Compilation"

2. "Android 11 source code compilation and pixel3 flashing"

3. "Android Framework Code IDE Loading and Debugging"

Android System Development Practice

1. "Android Setting Default Input Method"

2, "android framework prefabricated APK application"

3. "Detailed Explanation of Restricting Apps from Starting at the Android System Level"

4. "Android compiles the framework module separately and pushes it"

5. "Android Framework Development System Problem Analysis"

Android System Development Core Knowledge Reserve

1, "Android Compilation System - envsetup and lunch code articles"

2. "Android Compilation System - Concept"

3. "Detailed Explanation of Android Log System"

4. "Android System Handler Detailed Explanation"

5. "Android System Binder Detailed Explanation"

6. "Detailed Explanation of the Relationship between Activity, View and Window in Android"

7. "Detailed Explanation of the Android View Drawing Process"

8. "Detailed Explanation of Android Reading System Attributes"

9. "Detailed Explanation of Android Window Management Mechanism"

10. "Acquaintance with Android System"

11. "The communication method of AMS process in android notifying Zygote process fork new process"

Detailed explanation of Android core functions

1. "Android application market click to download APK installation details"

2, "Android gesture navigation (swipe from bottom to top to enter the multitasking page)"

3. "Android Gesture Analysis (Swipe left to right on the application interface to exit the application)"

4. "Detailed Explanation of Android Application Installation Process"

5, "android11 ​​installation application triggers desktop icon refresh process"

6. "Detailed Explanation of Android System Multitasking Recents"

7. "Android System Navigation Bar View Analysis"

———————————————————————————————————————————

Table of contents

1. Background knowledge

Second, the init process startup process

2.1 ueventd_main

2.2 The init process starts the first stage

2.3 Load SELinux rules

2.4 The init process starts the second phase

2.5 The third stage init.rc

2.6 init.rc parsing process

2.6.1 LoadBootScripts

2.6.2 Executing Actions

2.6.3 Zygote startup

Three, summary


1. Background knowledge

        The android startup process is as follows,

        The android system is based on the Linux system, and the startup steps are as above. The init process, as the first process in user mode, is engaged in the development of android framework or app. It is necessary to analyze the startup process of the init process.

Second, the init process startup process

        init process entry system/core/init/main.cpp,

using namespace android::init;
/*
 * 1.第一个参数argc表示参数个数,第二个参数是参数列表,也就是具体的参数
 * 2.main函数有四个参数入口,
 *一是参数中有ueventd,进入ueventd_main
 *二是参数中有subcontext,进入InitLogging 和SubcontextMain
 *三是参数中有selinux_setup,进入SetupSelinux
 *四是参数中有second_stage,进入SecondStageMain
 *3.main的执行顺序如下:
   *  (1)ueventd_main    init进程创建子进程ueventd,
   *      并将创建设备节点文件的工作托付给ueventd,ueventd通过两种方式创建设备节点文件
   *  (2)FirstStageMain  启动第一阶段
   *  (3)SetupSelinux     加载selinux规则,并设置selinux日志,完成SELinux相关工作
   *  (4)SecondStageMain  启动第二阶段
 */
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#endif
    //当argv[0]的内容为ueventd时,strcmp的值为0,!strcmp为1
    //1表示true,也就执行ueventd_main,ueventd主要是负责设备节点的创建、权限设定等一些列工作
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }
   //当传入的参数个数大于1时,执行下面的几个操作
    if (argc > 1) {
        if (!strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();

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


2.1 ueventd_main

Code path: platform/system/core/init/ueventd.cpp

The "/dev" directory does not exist in the image of the Android root file system. This directory is dynamically created after the init process starts.

Therefore, the heavy task of establishing the device node file in Android also falls on the init process. To this end, the init process creates a child process ueventd, and entrusts ueventd with the work of creating device node files.

ueventd creates device node files in two ways.

The first method corresponds to "Cold Plug", that is, based on the predefined device information, when ueventd is started, the device node file is created uniformly. This type of device node file is also called a static node file.

The second method corresponds to "Hot Plug", that is, when the system is running, when a device is inserted into the USB port, ueventd will receive this event and dynamically create a device node file for the inserted device. This type of device node file is also called a dynamic node file.
 

int ueventd_main(int argc, char** argv) {
    /*
     * init sets the umask to 077 for forked processes. We need to
     * create files with exact permissions, without modification by
     * the umask.
     */
    //设置新建文件的默认值,这个与chmod相反,这里相当于新建文件后的权限为666
    umask(000);
    //初始化内核日志,位于节点/dev/kmsg, 此时logd、logcat进程还没有起来,
    //采用kernel的log系统,打开的设备节点/dev/kmsg, 那么可通过cat /dev/kmsg来获取内核log。
    android::base::InitLogging(argv, &android::base::KernelLogger);

    LOG(INFO) << "ueventd started!";
    //注册selinux相关的用于打印log的回调函数
    SelinuxSetupKernelLogging();
    SelabelInitialize();

    std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;

    // Keep the current product name base configuration so we remain backwards compatible and
    // allow it to override everything.
    // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
    auto hardware = android::base::GetProperty("ro.hardware", "");
    //解析xml,根据不同SOC厂商获取不同的hardware rc文件
    auto ueventd_configuration = ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc",
                                              "/odm/ueventd.rc", "/ueventd." + hardware + ".rc"});

    uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
            std::move(ueventd_configuration.dev_permissions),
            std::move(ueventd_configuration.sysfs_permissions),
            std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), true));
    uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
            std::move(ueventd_configuration.firmware_directories),
            std::move(ueventd_configuration.external_firmware_handlers)));

    if (ueventd_configuration.enable_modalias_handling) {
        std::vector<std::string> base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
        uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>(base_paths));
    }
    UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);

    //冷启动
    if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
        ColdBoot cold_boot(uevent_listener, uevent_handlers,
                           ueventd_configuration.enable_parallel_restorecon);
        cold_boot.Run();
    }

    for (auto& uevent_handler : uevent_handlers) {
        uevent_handler->ColdbootDone();
    }

    // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
    signal(SIGCHLD, SIG_IGN);
    // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
    // for SIGCHLD above.
    while (waitpid(-1, nullptr, WNOHANG) > 0) {
    }
    //监听来自驱动的uevent,进行“热插拔”处理
    uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
        for (auto& uevent_handler : uevent_handlers) {
            uevent_handler->HandleUevent(uevent);
        }
        return ListenerAction::kContinue;
    });

    return 0;
}

2.2 The init process starts the first stage

Code path: platform\system\core\init\first_stage_init.cpp

The main work of the first stage of the init process is to mount partitions, create device nodes and some key directories, initialize the log output system, and enable SELinux security policies

The first phase completes the following:

/* 01. Create a file system directory and mount the related file system*/

/* 02. Shield standard input and output/initialize kernel log system*/
 

int FirstStageMain(int argc, char** argv) {
    //init crash时重启引导加载程序
    //这个函数主要作用将各种信号量,如SIGABRT,SIGBUS等的行为设置为SA_RESTART,一旦监听到这些信号即执行重启系统
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }
    //清空文件权限
    umask(0);
 
    CHECKCALL(clearenv());
    CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
 
    //在RAM内存上获取基本的文件系统,剩余的被rc文件所用
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
 
    // 非特权应用不能使用Andrlid cmdline
    CHECKCALL(chmod("/proc/cmdline", 0440));
    gid_t groups[] = {AID_READPROC};
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
 
    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
 
    if constexpr (WORLD_WRITABLE_KMSG) {
        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    }
 
    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
 
 
    //这对于日志包装器是必需的,它在ueventd运行之前被调用
    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
 
 
    //在第一阶段挂在tmpfs、mnt/vendor、mount/product分区。其他的分区不需要在第一阶段加载,
    //只需要在第二阶段通过rc文件解析来加载。
    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=1000"));
    
    //创建可供读写的vendor目录
    CHECKCALL(mkdir("/mnt/vendor", 0755));
    // /mnt/product is used to mount product-specific partitions that can not be
    // part of the product partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/product", 0755));
 
    // 挂载APEX,这在Android 10.0中特殊引入,用来解决碎片化问题,类似一种组件方式,对Treble的增强,
    // 不写谷歌特殊更新不需要完整升级整个系统版本,只需要像升级APK一样,进行APEX组件升级
    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));
 
    // /debug_ramdisk is used to preserve additional files from the debug ramdisk
    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));
#undef CHECKCALL
 
    //把标准输入、标准输出和标准错误重定向到空设备文件"/dev/null"
    SetStdioToDevNull(argv);
    //在/dev目录下挂载好 tmpfs 以及 kmsg 
    //这样就可以初始化 /kernel Log 系统,供用户打印log
    InitKernelLogging(argv);
 
    ...
 
    /* 初始化一些必须的分区
     *主要作用是去解析/proc/device-tree/firmware/android/fstab,
     * 然后得到"/system", "/vendor", "/odm"三个目录的挂载信息
     */
    if (!DoFirstStageMount()) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    }
 
    struct stat new_root_info;
    if (stat("/", &new_root_info) != 0) {
        PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
        old_root_dir.reset();
    }
 
    if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
        FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
    }
 
    SetInitAvbVersionInRecovery();
 
    static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
    uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
    setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
 
    //启动init进程,传入参数selinux_steup
    // 执行命令: /system/bin/init selinux_setup
    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args));
    PLOG(FATAL) << "execv(\"" << path << "\") failed";
 
    return 1;
}

2.3 Load SELinux rules

        SELinux is the abbreviation of "Security-Enhanced Linux". It is an expanded mandatory access control security module of Linux developed by the US National Security Agency "NSA=The National Security Agency" and SCC (Secure Computing Corporation).

        Under the restriction of this access control system, a process can only access those files needed in its task.

        selinux has two working modes:

1, permissive, all operations are allowed (that is, no MAC), but if the permission is violated, a log will be recorded, generally eng mode is used 2,
enforcing, all operations will be checked for permissions. General user and user-debug mode

        Whether it is security_setenforce or security_getenforce is to operate the /sys/fs/selinux/enforce file, 0 means permissive 1 means enforcing

2.4 The init process starts the second phase

The main content of the second stage:

    1. Create a process session key and initialize the attribute system
    2. Perform the second phase of SELinux
    3. Create a new epoll and initialize
    4. Start the server with matching attributes
    5. Analyze init.rc and other files, create the action and service of the rc file, and start other processes

int SecondStageMain(int argc, char** argv) {
    /* 
    *init crash时重启引导加载程序
    *这个函数主要作用将各种信号量,如SIGABRT,SIGBUS等的行为设置为SA_RESTART,一旦监听到这些信号即执行重启系统
    */
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }
 
    //把标准输入、标准输出和标准错误重定向到空设备文件"/dev/null"
    SetStdioToDevNull(argv);
    //在/dev目录下挂载好 tmpfs 以及 kmsg 
    //这样就可以初始化 /kernel Log 系统,供用户打印log
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";
 
    // 01. 创建进程会话密钥并初始化属性系统
    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
 
    //创建 /dev/.booting 文件,就是个标记,表示booting进行中
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
 
    // 初始化属性系统,并从指定文件读取属性
    property_init();
 
    /*
     * 1.如果参数同时从命令行和DT传过来,DT的优先级总是大于命令行的
     * 2.DT即device-tree,中文意思是设备树,这里面记录自己的硬件配置和系统运行参数,
     */
    process_kernel_dt(); // 处理 DT属性
    process_kernel_cmdline(); // 处理命令行属性
 
    // 处理一些其他的属性
    export_kernel_boot_props();
 
    // Make the time that init started available for bootstat to log.
    property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
 
    // Set libavb version for Framework-only OTA match in Treble build.
    const char* avb_version = getenv("INIT_AVB_VERSION");
    if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
    // See if need to load debug props to allow adb root, when the device is unlocked.
    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
        load_debug_prop = "true"s == force_debuggable_env;
    }
 
    // 基于cmdline设置memcg属性
    bool memcg_enabled = android::base::GetBoolProperty("ro.boot.memcg",false);
    if (memcg_enabled) {
       // root memory control cgroup
       mkdir("/dev/memcg", 0700);
       chown("/dev/memcg",AID_ROOT,AID_SYSTEM);
       mount("none", "/dev/memcg", "cgroup", 0, "memory");
       // app mem cgroups, used by activity manager, lmkd and zygote
       mkdir("/dev/memcg/apps/",0755);
       chown("/dev/memcg/apps/",AID_SYSTEM,AID_SYSTEM);
       mkdir("/dev/memcg/system",0550);
       chown("/dev/memcg/system",AID_SYSTEM,AID_SYSTEM);
    }
 
    // 清空这些环境变量,之前已经存到了系统属性中去了
    unsetenv("INIT_STARTED_AT");
    unsetenv("INIT_SELINUX_TOOK");
    unsetenv("INIT_AVB_VERSION");
    unsetenv("INIT_FORCE_DEBUGGABLE");
 
    // Now set up SELinux for second stage.
    SelinuxSetupKernelLogging();
    SelabelInitialize();
 
    /*
     * 02. 进行SELinux第二阶段并恢复一些文件安全上下文 
     * 恢复相关文件的安全上下文,因为这些文件是在SELinux安全机制初始化前创建的,
     * 所以需要重新恢复上下文
     */
    SelinuxRestoreContext();
 
   /*
    * 03. 新建epoll并初始化子进程终止信号处理函数
    *  创建epoll实例,并返回epoll的文件描述符
    */
    Epoll epoll;
    if (auto result = epoll.Open(); !result) {
        PLOG(FATAL) << result.error();
    }
 
    /* 
     *主要是创建handler处理子进程终止信号,注册一个signal到epoll进行监听
     *进行子继承处理
     */
    InstallSignalFdHandler(&epoll);
 
    // 进行默认属性配置相关的工作
    property_load_boot_defaults(load_debug_prop);
    UmountDebugRamdisk();
    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();
 
    /*
     *04. 设置其他系统属性并开启系统属性服务
     */
    StartPropertyService(&epoll);
    MountHandler mount_handler(&epoll);
 
    //为USB存储设置udc Contorller, sys/class/udc
    set_usb_controller();
 
    // 匹配命令和函数之间的对应关系
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);
 
    if (!SetupMountNamespaces()) {
        PLOG(FATAL) << "SetupMountNamespaces failed";
    }
 
    // 初始化文件上下文
    subcontexts = InitializeSubcontexts();
 
   /*
     *05 解析init.rc等文件,建立rc文件的action 、service,启动其他进程
     */
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
 
    LoadBootScripts(am, sm);
 
    // Turning this on and letting the INFO logging be discarded adds 0.2s to
    // Nexus 9 boot time, so it's disabled by default.
    if (false) DumpState();
 
    // 当GSI脚本running时,确保GSI状态可用.
    if (android::gsi::IsGsiRunning()) {
        property_set("ro.gsid.image_running", "1");
    } else {
        property_set("ro.gsid.image_running", "0");
    }
 
 
    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
 
    // 执行rc文件中触发器为 on early-init 的语句
    am.QueueEventTrigger("early-init");
 
    // 等冷插拔设备初始化完成
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
 
    // 开始查询来自 /dev的 action
    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
 
    // 设备组合键的初始化操作
    Keychords keychords;
    am.QueueBuiltinAction(
        [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
            for (const auto& svc : ServiceList::GetInstance()) {
                keychords.Register(svc->keycodes());
            }
            keychords.Start(&epoll, HandleKeychord);
            return Success();
        },
        "KeychordInit");
 
    //在屏幕上显示Android 静态LOGO
    am.QueueBuiltinAction(console_init_action, "console_init");
 
    // 执行rc文件中触发器为on init的语句
    am.QueueEventTrigger("init");
 
    // Starting the BoringSSL self test, for NIAP certification compliance.
    am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
 
    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
    // wasn't ready immediately after wait_for_coldboot_done
    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
 
    // Initialize binder before bringing up other system services
    am.QueueBuiltinAction(InitBinder, "InitBinder");
 
    // 当设备处于充电模式时,不需要mount文件系统或者启动系统服务
    // 充电模式下,将charger假如执行队列,否则把late-init假如执行队列
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }
 
    // 基于属性当前状态 运行所有的属性触发器.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
 
    while (true) {
        // By default, sleep until something happens.
        auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
 
        if (do_shutdown && !shutting_down) {
            do_shutdown = false;
            if (HandlePowerctlMessage(shutdown_command)) {
                shutting_down = true;
            }
        }
 
//依次执行每个action中携带command对应的执行函数
        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            am.ExecuteOneCommand();
        }
        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            if (!shutting_down) {
                auto next_process_action_time = HandleProcessActions();
 
                // If there's a process that needs restarting, wake up in time for that.
                if (next_process_action_time) {
                    epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                            *next_process_action_time - boot_clock::now());
                    if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
                }
            }
 
            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout = 0ms;
        }
 
        // 循环等待事件发生
        if (auto result = epoll.Wait(epoll_timeout); !result) {
            LOG(ERROR) << result.error();
        }
    }
 
    return 0;
}

2.5 The third stage init.rc

        When the attribute service is established, the init's own functions basically come to an end, and then other processes need to be started. But how does the init process start other processes? The other process is a binary file, we can start it directly through the exec command, such as ./system/bin/init second_stage, to start the second stage of the init process. But there are so many Native processes in the Android system, if they all execute the processes one by one in the code by passing exec, it is undoubtedly a disastrous design.

        On this basis, Android introduces an init.rc mechanism, which is similar to reading configuration files to start different processes. Next, let's take a look at how init.rc works.

2.5.1 Introduction to init.rc

        One of the important features of Linux is that everything exists in the form of files. For example, a device usually corresponds to one or more device files. These files that interact with the kernel space are all in the user space, so after the Linux kernel is loaded, you need to first create the directory where these files are located. The program to complete these tasks is the init to be introduced in this article. Init is a command-line program. One of its main tasks is to create the directories where these files that interact with the kernel space reside. When the Linux kernel is loaded, the first thing to do is to call the init program, that is to say, init is the first program executed in user space. Of course, the function of the init program is not only to load the above device files. init.rc is a configuration file that contains scripts written in the Android Init Language.

        There are two init.rc, to be exact, two sets, located in:

        1,./system/core/rootdir/init.rc

        2,./bootable/recovery/etc/init.rc

        From the directory, it can be roughly guessed that the usage scenarios of these two init.rc are different, one is used for normal startup; the other is used for flashing, that is, entering recovery mode. What we focus on here is the one above, which is also the one associated with init.c.

        /init.rc is the main .rc file, loaded by the init executable when it starts executing. It is responsible for the initial setup of the system.

        After loading the main directory /init.rc, init immediately loads all files contained in the /{system,vendor,odm}/etc/init/ directory.

        1, /system/etc/init/ is used for core system items, such as SurfaceFlinger, MediaService and logd.

        2. /vendor/etc/init/ is for SoC vendor projects, such as actions or daemons required for SoC core functions.

        3, /odm/etc/init/ is used for device manufacturer's projects, such as actions or daemons required by motion sensors or other peripheral functions.

        How each system loads these .rc files in different directories depends on the import statement starting from init.rc. Among them, import /init.${ro.hardware}.rc is more common. Through cat proc/cpuinfo, you can view the value of ro.hardware = the value of Hardware; generally at the end of the cpuinfo file.

2.5.2 Init.rc statement description

        init.rc mainly contains five types of statements: Action, Command, Service, Option, Import

1. Actions: When a certain trigger condition is reached, the command is executed, that is, the process of responding to an event. The statement starting with on determines the timing of executing the corresponding service. The specific timing is as follows:

    on early-init; Triggered in the early stage of initialization;
    on init; Triggered in the initialization stage;
    on late-init; Triggered in the late stage of initialization;
    on boot/charger: Triggered when the system starts/charges;
    on property:<key>= <value>: Triggered when the attribute value meets the condition;

Actions take the form of:

    on <trigger> [&& <trigger>]*  // 触发条件
       <command>  // 执行命令
       <command>
       <command>

 2. Commands: Commands will be executed one by one when the corresponding event occurs. Many commands are defined in the rc script, such as

    boot:init程序启动后触发的第一个事件
    class_start <service_class_name>: 启动属于同一个class的所有服务;
    class_stop <service_class_name> : 停止指定类的服务
    start <service_name>: 启动指定的服务,若已启动则跳过;
    stop <service_name>: 停止正在运行的服务
    setprop <name> <value>:设置属性值
    mkdir <path>:创建指定目录
    symlink <target> <sym_link>: 创建连接到<target>的<sym_link>符号链接;
    write <path> <string>: 向文件path中写入字符串;
    exec: fork并执行,会阻塞init进程直到程序完毕;
    exprot <name> <name>:设定环境变量;
    loglevel <level>:设置log级别
    hostname <name> : 设置主机名
    import <filename> :导入一个额外的init配置文件

3. Services: Service Service, starting with service, is started by the init process, and generally runs in a sub-process of init, so it is necessary to determine whether the corresponding executable file exists before starting the service.

Services take the form of:

    service <name> <pathname> [ <argument> ]*
       <option>
       <option>
       ...

// name:服务名称
// pathname:服务路径
// argument:启动服务传递的参数
// option:对服务的约束项

        The child process generated by init is defined in the rc file, and each service will generate a child process by fork when it starts. For example: service servicemanager /system/bin/servicemanager means that the service name is servicemanager, and the service execution path is /system/bin/servicemanager.

4. Options: Service modifiers, which affect how and when the init process runs the service.

1,class <class_name>
说明服务属于class_name这个类。缺省值service属于 “default” 类。同一个class下面的服务可以一起启动或停止。
2,disabled
表示当这个服务所在的class启动的时候,服务不会自动启动,
要用start server_name 或 property_set("ctl.start", server_name);才能启动。
3,oneshot
当服务退出后,不会再重新启动,如果没有加这个option,则服务默认退出后又会重新重启
4,user <username>
执行服务之前,先声明服务的用户名,缺省值应该为root用户.
5,group <groupname> [ <groupname> ]*
执行服务之前,先声明服务所属组名,可以一次声明属于多个组。
声明多个组时,除第一个组名外,其他的为服务的补充组名(调用接口 setgroups()).
6,onrestart + command
服务重启的时,会执行onrestart后面的command.
eg:onrestart restart media 重启名为media的服务
7,setenv <name> <value>
在当前服务进程中设置环境变量name的值为value。
注意:setenv定义的环境变量仅在本进程内生效,退出该进程,或者关闭相应的程序运行窗口,该环境变量即无效)
程序中可通过getenv("name")接口获取这个环境变量的值
setenv和export 的区别:
setenv csh ,本进程生效,退出后,变量无效
export bash ,全局生效,一直存在
格式:
export key=value
setenv key value
8,critical
声明为关键服务。如果服务在四分钟内退出了四次,则设备会进入recovery模式
9,socket <name> <type> <perm> [ <user> [ <group> ] ]
创建名为/dev/socket/<name>的unix domain socket ,并把它的句柄fd传给本服务进程
<type> 必须为 "dgram", "stream" or "seqpacket".User and group default to 0 ,也就是root.
10,seclablel 执行服务之前改变安全上下文

5. Imports: import other rc configuration files, the syntax format import <path>is

2.5.3 Common commands

1).import <filename>          
  导入init.XX.rc、xxx.conf等文件  
   Parse an init config file, extending the current configuration.

2).chmod <octal-mode> <path>  
   Change file access permissions.

3).chown <owner> <group> <path>  
   Change file owner and group.  
    
4).chdir <directory>  
   Change working directory.  
    
5).chroot <directory>   
  改变进程根目录  
    
6).insmod <path>     
  加载XX.ko驱动模块

7).start <service>  
   Start a service running if it is not already running.

8).stop <service>  
   Stop a service from running if it is currently running.

9).class_start <serviceclass>  
   Start all services of the specified class if they are not already running.

10).class_stop <serviceclass>  
   Stop all services of the specified class if they are currently running.  
       
   class_reset <serviceclass>   //重启class下面所有的服务  
       
11).setprop <name> <value>    
   Set system property <name> to <value>.  
   通过getprop命令可以查看当前系统的属性值  
    
12).export <name> <value>         
  设置全局环境变量,这个变量值可以被所有进程访问(全局的,一直存在)  
  在代码中通过value = getenv("name")接口可以获取这个环境变量的值  
    
13).mkdir <path> [mode] [owner] [group]  
   创建目录,后面项缺省值为 mode,owner,group: 0755 root root

14).trigger <event>  
   Trigger an action.  Used to queue an action from another action.  
   例:trigger post-fs-data

15).exec <path> [ <argument> ]*       
   执行<path>指定的Program,并可以带有执行参数。  
   exec在调用进程内部执行一个可执行文件,并会阻塞当前进程,直到运行完成。  
   最好避免和那些builtin commands一样使用exec命令,否则容易造成阻塞 or stuck ( maybe there should be a timeout?)

16).ifup <interface>  
   启动某个网络接口,使其为up状态,通过netcfg可以查看,ifup eth0  等价于 netcfg eth0 up 功能一样  
    
17).hostname <name>  
   设置设备的主机名,一般默认设置为localhost,可以在终端通过hostname new_name进行修改

18).domainname <name>  
   设置网络域名localdomain

19).mount <type> <device> <dir> [ <mountoption> ]* 
    把device挂接到dir目录下面,文件系统类型为type。  
   <mountoption>s include "ro", "rw", "remount", "noatime", “nosuid”......,具体可查看[linux](http://lib.csdn.net/base/linux "Linux知识库")的mount命令说明  
    
20).setkey  
   TBD  == to be determined 暂时没有使用

21).setrlimit <resource> <cur> <max>  
  设置本服务进程的资源上限值。(使用例子??)

22).symlink <target> <path>     
   path 链接到 ---》target ;创建符号链接

23).sysclktz <mins_west_of_gmt>    
  设置系统时区(0 if system clock ticks in GMT)

24).wait <path> [ <timeout> ]  
 轮询查找给定的文件path是否存在,如果找到或者超时则返回默认超时为5秒。(使用实例???)

25).write <path> <string> [ <string> ]*  
   打开一个文件,利用write命令写入一个或多个字符串

2.6 init.rc parsing process

2.6.1 LoadBootScripts

Code path: platform\system\core\init\init.cpp

Description: If there is no special configuration ro.boot.init_rc, then parse ./init.rc

把/system/etc/init,/product/etc/init,/product_services/etc/init,/odm/etc/init,

/vendor/etc/init These paths are added to the paths parsed after init.rc, after the init.rc parsing is completed, parse the rc files in these directories

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);
 
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/product_services/etc/init")) {
            late_import_paths.emplace_back("/product_services/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

Code path: platform\system\core\init\init.cpp

Description: Create Parser parsing objects, such as service, on, import objects

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;
 
    parser.AddSectionParser(
            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
    return parser;
}

2.6.2 Executing Actions

        Add related Actions to the trigger queue in order, the order is early-init -> init -> late-init. Then in the loop, execute all the execution functions with Command in the Action in the trigger queue.

am.QueueEventTrigger("early-init");
am.QueueEventTrigger("init");
am.QueueEventTrigger("late-init");
...
while (true) {
if (!(waiting_for_prop || Service::is_exec_service_running())) {
            am.ExecuteOneCommand();
        }
}

2.6.3 Zygote startup

        Use the attribute ro.zygote to control the startup of different versions of the zygote process. In the import section of init.rc we see the following code:

import /init.${ro.zygote}.rc // 可以看出init.rc不再直接引入一个固定的文件,而是根据属性ro.zygote的内容来引入不同的文件

 init.rc is located under /system/core/rootdir. In this path also include four rc files about zygote.

They are init.zygote32.rc, init.zygote32_64.rc, init.zygote64.rc, init.zygote64_32.rc, which file is determined by the hardware.

Take the 64-bit processor as an example, the code of init.zygote64.rc is as follows:
 

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main		# class是一个option,指定zygote服务的类型为main
    	    priority -20
            user root
   	    group root readproc reserved_disk
    	    socket zygote stream 660 root system  # socket关键字表示一个option,创建一个名为dev/socket/zygote,类型为stream,权限为660的socket
   	    socket usap_pool_primary stream 660 root system
            onrestart write /sys/android_power/request_state wake # onrestart是一个option,说明在zygote重启时需要执行的command
    	    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server 解析:

service zygote: A zygote service is defined in init.zygote64.rc. The init process is to create the zygote process through this service name

/system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server解析:

The zygote service is run by executing /system/bin/app_process64 and passing in 4 parameters:

    Parameter 1: -Xzygote This parameter will be used as the parameter required when the virtual machine starts
    Parameter 2: /system/bin represents the directory where the virtual machine program is located
    Parameter 3: --zygote indicates that the main function in the ZygoteInit.java class is executed as a virtual machine Entry
    parameter 4: --start-system-server tells the Zygote process to start the systemServer process

Three, summary

        The main work of the first stage of the init process is to mount partitions, create device nodes and some key directories, initialize the log output system, and enable SELinux security policies.

        The main work of the second stage of the init process is to initialize the attribute system, analyze the matching rules of SELinux, process the termination signal of the child process, and start the system attribute service. It can be said that each item is very important. If the first stage is for the attribute system, SELinux does Prepare, then the second stage is to actually implement these functions.

        The third stage of init is mainly to analyze init.rc to start other processes, enter an infinite loop, and monitor sub-processes in real time.

Guess you like

Origin blog.csdn.net/allen_xu_2012_new/article/details/130701425