Android源码阅读---init进程

Android源码阅读—init进程


init进程是系统的第一个进程,它会解析配置文件 init.rc,并根据init.rc中的内容:

  • 装载android 文件系统
  • 创建系统目录
  • 初始化系统属性
  • 启动重要的守护进程

1. 编译命令和进程入口

1. init 进程编译命令

LOCAL_PATH:= $(call my-dir)
# --
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_PERMISSIVE_SELINUX=1
else
init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_PERMISSIVE_SELINUX=0
endif
init_options += -DLOG_UEVENTS=0
ifneq ($(TARGET_INIT_COLDBOOT_TIMEOUT),)
init_options += -DCOLDBOOT_TIMEOUT_OVERRIDE=$(TARGET_INIT_COLDBOOT_TIMEOUT)
endif
ifneq ($(TARGET_INIT_CONSOLE_TIMEOUT),)
init_options += -DCONSOLE_TIMEOUT_SEC=$(TARGET_INIT_CONSOLE_TIMEOUT)
endif
/* c flag 忽略编译报错*/
init_cflags += \
    $(init_options) \
    -Wall -Wextra \
    -Wno-unused-parameter \
    -Werror \
# --

# If building on Linux, then build unit test for the host./* 模块libinit_parser 和init_parser_tests看起来像是单元测试用的额 */
ifeq ($(HOST_OS),linux)
LOCAL_MODULE := libinit_parser
LOCAL_CLANG := true
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \
    parser/tokenizer.cpp \

include $(CLEAR_VARS)
LOCAL_MODULE := init_parser_tests
LOCAL_SRC_FILES := \
    parser/tokenizer_test.cpp \

LOCAL_STATIC_LIBRARIES := libinit_parser
LOCAL_CLANG := true
include $(BUILD_HOST_NATIVE_TEST)
endif

include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \
    action.cpp \
    import_parser.cpp \
    init_parser.cpp \
    log.cpp \
    parser.cpp \
    service.cpp \
    util.cpp \

LOCAL_STATIC_LIBRARIES := libbase libselinux
LOCAL_MODULE := libinit
/*init程序编译需要的静态库,开起来像是 watchdog h和 uevent的实现逻辑*/
LOCAL_SANITIZE := integer
LOCAL_CLANG := true
include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \
    bootchart.cpp \
    builtins.cpp \
    devices.cpp \
    init.cpp \
    keychords.cpp \
    property_service.cpp \
    signal_handler.cpp \
    ueventd.cpp \
    ueventd_parser.cpp \
    watchdogd.cpp \

/*此处才是真正init程序的编译命令*/
LOCAL_MODULE:= init
LOCAL_C_INCLUDES += \
    system/extras/ext4_utils \
    system/core/mkbootimg

LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
/*init二进制文件包括的依赖库,都要以静态链接的方式包括进init中,保证它自己可以独立运行*/
LOCAL_STATIC_LIBRARIES := \
    libinit \
    libbootloader_message \
    libfs_mgr \
    libfec \
    libfec_rs \
    libsquashfs_utils \
    liblogwrap \
    libcutils \
    libext4_utils_static \
    libbase \
    libutils \
    libc \
    libselinux \
    liblog \
    libmincrypt \
    libcrypto_static \
    libc++_static \
    libdl \
    libsparse_static \
    libz

/*创建2个符号链接 实际上指向的就是init这个二进制文件*/
# Create symlinks
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
    ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
    ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
LOCAL_SANITIZE := integer
LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)



/*编译init_tests模块*/
include $(CLEAR_VARS)
LOCAL_MODULE := init_tests
LOCAL_SRC_FILES := \
    init_parser_test.cpp \
    util_test.cpp \
LOCAL_SHARED_LIBRARIES += \
    libcutils \
    libbase \
LOCAL_STATIC_LIBRARIES := libinit
LOCAL_SANITIZE := integer
LOCAL_CLANG := true
include $(BUILD_NATIVE_TEST)

2. main函数流程

int main(int argc, char** argv) {
/*如果启动程序文件名是ueventd,执行守护进程ueventd主函数,sbin/watchdogd 和 sbin/ueventd链接文件实现逻辑的入口*/
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }
    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }
    // Clear the umask.
    umask(0);
    add_environment("PATH", _PATH_DEFPATH);
    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
    // Get the basic filesystem setup we need put together in the initramdisk
    // on / and then we'll let the rc file figure out the rest.
    if (is_first_stage) {
    /*其实在内核起来后,系统的文件系统至于 / 和 /sbin 2个文件目录,然后在此基础上 创建基本目录,将文件系统 tmpfs devpts proc sysfc 这4个 mount到对应目录 */
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    }
    // We must have some place other than / to create the device nodes for kmsg and null, otherwise we won't be able to remount / read-only later on. Now that tmpfs is mounted on /dev, we can actually talk to the outside world.
    open_devnull_stdio();/*定向标准输入 输出 错误重定向等的文件目录*/
    klog_init();/*创建节点/dev/__kmsg__,让init进程使用kernel的log系统输出log*/
    klog_set_level(KLOG_NOTICE_LEVEL);
    NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
    if (!is_first_stage) {
        // Indicate that booting is in progress to background fw loaders, etc.
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
        property_init();/*初始化Android属性系统,创建共享区域存储属性值*/
        // If arguments are passed both on the command line and in DT,
        // properties set in DT always have priority over the command-line ones.
        process_kernel_dt();
        process_kernel_cmdline();
        // Propagate the kernel variables to internal variables
        // used by init as well as the current required properties.
        export_kernel_boot_props();
    }
    // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
    selinux_initialize(is_first_stage);
    // If we're in the kernel domain, re-exec init to transition to the init domain now
    // that the SELinux policy has been loaded.
    if (is_first_stage) {
        if (restorecon("/init") == -1) {
            ERROR("restorecon failed: %s\n", strerror(errno));
            security_failure();
        }
        char* path = argv[0];
        char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
        if (execv(path, args) == -1) {/*函数说明:execv()用来执行参数path 字符串所代表的文件路径, 第二个参数利用数组指针来传递给执行文件.*/
            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
            security_failure();
        }
    }
    // These directories were necessarily created before initial policy load
    // and therefore need their security context restored to the proper value.
    // This must happen before /dev is populated by ueventd.
    NOTICE("Running restorecon...\n");
    restorecon("/dev");
    restorecon("/dev/socket");
    restorecon("/dev/__properties__");
    restorecon("/property_contexts");
    restorecon_recursive("/sys");
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        ERROR("epoll_create1 failed: %s\n", strerror(errno));
        exit(1);
    }
    signal_handler_init();
    property_load_boot_defaults();
    export_oem_lock_status();
    start_property_service();

    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);
    Parser& parser = Parser::GetInstance();
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
    parser.AddSectionParser("on", std::make_unique<ActionParser>());
    parser.AddSectionParser("import", std::make_unique<ImportParser>());
    parser.ParseConfig("/init.rc");/*parse init.rc file and the item of server and action into service_list and action_list*/
    ActionManager& am = ActionManager::GetInstance();
    am.QueueEventTrigger("early-init");
    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
    am.QueueBuiltinAction(console_init_action, "console_init");
    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");
    // 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(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    // Don't mount filesystems or start core system services in charger mode.
    std::string bootmode = property_get("ro.bootmode");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }
    // Run all property triggers based on current state of the properties.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
    while (true) {
        if (!waiting_for_exec) {
            am.ExecuteOneCommand();
            restart_processes();
        }
        int timeout = -1;
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }
        if (am.HasMoreCommands()) {
            timeout = 0;
        }
        bootchart_sample(&timeout);
        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

2. 主函数处理流程

1. 创建基本目录

    if (is_first_stage) {
    /*其实在内核起来后,系统的文件系统至于 / 和 /sbin 2个文件目录,然后在此基础上 创建基本目录,将文件系统 tmpfs devpts proc sysfc 这4个 mount到对应目录 */
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    }
  • tmpfs 基于内存的文件系统,访问速度快,关机后内容丢失
  • devpts 虚拟终端文件系统
  • proc 基于内存的文件系统,内核内部数据结构的接口,通过它查看系统信息
  • sysfc 同上

2. open_devnull_stdio 重定向到空设备

void open_devnull_stdio(void)
{
    // Try to avoid the mknod() call if we can. Since SELinux makes a /dev/null replacement available for free, let's use it.
    int fd = open("/sys/fs/selinux/null", O_RDWR);
    if (fd == -1) {
        // OOPS, /sys/fs/selinux/null isn't available, likely because /sys/fs/selinux isn't mounted. Fall back to mknod.
        static const char *name = "/dev/__null__";
        if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
            fd = open(name, O_RDWR);
            unlink(name);
        }
        if (fd == -1) {
            exit(1);
        }
    }
/*
标准输入0 从键盘获得输入 /proc/self/fd/0 

标准输出1 输出到屏幕(即控制台) /proc/self/fd/1 

错误输出2 输出到屏幕(即控制台) /proc/self/fd/2 
将这个类型的内容统一输出到"/dev/__null__"文件中

*/
    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
    if (fd > 2) {
        close(fd);
    }
}

3. klog_init() log输出

init可以使用kernel的log系统(/dev/kmsg)来输出log

static int klog_fd = -1;
void klog_init(void) {
    if (klog_fd >= 0) return; /* Already initialized */
    klog_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
    if (klog_fd >= 0) {
        return;
    }
    static const char* name = "/dev/__kmsg__";/*如果/dev/kmsg不存在 ,创建/dev/__kmsg__节点*/
    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
        klog_fd = open(name, O_WRONLY | O_CLOEXEC);
        unlink(name);
    }
}

4. selinux_initialize 初始化selinx模块

static void selinux_initialize(bool in_kernel_domain) {
    Timer t;
    selinux_callback cb;
    cb.func_log = selinux_klog_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);/*将函数指针selinux_klog_callback赋值给selinux_log */
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);/*audit_callback/*将函数指针赋值给selinux_audit */
    if (in_kernel_domain) {    /*在init第一阶段执行*/
        INFO("Loading SELinux policy...\n");
        if (selinux_android_load_policy() < 0) {     /*加载并向内核设置policy*/
            ERROR("failed to load policy: %s\n", strerror(errno));
            security_failure();
        }
        bool kernel_enforcing = (security_getenforce() == 1);
        bool is_enforcing = selinux_is_enforcing();
        if (kernel_enforcing != is_enforcing) {
            if (security_setenforce(is_enforcing)) {
                ERROR("security_setenforce(%s) failed: %s\n",
                      is_enforcing ? "true" : "false", strerror(errno));
                security_failure();
            }
        }
        if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
            security_failure();
        }
        NOTICE("(Initializing SELinux %s took %.2fs.)\n",
               is_enforcing ? "enforcing" : "non-enforcing", t.duration());
    } else {
        selinux_init_all_handles();
    }
}


  1. 全局函数赋值
    将函数指针selinux_klog_callback赋值给selinux_log,audit_callback/*将函数指针赋值给selinux_audit ,具体的selinux_set_callback函数实现如下:
/*external/libselinux/src/callbacks.c文件*/
/* callback setting function */
void selinux_set_callback(int type, union selinux_callback cb)
{
 switch (type) {
 case SELINUX_CB_LOG:
  selinux_log = cb.func_log;
  break;
 case SELINUX_CB_AUDIT:
  selinux_audit = cb.func_audit;
  break;
 case SELINUX_CB_VALIDATE:
  selinux_validate = cb.func_validate;
  break;
 case SELINUX_CB_SETENFORCE:
  selinux_netlink_setenforce = cb.func_setenforce;
  break;
 case SELINUX_CB_POLICYLOAD:
  selinux_netlink_policyload = cb.func_policyload;
  break;
}
}
  1. 加载并向内核设置policy

selinux_android_load_policy做了2个操作,其一是挂载selinuxfs 文件系统,其二是装载selinux策略(selinux_android_load_policy_helper中的实现)

/*external/libselinux/src/android.c文件*/
int selinux_android_load_policy(void)
{
 const char *mnt = SELINUXMNT;/* SELINUXMNT为 /sys/fs/selinux */
 int rc;
 rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);/* #define SELINUXFS "selinuxfs" selinuxfs filesystem type string. */
/*挂载selinuxfs文件系统到/sys/fs/selinux*/
 if (rc < 0) {
  if (errno == ENODEV) {
   /* SELinux not enabled in kernel */
   return -1;
  }
  if (errno == ENOENT) {
   /* Fall back to legacy mountpoint. */
   mnt = OLDSELINUXMNT;
   rc = mkdir(mnt, 0755);
   if (rc == -1 && errno != EEXIST) {
    selinux_log(SELINUX_ERROR,"SELinux: Could not mkdir: %s\n",
     strerror(errno));
    return -1;
   }
   rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
  }
 }
 if (rc < 0) {
  selinux_log(SELINUX_ERROR,"SELinux: Could not mount selinuxfs: %s\n",
    strerror(errno));
  return -1;
 }
/* 之前都在错误处理,保证虚拟文件系统selinuxfs能够挂载成功*/
 set_selinuxmnt(mnt);/* 将 mnt的值赋值给 char *selinux_mnt = NULL;*/
    return selinux_android_load_policy_helper(false);/*装载 selinux策略*/

}

static int selinux_android_load_policy_helper(bool reload)
{
 int fd = -1, rc;
 struct stat sb;
 void *map = NULL;
 int old_policy_index = policy_index;

 /*
  * If reloading policy and there is no /data policy or
  * that /data policy has the wrong version and our prior
  * load was from the / policy, then just return.
  * There is no point in reloading policy from / a second time.
  */
 set_policy_index();
 if (reload && !policy_index && !old_policy_index)
  return 0;

 fd = open(sepolicy_file[policy_index], O_RDONLY | O_NOFOLLOW);
 if (fd < 0) {
  selinux_log(SELINUX_ERROR, "SELinux: Could not open sepolicy: %s\n",
    strerror(errno));
  return -1;
 }
 if (fstat(fd, &sb) < 0) {
  selinux_log(SELINUX_ERROR, "SELinux: Could not stat %s: %s\n",
    sepolicy_file[policy_index], strerror(errno));
  close(fd);
  return -1;
 }
 map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
 if (map == MAP_FAILED) {
  selinux_log(SELINUX_ERROR, "SELinux: Could not map %s: %s\n",
    sepolicy_file[policy_index], strerror(errno));
  close(fd);
  return -1;
 }

 rc = security_load_policy(map, sb.st_size);
 if (rc < 0) {
  selinux_log(SELINUX_ERROR, "SELinux: Could not load policy: %s\n",
    strerror(errno));
  munmap(map, sb.st_size);
  close(fd);
  return -1;
 }

 munmap(map, sb.st_size);
 close(fd);
 selinux_log(SELINUX_INFO, "SELinux: Loaded policy from %s\n", sepolicy_file[policy_index]);

 return 0;
}

  1. 如果policy加载失败,重启android
static void security_failure() {
    ERROR("Security failure; rebooting into recovery mode...\n");
    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
    while (true) { pause(); } // never reached
}

5. 切换到第二阶段

使用参数"–second-stage",重新执行init的main函数.

    // If we're in the kernel domain, re-exec init to transition to the init domain now
    // that the SELinux policy has been loaded.
    if (is_first_stage) {
        if (restorecon("/init") == -1) {
            ERROR("restorecon failed: %s\n", strerror(errno));
            security_failure();
        }
        char* path = argv[0];
        char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
        if (execv(path, args) == -1) {/*函数说明:execv()用来执行参数path 字符串所代表的文件路径, 第二个参数利用数组指针来传递给执行文件.*/
            ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
            security_failure(); /*和policy加载失败时,执行同样逻辑----重启android*/
        }
    }

6. 开启第二阶段

    if (!is_first_stage) {
        // Indicate that booting is in progress to background fw loaders, etc.
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));/*  删除文件/dev/.booting 来表示初始化阶段结束 */
        property_init();/*初始化Android属性系统,创建共享区域存储属性值*/
        // If arguments are passed both on the command line and in DT,
        // properties set in DT always have priority over the command-line ones.
        process_kernel_dt();
        process_kernel_cmdline();/*解析kernel的启动参数*/
        // Propagate the kernel variables to internal variables
        // used by init as well as the current required properties.
        export_kernel_boot_props();
    }
  1. 创建一个共享区域存储属性值
    其步骤如下:
  • 声明2个链表,类型分别是prefix_node(存储属性值) &&context_node(存储slelinux安全上下文),并清空它们
  • 通过函数initialize_properties 读取文件/property_contexts中的数据并将该数据填充到prefix_node &&context_node链表中去
  • 遍历链表context_node为每个context_node值都申请一个共享内存空间地址
/*system/core/init/property_service.cpp文件*/
void property_init() {
    if (__system_property_area_init()) {
        ERROR("Failed to initialize property area\n");
        exit(1);
    }
}

该函数实际调用bionic/libc/bionic/system_properties.cpp文件中的__system_property_area_init()方法创建和初始化属性的共享空间,:

static prefix_node* prefixes = nullptr;
static context_node* contexts = nullptr;

int __system_property_area_init()
{
    free_and_unmap_contexts();  /*清空属性类别相关的两个链表, prefixes 和 contexts 链表*/
    mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);/*#define PROP_FILENAME "/dev/__properties__" 创建文件 /dev/__properties__*/
    if (!initialize_properties()) {/*将/property_contexts 文件中所有的属性类别信息存储到 prefixs 和 contexts 链表中,虽然对应关系还是多对多的模式,但是contexts 链表中的值都是唯一的,而在prefixs链表中,同一个值可以有多份*/
        return -1;
    }
    bool open_failed = false;
    bool fsetxattr_failed = false;
    list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) {
        if (!l->open(true, &fsetxattr_failed)) { /*为每个属性context创建相应的内存映射共享地址*/
            open_failed = true;
        }
    });
    if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
        free_and_unmap_contexts();
        return -1;
    }
    initialized = true;
    return fsetxattr_failed ? -2 : 0;
}

步骤一

/* 首先看看prefix_node(存储属性值) &&context_node(存储slelinux安全上下文)类型的定义
class context_node {
public:
    context_node(context_node* next, const char* context, prop_area* pa)
        : next(next), context_(strdup(context)), pa_(pa), no_access_(false) {
        lock_.init(false);
    }
    ~context_node() {
        unmap();
        free(context_);
    }
    bool open(bool access_rw, bool* fsetxattr_failed);
    bool check_access_and_open();
    void reset_access()
    const char* context() const { return context_; }
    prop_area* pa() { return pa_; )
    context_node* next;

private:
    bool check_access();
    void unmap();
    Lock lock_;
    char* context_;
    prop_area* pa_;
    bool no_access_;
};

struct prefix_node {
    prefix_node(struct prefix_node* next, const char* prefix, context_node* context)
        : prefix(strdup(prefix)), prefix_len(strlen(prefix)), context(context), next(next) {
    }
    ~prefix_node() {
        free(prefix);
    }
    char* prefix;
    const size_t prefix_len;
    context_node* context;
    struct prefix_node* next;
};

*/

 static void free_and_unmap_contexts() {
  list_free(&prefixes);
  list_free(&contexts);
  if (__system_property_area__) {
  munmap(__system_property_area__, pa_size);
  __system_property_area__ = nullptr;
  }
 }

步骤二

首先,打开文件property_contexts
然后,按行遍历,将指针prop_prefix指向属性值,指针context指向selinux安全上下文
其次,如果属性为ctl.类型,则跳过该行
最后,如果context的值,在链表contexts中已经存在,该值则不加入contexts中

static bool initialize_properties() {
    FILE* file = fopen("/property_contexts", "re");
/*property_contexts  文件中存储的部分属性如下:
属性值 + selinux安全上下文
ro.qualcomm.bluetooth. u:object_r:bluetooth_prop:s0
ctl.ipacm u:object_r:ipacm_prop:s0
ctl.ipacm-diag u:object_r:ipacm-diag_prop:s0
ctl.qti u:object_r:qti_prop:s0
*/
    if (!file) {
        return false;
    }
    char* buffer = nullptr;
    size_t line_len;
    char* prop_prefix = nullptr;
    char* context = nullptr;

    while (getline(&buffer, &line_len, file) > 0) {
        int items = read_spec_entries(buffer, 2, &prop_prefix, &context);
        if (items <= 0) {
            continue;
        }
        if (items == 1) {
            free(prop_prefix);
            continue;
        }
        /*
         * init uses ctl.* properties as an IPC mechanism and does not write them
         * to a property file, therefore we do not need to create property files
         * to store them.
         */
        if (!strncmp(prop_prefix, "ctl.", 4)) {/*跳过ctl.类型的属性*/
            free(prop_prefix);
            free(context);
            continue;
        }

        auto old_context = list_find(
            contexts, [context](context_node* l) { return !strcmp(l->context(), context); });
        if (old_context) {
            list_add_after_len(&prefixes, prop_prefix, old_context);
        } else {
            list_add(&contexts, context, nullptr);
            list_add_after_len(&prefixes, prop_prefix, contexts);
        }
        free(prop_prefix);
        free(context);
    }

    free(buffer);
    fclose(file);
    return true;
}

步骤三

首先,遍历 contexts中的每个属性,
然后,构造类似/dev/properties/u:object_r:bluetooth_prop:s0的文件名,如果该文件不存在就创建一个
其次,mmap函数在内存中分配一块共享内存空间,并与/dev/properties/u:object_r:bluetooth_prop:s0的文件的文件描述符fd关联起来
最后,将该共享内存空间首地址赋值给 _pa,
经过上面的步骤,将contexts链表中的每个context_node对象都分配了一个共享内存空间

    bool open_failed = false;
    bool fsetxattr_failed = false;
    list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) {
        if (!l->open(true, &fsetxattr_failed)) { /*为每个属性context创建相应的内存映射共享地址*/
            open_failed = true;
        }
    });
    if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
        free_and_unmap_contexts();/* 如果共享内存分配失败,清空链表并退出*/
        return -1;
    }
    initialized = true;
    return fsetxattr_failed ? -2 : 0;

/*
 * pthread_mutex_lock() calls into system_properties in the case of contention.
 * This creates a risk of dead lock if any system_properties functions
 * use pthread locks after system_property initialization.
 *
 * For this reason, the below three functions use a bionic Lock and static
 * allocation of memory for each filename.
 */

bool context_node::open(bool access_rw, bool* fsetxattr_failed) {
    lock_.lock();
    if (pa_) {
        lock_.unlock();
        return true;
    }

    char filename[PROP_FILENAME_MAX];
    int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s",
                                   property_filename, context_);
    if (len < 0 || len > PROP_FILENAME_MAX) {
        lock_.unlock();
        return false;
    }
/*创建文件名 如 /dev/properties/u:object_r:bluetooth_prop:s0 ,如果创建失败则退出 */
    if (access_rw) {
        pa_ = map_prop_area_rw(filename, context_, fsetxattr_failed);
    } else {
        pa_ = map_prop_area(filename, false);
    }
    lock_.unlock();
    return pa_;
}

static prop_area* map_prop_area_rw(const char* filename, const char* context,
                                   bool* fsetxattr_failed) {
    /* dev is a tmpfs that we can use to carve a shared workspace
     * out of, so let's do that...
     */
    const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);

    if (fd < 0) {
        if (errno == EACCES) {
            /* for consistency with the case where the process has already
             * mapped the page in and segfaults when trying to write to it
             */
            abort();
        }
        return nullptr;
    }

    if (context) {
        if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) {
            __libc_format_log(ANDROID_LOG_ERROR, "libc",
                              "fsetxattr failed to set context (%s) for \"%s\"", context, filename);
            /*
             * fsetxattr() will fail during system properties tests due to selinux policy.
             * We do not want to create a custom policy for the tester, so we will continue in
             * this function but set a flag that an error has occurred.
             * Init, which is the only daemon that should ever call this function will abort
             * when this error occurs.
             * Otherwise, the tester will ignore it and continue, albeit without any selinux
             * property separation.
             */
            if (fsetxattr_failed) {
                *fsetxattr_failed = true;
            }
        }
    }

    if (ftruncate(fd, PA_SIZE) < 0) {
        close(fd);
        return nullptr;
    }

    pa_size = PA_SIZE;
    pa_data_size = pa_size - sizeof(prop_area);
    compat_mode = false;

    void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (memory_area == MAP_FAILED) {
        close(fd);
        return nullptr;
    }

    prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);

    close(fd);
    return pa;
}
*/

7. process_kernel_dt()函数

遍历设备数中的路径,然后设置各个路径的属性值

        // If arguments are passed both on the command line and in DT,
        // properties set in DT always have priority over the command-line ones.

static void process_kernel_dt() {
    static const char android_dir[] = "/proc/device-tree/firmware/android";

    std::string file_name = android::base::StringPrintf("%s/compatible", android_dir); /*构造路径/proc/device-tree/firmware/android/compatible*/

    std::string dt_file;
    android::base::ReadFileToString(file_name, &dt_file);
    if (!dt_file.compare("android,firmware")) {
        ERROR("firmware/android is not compatible with 'android,firmware'\n");
        return;
    }

    std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
    if (!dir) return;

    struct dirent *dp;
    while ((dp = readdir(dir.get())) != NULL) {
        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
            continue;
        }

        file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);

        android::base::ReadFileToString(file_name, &dt_file);
        std::replace(dt_file.begin(), dt_file.end(), ',', '.');

        std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);
        property_set(property_name.c_str(), dt_file.c_str());
    }
}

8. process_kernel_cmdline() 函数

在kernel 的 cmdline 启动参数文件中提取以androidboot.开头的字段,并将它构造成ro.kernel.类型的属性值。如androidboot.switch.launcher=0构成成[ro.boot.switch.launcher]: [0]

/*/proc/cmdline中的内容如下:
sched_enable_hmp=1 sched_enable_power_aware=1 console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom msm_rtb.filter=0x237 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 androidboot.bootdevice=7824900.sdhci earlycon=msm_hsl_uart,0x78af000 log_buf_len=1048576 buildvariant=userdebug androidboot.emmc=true androidboot.verifiedbootstate=orange androidboot.veritymode=enforcing androidboot.keymaster=1 androidboot.alarm=0 androidboot.serialno=7569704 androidboot.baseband=msm mdss_mdp.panel=1:dsi:0:qcom,mdss_dsi_ft8716u_1080p_video:1:none:cfg:single_dsi lcd_id=1 androidboot.switch.launcher=0
*/

static void process_kernel_cmdline() {
    // Don't expose the raw commandline to unprivileged processes.
    chmod("/proc/cmdline", 0440);

    // The first pass does the common stuff, and finds if we are in qemu.
    // The second pass is only necessary for qemu to export all kernel params
    // as properties.
    import_kernel_cmdline(false, import_kernel_nv);
    if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
    if (key.empty()) return;

    if (for_emulator) {
        // In the emulator, export any kernel option with the "ro.kernel." prefix.
        property_set(android::base::StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
        return;
    }

    if (key == "qemu") {
        strlcpy(qemu, value.c_str(), sizeof(qemu));
    } else if (android::base::StartsWith(key, "androidboot.")) {
        property_set(android::base::StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(),
                     value.c_str());
    }
}
/*
void import_kernel_cmdline(bool in_qemu,
                           std::function<void(const std::string&, const std::string&, bool)> fn) {
    std::string cmdline;
    android::base::ReadFileToString("/proc/cmdline", &cmdline);/*将文件/proc/cmdline中的内容读取并存储在字符串cmdline中*/

    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {/*以空格为分界线 ,=前段字符串存在pieces[0].之后的存在pieces[1]中 */
        std::vector<std::string> pieces = android::base::Split(entry, "=");
        if (pieces.size() == 2) {
            fn(pieces[0], pieces[1], in_qemu);
        }
    }
}
*/

9. export_kernel_boot_props()函数

如果所有的属性值中存在prop_map[].src_prop属性值,则将该属性值的内容同时赋值给prop_map[].dst_prop属性
如ro.boot.serialno 属性存在,那么同时将它的值赋值给ro.serialno属性,如果ro.boot.serialno 属性不存在,将unknown赋值给ro.serialno属性

root:/ # getprop | egrep 'serialno'                                                                                                                                                                               
[ro.boot.serialno]: [7569704]
[ro.serialno]: [7569704]
root:/ # getprop | egrep 'ro.bootloader'                                                                                                                                                                          
[ro.bootloader]: [unknown]

实现代码如下:

        // Propagate the kernel variables to internal variables
        // used by init as well as the current required properties.
static void export_kernel_boot_props() {
    struct {
        const char *src_prop;
        const char *dst_prop;
        const char *default_value;
    } prop_map[] = {
        { "ro.boot.serialno", "ro.serialno", "", },
        { "ro.boot.mode", "ro.bootmode", "unknown", },
        { "ro.boot.baseband", "ro.baseband", "unknown", },
        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        { "ro.boot.hardware", "ro.hardware", "unknown", },
        { "ro.boot.revision", "ro.revision", "0", },
    };
    for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
        std::string value = property_get(prop_map[i].src_prop);
        property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
    }
}

10. signal_handler_init() 处理子进程信号的函数

/* Flags to be passed to epoll_create1. */
enum
  {
    EPOLL_CLOEXEC = 02000000
  };

    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        ERROR("epoll_create1 failed: %s\n", strerror(errno));
        exit(1);
    }
signal_handler_init();
  1. 系统中的所有进程都是通过系统的第一个进程init 通过fork直接或者间接创建出来的
  2. 如果子进程已经退出,而它的父进程并不知道子进程已经退出的情况下,在该用户的系统进程表中还会保留一部分该进程的信息,这个进程被称为僵尸进程。
  3. 每个用户在系统中能够运行的进程数量是确定并有限的,如果已经创建的进程数量(包括其中在系统进程表中占用空间的僵尸进程)到达了限制值,后续再创新新的进程就会失败
  4. 所以在inti进程中引入了信号机制来防止僵尸进程的出现

文件system/core/init/signal_handler.cpp中

static int signal_write_fd = -1;
static int signal_read_fd = -1;

void signal_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
        ERROR("socketpair failed: %s\n", strerror(errno));
        exit(1);
    }

    signal_write_fd = s[0];
    signal_read_fd = s[1];

    // Write to signal_write_fd if we catch SIGCHLD.
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIGCHLD_handler;
    act.sa_flags = SA_NOCLDSTOP;
    sigaction(SIGCHLD, &act, 0);

    ServiceManager::GetInstance().ReapAnyOutstandingChildren();

    register_epoll_handler(signal_read_fd, handle_signal);
}

static void handle_signal() {
    // Clear outstanding requests.
    char buf[32];
    read(signal_read_fd, buf, sizeof(buf));

    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
}

static void SIGCHLD_handler(int) {
    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
        ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
    }
}

11. property_load_boot_defaults() 解析根目录下的defualt.prop

读取defualt.prop文件中的属性值,具体实现system/core/init/property_service.cpp文件中

void property_load_boot_defaults() {
    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);/*#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"*/
}
/*
 * Filter is used to decide which properties to load: NULL loads all keys,
 * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
 */
static void load_properties_from_file(const char* filename, const char* filter) {
    Timer t;
    std::string data;
    if (read_file(filename, &data)) {/*将/default.prop内容全部读取到字符串data中*/
        data.push_back('\n');/*在字符串最后添加终结符,因为后续会按行来遍历*/
        load_properties(&data[0], filter);
    }
    NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
}
/*
 * Filter is used to decide which properties to load: NULL loads all keys,
 * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
 */
static void load_properties(char *data, const char *filter)
{
    char *key, *value, *eol, *sol, *tmp, *fn;
    size_t flen = 0;

    if (filter) {
        flen = strlen(filter);
    }

    sol = data;
    while ((eol = strchr(sol, '\n'))) {/*按行来读取*/
        key = sol;
        *eol++ = 0;
        sol = eol;

        while (isspace(*key)) key++;
        if (*key == '#') continue;

        tmp = eol - 2;
        while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;

        if (!strncmp(key, "import ", 7) && flen == 0) {
            fn = key + 7;
            while (isspace(*fn)) fn++;

            key = strchr(fn, ' ');
            if (key) {
                *key++ = 0;
                while (isspace(*key)) key++;
            }

            load_properties_from_file(fn, key);/*嵌套读取import 开头的文件*/

        } else {
            value = strchr(key, '=');
            if (!value) continue;
            *value++ = 0;

            tmp = value - 2;
            while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;

            while (isspace(*value)) value++;

            if (flen > 0) {
                if (filter[flen - 1] == '*') {
                    if (strncmp(key, filter, flen - 1)) continue;
                } else {
                    if (strcmp(key, filter)) continue;
                }
            }
            property_set(key, value);
        }
    }
}

12.start_property_service() 启动服务

  1. 在Android中,所有的进程都可以直接读取系统属性值,但是想要对属性值执行写操作,则必须委托init进程帮忙处理
  2. start_property_service()函数就会开启一个socket,这个socket就是一个服务
  3. 这个服务就是用来帮助除init进程外的其他进程对属性值执行写操作的

具体实现system/core/init/property_service.cpp文件中

void start_property_service() {
/*
1. 创建一个非阻塞的socket
2. 通过socket文件描述符设置最大并发数为 8
3. 注册 epoll 事件,监听socket请求,如果有请求调用handle_property_set_fd
*/
    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,/*#define PROP_SERVICE_NAME "property_service"*/
                                    0666, 0, 0, NULL);/*创建非阻塞的socket,它将被当做一个服务  在 /dev/socket/property_service可以找到它*/
    if (property_set_fd == -1) {
        ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
        exit(1);
    }
    listen(property_set_fd, 8);/*最多为8个用户提供服务*/
    register_epoll_handler(property_set_fd, handle_property_set_fd);/*注册 epoll 事件,当监听到 property_set_fd有数据到来,调用 handle_property_set_fd函数处理*/
}

static void handle_property_set_fd()/*具体的处理逻辑*/
{
    prop_msg msg;
/*prop_msg 定义
struct prop_msg
{
    unsigned cmd;
    char name[PROP_NAME_MAX];
    char value[PROP_VALUE_MAX];
};
*/
    int s;
    int r;
    struct ucred cr;
    struct sockaddr_un addr;
    socklen_t addr_size = sizeof(addr);
    socklen_t cr_size = sizeof(cr);
    char * source_ctx = NULL;
    struct pollfd ufds[1];
    const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */
    int nr;

    if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
        return;
    }
    /* Check socket options here */
    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
        close(s);
        ERROR("Unable to receive socket options\n");
        return;
    }

    ufds[0].fd = s;
    ufds[0].events = POLLIN;
    ufds[0].revents = 0;
    nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));/*超时处理*/
    if (nr == 0) {
        ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
        close(s);
        return;
    } else if (nr < 0) {
        ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
        close(s);
        return;
    }

    r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));/*读取 socket中的信息*/
    if(r != sizeof(prop_msg)) {
        ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
              r, sizeof(prop_msg), strerror(errno));
        close(s);
        return;
    }

    switch(msg.cmd) {
    case PROP_MSG_SETPROP:
        msg.name[PROP_NAME_MAX-1] = 0;
        msg.value[PROP_VALUE_MAX-1] = 0;

        if (!is_legal_property_name(msg.name, strlen(msg.name))) {/*检查 属性值 格式*/
            ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
            close(s);
            return;
        }
        getpeercon(s, &source_ctx);/*获取请求进程的安全上下文并赋值给source_ctx*/
        if(memcmp(msg.name,"ctl.",4) == 0) {
/*以ctl.开头的 调用 handle_control_message处理
以ctl.开头的,说明是控制属性
*/
            // Keep the old close-socket-early behavior when handling
            // ctl.* properties.
            close(s);
            if (check_control_mac_perms(msg.value, source_ctx, &cr)) {/*进行权限检查*/
                handle_control_message((char*) msg.name + 4, (char*) msg.value);/*实际的处理函数*/
            } else {
                ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
                        msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
            }
        } else {
            if (cr.uid == AID_SYSTEM || check_mac_perms(msg.name, source_ctx, &cr)) {
                property_set((char*) msg.name, (char*) msg.value);
            } else {
                ERROR("sys_prop: permission denied uid:%d name:%s\n",
                      cr.uid, msg.name);
            }
            // Note: bionic's property client code assumes that the
            // property server will not close the socket until *AFTER*
            // the property is written to memory.
            close(s);
        }
        freecon(source_ctx);
        break;
    default:
        close(s);
        break;
    }
}

然后继续看看handle_control_message函数和property_set函数怎么样属性值存入共享内存的
首先,看看handle_control_message函数

void handle_control_message(const std::string& msg, const std::string& name) {
/*根据属性值的值 来获取对应服务*/
    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
    if (svc == nullptr) {/*服务获取失败*/
        ERROR("no such service '%s'\n", name.c_str());
        return;
    }
/*然后根据吃 ctl. 后面的字符来判断 是 开启?关闭?重启  服务*/
    if (msg == "start") {
        svc->Start();
    } else if (msg == "stop") {
        svc->Stop();
    } else if (msg == "restart") {
        svc->Restart();
    } else {
        ERROR("unknown control msg '%s'\n", msg.c_str());
    }
}

然后,看看handle_control_message函数

int property_set(const char* name, const char* value) {
    int rc = property_set_impl(name, value);
    if (rc == -1) {
        ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
    }
    return rc;
}
static int property_set_impl(const char* name, const char* value) {
    size_t namelen = strlen(name);
    size_t valuelen = strlen(value);

    if (!is_legal_property_name(name, namelen)) return -1;
    if (valuelen >= PROP_VALUE_MAX) return -1;

    if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {/*单独检查selinux.reload_policy属性值,如果值为1 重新加载 selinux*/
        if (selinux_reload_policy() != 0) {
            ERROR("Failed to reload policy\n");
        }
    } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
        if (restorecon_recursive(value) != 0) {
            ERROR("Failed to restorecon_recursive %s\n", value);
        }
    }

    prop_info* pi = (prop_info*) __system_property_find(name);/*检查共享内存中是否已经有该[name]属性值了,如果存在,返回该[name]属性值共享内存的地址*/

    if(pi != 0) {
        /* ro.* properties may NEVER be modified once set */
    if(!strncmp(name, "ro.", 3)){
            return -1;
    }
        __system_property_update(pi, value, valuelen);/*在pi 这个地址上写入value值,长度为valuelen*/
    } else {/*如果不存在,即该属性值属于新添加的*/
        int rc = __system_property_add(name, namelen, value, valuelen);
        if (rc < 0) {
            return rc;
        }
    }
/*特殊的属性值需要特殊的处理


*/
    /* If name starts with "net." treat as a DNS property. */
    if (strncmp("net.", name, strlen("net.")) == 0) {
        if (strcmp("net.change", name) == 0) {
            return 0;
        }
       /*
        * The 'net.change' property is a special property used track when any
        * 'net.*' property name is updated. It is _ONLY_ updated here. Its value
        * contains the last updated 'net.*' property.
        */
        property_set("net.change", name);
    } else if (persistent_properties_loaded &&
            strncmp("persist.", name, strlen("persist.")) == 0) {
        /*
         * Don't write properties to disk until after we have read all default properties
         * to prevent them from being overwritten by default values.
         */
        write_persistent_property(name, value);
    }
    property_changed(name, value);
    return 0;
}

 系统属性值
* persist :系统重启后依然生效的,他们被保存在/data/property/目录下,拥有者为owner ,不可被链接
* ro :只读属性,不可改动的属性值。
* ctl : 为控制init中的各种服务设立的,通过把相关服务的服务名设置为ctl.start或者ctl.restart中来启动或者重启相关服务(handle_control_message具体实现)


13. 解析init.rc配置文件

首先,先看主代码:

/*
1. 创建解析器
2. 解析配置文件init.rc并将server块和action块存入成员变量service_&&action_&&imports_中
*/
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);
    Parser& parser = Parser::GetInstance(); /*获得一个解析器对象*/
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());/*创建一个service块的解析*/
    parser.AddSectionParser("on", std::make_unique<ActionParser>());/*创建一个aciton块的解析*/
    parser.AddSectionParser("import", std::make_unique<ImportParser>());/*此处将import的rc文件解析出来,将import也当成一个块来解析*/
/*这3个解析器都存放在  parser 对象的section_parsers_ 变量中*/
    parser.ParseConfig("/init.rc");/*parse init.rc file and the item of server and action into service_list and action_list*/
  1. init.rc组成结构
    init.rc文件是以块(section)为单位组织的,然后块分为2类(Actions and Services implicitly declare a new section. All commands or options belong to the section most recently declared. Commands or options before the first section are ignored.):
  • 行为(action) : 行为类的块,以单词on开始,以下一个on或者service结束,
    行为块结构定义如下:
on <trigger>
    <command>
    <command>
    <command>

从结构定义上看,行为(action)块是由一系列command的集合组成,每个action都有个trigger,根据这个触发器(trigger)来决定这一系列command执行时机。如下图中②就是一个行为类的块。 在文件system/core/init/action.h中行为(action)块类型的代码定义:

class Action {
public:
    Action(bool oneshot = false);
    bool AddCommand(const std::vector<std::string>& args,
                    const std::string& filename, int line, std::string* err);
    void AddCommand(BuiltinFunction f,
                    const std::vector<std::string>& args,
                    const std::string& filename = "", int line = 0);
    void CombineAction(const Action& action);
    bool InitTriggers(const std::vector<std::string>& args, std::string* err);
    bool InitSingleTrigger(const std::string& trigger);
    std::size_t NumCommands() const;
    void ExecuteOneCommand(std::size_t command) const;
    void ExecuteAllCommands() const;
    bool CheckEventTrigger(const std::string& trigger) const;
    bool CheckPropertyTrigger(const std::string& name, const std::string& value) const;
    bool TriggersEqual(const Action& other) const;
    std::string BuildTriggersString() const;
    void DumpState() const;
    bool oneshot() const { return oneshot_; }
    static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
        function_map_ = function_map;
    }
private:
    void ExecuteCommand(const Command& command) const;
    bool CheckPropertyTriggers(const std::string& name = "", const std::string& value = "") const;
    bool ParsePropertyTrigger(const std::string& trigger, std::string* err);
    std::map<std::string, std::string> property_triggers_;/*用来记录属性触发器,类似 on property:vold.decrypt=trigger_post_fs_data*/
    std::string event_trigger_;/*用来记录纯字符串触发器*/
    std::vector<Command> commands_;/*用来记录一系列Command命令*/
    bool oneshot_;
    static const KeywordMap<BuiltinFunction>* function_map_;
};
  • 服务(service): 服务类的块,以单词service开始,以下一个on或者service结束
    服务块结构定义如下:
service <name> <pathname> [ <argument> ]*
   <option>
   <option>
   ...

从结构定义上看,服务(service)块 包括一个服务名、可执行程序路径、可执行程序需要用到的参数以及一系列option组成 ,如下图中①就是一个服务类的块。 在文件system/core/init/service.h中,服务(service)块类型的代码定义:

class Service {
public:
    Service(const std::string& name, const std::string& classname,
            const std::vector<std::string>& args);
    Service(const std::string& name, const std::string& classname,
            unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
            const std::string& seclabel, const std::vector<std::string>& args);
    bool HandleLine(const std::vector<std::string>& args, std::string* err);
    bool Start();
    bool StartIfNotDisabled();
    bool Enable();
    void Reset();
    void Stop();
    void Terminate();
    void Restart();
    void RestartIfNeeded(time_t& process_needs_restart);
    bool Reap();
    void DumpState() const;
    const std::string& name() const { return name_; }
    const std::string& classname() const { return classname_; }
    unsigned flags() const { return flags_; }
    pid_t pid() const { return pid_; }
    uid_t uid() const { return uid_; }
    gid_t gid() const { return gid_; }
    const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
    const std::string& seclabel() const { return seclabel_; }
    const std::vector<int>& keycodes() const { return keycodes_; }
    int keychord_id() const { return keychord_id_; }
    void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
    const std::vector<std::string>& args() const { return args_; }

private:
    using OptionHandler = bool (Service::*) (const std::vector<std::string>& args,
                                             std::string* err);
    class OptionHandlerMap;
    void NotifyStateChange(const std::string& new_state) const;
    void StopOrReset(int how);
    void ZapStdio() const;
    void OpenConsole() const;
    void PublishSocket(const std::string& name, int fd) const;
    bool HandleClass(const std::vector<std::string>& args, std::string* err);
    bool HandleConsole(const std::vector<std::string>& args, std::string* err);
    bool HandleCritical(const std::vector<std::string>& args, std::string* err);
    bool HandleDisabled(const std::vector<std::string>& args, std::string* err);
    bool HandleGroup(const std::vector<std::string>& args, std::string* err);
    bool HandleIoprio(const std::vector<std::string>& args, std::string* err);
    bool HandleKeycodes(const std::vector<std::string>& args, std::string* err);
    bool HandleOneshot(const std::vector<std::string>& args, std::string* err);
    bool HandleOnrestart(const std::vector<std::string>& args, std::string* err);
    bool HandleSeclabel(const std::vector<std::string>& args, std::string* err);
    bool HandleSetenv(const std::vector<std::string>& args, std::string* err);
    bool HandleSocket(const std::vector<std::string>& args, std::string* err);
    bool HandleUser(const std::vector<std::string>& args, std::string* err);
    bool HandleWritepid(const std::vector<std::string>& args, std::string* err);
    std::string name_;
    std::string classname_;
    unsigned flags_;
    pid_t pid_;
    time_t time_started_; // time of last start
    time_t time_crashed_; // first crash within inspection window
    int nr_crashed_; // number of times crashed within window
    uid_t uid_;
    gid_t gid_;
    std::vector<gid_t> supp_gids_;
    std::string seclabel_;
    std::vector<SocketInfo> sockets_;
    std::vector<ServiceEnvironmentInfo> envvars_;
    Action onrestart_; // Commands to execute on restart.
    std::vector<std::string> writepid_files_;
    // keycodes for triggering this service via /dev/keychord
    std::vector<int> keycodes_;
    int keychord_id_;
    IoSchedClass ioprio_class_;
    int ioprio_pri_;
    std::vector<std::string> args_;
};

然后在init.rc配置文件中除了上面2个结构外,还有行为(action)块中使用的,触发器(trigger)命令(commond),服务(service)块中使用的选项(options),它们对应的定义分别如下:

class Command {
public:
    Command(BuiltinFunction f, const std::vector<std::string>& args,
            const std::string& filename, int line);

    int InvokeFunc() const;
    std::string BuildCommandString() const;
    std::string BuildSourceString() const;

private:
    BuiltinFunction func_;
    std::vector<std::string> args_;
    std::string filename_;
    int line_;
};

class Trigger {
public:
    virtual ~Trigger() { }
    virtual bool CheckTriggers(const Action& action) const = 0;
};
  1. 创建解析器
    Parser& parser = Parser::GetInstance(); /*获得一个解析器对象*/
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());/*创建一个service块的解析*/
    parser.AddSectionParser("on", std::make_unique<ActionParser>());/*创建一个aciton块的解析*/
    parser.AddSectionParser("import", std::make_unique<ImportParser>());/*此处将import的rc文件解析出来,将import也当成一个块来解析*/
/*这3个解析器都存放在  parser 对象的section_parsers_ 变量中*/

a. 首先,获得一个Parser对象
b. 然后创建3个具体的解析器对象,
c. 将这3个解析器对象存入 parser 对象的section_parsers_ 变量中

  1. init.rc解析

开始解析init.rc文件,具体查看在system/core/init/init_parser.cpp中的实现

bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {
/*首选,判断解析的路上是文件还是文件夹, 如果是文件使用ParseConfigFile解析文件,如果是文件夹,遍历里面的文件并对各个文件使用ParseConfigFile解析*/
        return ParseConfigDir(path);
    }
    return ParseConfigFile(path);
}
bool Parser::ParseConfigFile(const std::string& path) {
    INFO("Parsing file %s...\n", path.c_str());
    Timer t;
    std::string data;
    if (!read_file(path.c_str(), &data)) {/*将文件中的内容读取到data字符串中*/
        return false;
    }
    data.push_back('\n'); // TODO: fix parse_config.
    ParseData(path, data);   /*更具块的类型选择不同解析器解析data的数据*/
    for (const auto& sp : section_parsers_) {
        sp.second->EndFile(path);
    }
    // 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();
    NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
    return true;
}

然后看看ParseData方法具体怎么解析data中的数据的

void Parser::ParseData(const std::string& filename, const std::string& data) {
    //TODO: Use a parser with const input and remove this copy
    std::vector<char> data_copy(data.begin(), data.end());
    data_copy.push_back('\0');

    parse_state state;
    state.filename = filename.c_str();
    state.line = 0;
    state.ptr = &data_copy[0];/*将data中的指针存入parse_state 对象中*/
    state.nexttoken = 0;

    SectionParser* section_parser = nullptr;
    std::vector<std::string> args;/*以行为单位,将每行的内容存在args中*/

    for (;;) {/*相当于一个while循环,在条件到达 case T_EOF时return结束循环*/
        switch (next_token(&state)) {
/*next_token函数开始遍历data中的数据,
 如果遍历完一个单词 返回 T_TEXT
 如果遍历完一行 返回T_NEWLINE
 如果遍历完整个文本 返回T_EOF
*/
        case T_TEXT:/*一个词一个词的读取文本,并将读到的内容存入args中*/
            args.emplace_back(state.text);
            break;

        case T_NEWLINE:/*当一行遍历完后跳入此case,即一行的文本内容都存入了args中,然后在这个case中处理args中内容*/
            state.line++;
            if (args.empty()) {
                break;
            }
            if (section_parsers_.count(args[0])) {/*如果新的一行以[service,on,import]开头的,则表示开始一个新的块了*/
                if (section_parser) {
/*将上一个块的解析过程先结束*/
                    section_parser->EndSection();
                }
                section_parser = section_parsers_[args[0]].get();/*根据开头的单词,选择新的解析器*/
                std::string ret_err;
                if (!section_parser->ParseSection(args, &ret_err)) {/*ParseSection解析块的第一行内容 */
                    parse_error(&state, "%s\n", ret_err.c_str());/*容错处理*/
                    section_parser = nullptr;
                }
            } else if (section_parser) {/*如果新的一行不是以[service,on,import]开头的,则表示还在上一个块中*/
                std::string ret_err;
                if (!section_parser->ParseLineSection(args, state.filename,/*ParseLineSection解析块中的内容*/
                                                      state.line, &ret_err)) {
                    parse_error(&state, "%s\n", ret_err.c_str());
                }
            }
            args.clear();
            break;

        case T_EOF:/*如果配置文件文本结束,结束中在解析的块并跳出循环*/
            if (section_parser) {
                section_parser->EndSection();
            }
            return;
        }
    }
}

在文件system/core/init/init_parser.h中定义的解析器类,在该类中成员变量section_parsers_用来存储不同块的解析器

class SectionParser {/*块解析器接口类*/
public:
    virtual ~SectionParser() {
    }
    virtual bool ParseSection(const std::vector<std::string>& args,
                              std::string* err) = 0;
    virtual bool ParseLineSection(const std::vector<std::string>& args,
                                  const std::string& filename, int line,
                                  std::string* err) const = 0;
    virtual void EndSection() = 0;
    virtual void EndFile(const std::string& filename) = 0;
};

class Parser {
public:
    static Parser& GetInstance();
    void DumpState() const;
    bool ParseConfig(const std::string& path);
    void AddSectionParser(const std::string& name,
                          std::unique_ptr<SectionParser> parser);

private:
    Parser();

    void ParseData(const std::string& filename, const std::string& data);
    bool ParseConfigFile(const std::string& path);
    bool ParseConfigDir(const std::string& path);

    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
};

最后分别看看ServiceParser、ActionParser、ImportParser这3个解析器如何解析各自的块数据
Service块的解析器ServiceParser

/*首先,解析Service块首行*/
bool ServiceParser::ParseSection(const std::vector<std::string>& args,
                                 std::string* err) {
    if (args.size() < 3) {/*从Service块格式来看,初始行包括service <name> <pathname> [ <argument> ]* 等至少3个字符*/
        *err = "services must have a name and a program";
        return false;
    }
    const std::string& name = args[1];
    if (!IsValidName(name)) {/*服务命名校错*/
        *err = StringPrintf("invalid service name '%s'", name.c_str());
        return false;
    }
    std::vector<std::string> str_args(args.begin() + 2, args.end());/*将服务名后面的字符全都存入str_args对象中*/
    service_ = std::make_unique<Service>(name, "default", str_args);/*更具解析到的字符 ,创建一个Service对象 classname_="default" str_args存入Service对象的成员变量args_中*/
    return true;
}

/*然后,解析Service块其他行*/
bool ServiceParser::ParseLineSection(const std::vector<std::string>& args, const std::string& filename, int line,  std::string* err) const {
    return service_ ? service_->HandleLine(args, err) : false;
}
bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {
    if (args.empty()) {
        *err = "option needed, but not provided";
        return false;
    }
    static const OptionHandlerMap handler_map;
    auto handler = handler_map.FindFunction(args[0], args.size() - 1, err);/*根据args[0]找到对应的函数的指针 实际上就是用args填充Service块对象的各个成员变量*/
    if (!handler) {
        return false;
    }
    return (this->*handler)(args, err);/*根据args[0]找到对应的函数 来处理args*/
}

/*最后,Service块解析完成时,即将已经填充好的Service块对象加入到ServiceManager对象的链表services_中*/
void ServiceParser::EndSection() {
    if (service_) {
        ServiceManager::GetInstance().AddService(std::move(service_));
    }
}

具体的解析器
Action块的解析器ActionParser

/*首先,解析Action块的首行*/
bool ActionParser::ParseSection(const std::vector<std::string>& args,
                                std::string* err) {
    std::vector<std::string> triggers(args.begin() + 1, args.end());/*将除on之外的所有字符存入triggers*/
    if (triggers.size() < 1) {
        *err = "actions must have a trigger";
        return false;
    }
    auto action = std::make_unique<Action>(false);/*创建一个Action对象 oneshot_=false*/
    if (!action->InitTriggers(triggers, err)) {
        return false;
    }
    action_ = std::move(action);
    return true;
}
bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
    const static std::string prop_str("property:");
    for (std::size_t i = 0; i < args.size(); ++i) {
        if (i % 2) {/* 跳过&& 字符*/
            if (args[i] != "&&") {
                *err = "&& is the only symbol allowed to concatenate actions";
                return false;
            } else {
                continue;
            }
        }
        if (!args[i].compare(0, prop_str.length(), prop_str)) {/*如果args[i]包含字段property*/
            if (!ParsePropertyTrigger(args[i], err)) {/*将键值对存入map对象property_triggers_中*/
                return false;
            }
        } else {/*某个时间条件触发*/
            if (!event_trigger_.empty()) {
                *err = "multiple event triggers are not allowed";
                return false;
            }
            event_trigger_ = args[i];/*如果是以某个时间条件为触发,将该值存入string对象event_trigger_中*/
        }
    }
    return true;
}

/*然后,解析Action块其他行*/
bool ActionParser::ParseLineSection(const std::vector<std::string>& args,const std::string& filename, int line,std::string* err) const {
    return action_ ? action_->AddCommand(args, filename, line, err) : false;
}
bool Action::AddCommand(const std::vector<std::string>& args, const std::string& filename, int line, std::string* err) {
    if (!function_map_) {
        *err = "no function map available";
        return false;
    }
    if (args.empty()) {
        *err = "command needed, but not provided";
        return false;
    }
    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);/*function_map_是一个BuiltinFunctionMap对象的指针,根据args[0]查找到对应的函数指针*/
/*在init的主函数声明解析器之前,创建过一个BuiltinFunctionMap对象,并将该对象的指针赋值给Action对象的function_map_变量
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);
*/
    if (!function) {
        return false;
    }
    AddCommand(function, args, filename, line);/*如果查找到对应的函数指针,将该函数指针存入成员变量commands_中*/
    return true;
}

void Action::AddCommand(BuiltinFunction f,
                        const std::vector<std::string>& args,
                        const std::string& filename, int line) {
    commands_.emplace_back(f, args, filename, line);
}

/*最后,Action块解析完成时,即将已经填充好的Action块对象加入到ActionManager对象的链表actions_中*/
void ActionParser::EndSection() {
    if (action_ && action_->NumCommands() > 0) {
        ActionManager::GetInstance().AddAction(std::move(action_));
    }
}

Import块的解析器ImportParser

/*ImportParser类的声明如下*/
class ImportParser : public SectionParser {
public:
    ImportParser() {
    }
    bool ParseSection(const std::vector<std::string>& args,
                      std::string* err) override;
    bool ParseLineSection(const std::vector<std::string>& args,
                          const std::string& filename, int line,
                          std::string* err) const override {
        return true;
    }
    void EndSection() override {
    }
    void EndFile(const std::string& filename) override;
private:
    std::vector<std::string> imports_;
};

/*首先,解析Import块的首行*/
bool ImportParser::ParseSection(const std::vector<std::string>& args,
                                std::string* err) {
    if (args.size() != 2) {
/*Import块格式如下所示,size为2,否则判断格式错误
import /init.${ro.zygote}.rc
*/
        *err = "single argument needed for import\n";
        return false;
    }

    std::string conf_file;
    bool ret = expand_props(args[1], &conf_file);
    if (!ret) {
        *err = "error while expanding import";
        return false;
    }

    INFO("Added '%s' to import list\n", conf_file.c_str());
    imports_.emplace_back(std::move(conf_file));/*配置文件名存入成员变量imports_中*/
    return true;
}

/*然后,解析Import块其他行, 但是因为Import块只有一个声明行,无实际的内容来填充,所以ParseLineSection函数没有具体的实现*/


/*最后,Import块解析完成时,再次使用Parser对象的ParseConfig函数解析该配置文件中的各个块对象*/
void ImportParser::EndFile(const std::string& filename) {
    auto current_imports = std::move(imports_);
    imports_.clear();
    for (const auto& s : current_imports) {
        if (!Parser::GetInstance().ParseConfig(s)) {
            ERROR("could not import file '%s' from '%s': %s\n",
                  s.c_str(), filename.c_str(), strerror(errno));
        }
    }
}

到此,将所有配置文件中的Action块和Service块对象分别存入了链表actions_和services_中

14 按触发添加执行 Action块

    ActionManager& am = ActionManager::GetInstance();/*获得一个行为块管理器对象,因为是单例模式,所以拿到的就是上一步 填充后的ActionManager对象*/
    am.QueueEventTrigger("early-init"); /*将early-init加入触发器队列中*/
/*
void ActionManager::QueueEventTrigger(const std::string& trigger) {
    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
}
*/
    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
/*创建一个名为wait_for_coldboot_done的Action块,触发器就是wait_for_coldboot_done,该函数具体实现如下
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
    auto action = std::make_unique<Action>(true);/*创建一个Action对象 oneshot_=true*/
    std::vector<std::string> name_vector{name};
    if (!action->InitSingleTrigger(name)) {/*该Action对象的event_trigger_="wait_for_coldboot_done"*/
        return;
    } 
//将函数指针wait_for_coldboot_done_action存入成员变量commands_中
    action->AddCommand(func, name_vector);//void AddCommand(BuiltinFunction f,const std::vector<std::string>& args,const std::string& filename = "", int line = 0);
    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get())); /*将wait_for_coldboot_done加入ActionManager触发器队列中*/
    actions_.emplace_back(std::move(action));/*将新创建的Action块加入ActionManager对象的actions_链表中*/
}
*/

    // ... so that we can start queuing up actions that require stuff from /dev.
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
    am.QueueBuiltinAction(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");/*将init加入触发器队列中*/
    // 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(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    // Don't mount filesystems or start core system services in charger mode.
    std::string bootmode = property_get("ro.bootmode");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }
    // Run all property triggers based on current state of the properties.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

至此,动态添加了几个Action块,并将系统运行各个时间点添加在trigger_queue_队列中。

15. 进入 while 循环

该循环中主要做了3个事:

  • 按trigger_queue_中触发器的顺序,依次执行actions_链表中的action块
  • 检查所有已经注册的服务,根据flag标识判断是否重启它
  • 子进程信号监控
while (true) {
        if (!waiting_for_exec) {
            am.ExecuteOneCommand(); /*按trigger_queue_中触发器的顺序,依次执行actions_链表中的action块*/ 
            restart_processes();/*重启链表services_中包含SVC_RESTARTING flag的service*/
        }
        int timeout = -1;
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }
        if (am.HasMoreCommands()) {/*当前current_executing_actions_队列还有action未执行完*/
            timeout = 0;
        }
        bootchart_sample(&timeout);
        epoll_event ev;/*子进程信号处理*/
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }
}
void ActionManager::ExecuteOneCommand() {
    // Loop through the trigger queue until we have an action to execute
    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
        for (const auto& action : actions_) {/*遍历actions_中的Action块*/
            if (trigger_queue_.front()->CheckTriggers(*action)) {
                current_executing_actions_.emplace(action.get());/*根据trigger_queue_中的顺序,依次遍历,对于当前具有相同触发点的action块,将他们都加入current_executing_actions_队列*/
            }
        }
        trigger_queue_.pop();
    }
/* 至此,current_executing_actions_队列填满了 包含 trigger_queue_队尾触发器的 action对象*/
    if (current_executing_actions_.empty()) {
        return;
    }
    auto action = current_executing_actions_.front();
    if (current_command_ == 0) {
        std::string trigger_name = action->BuildTriggersString();
        INFO("processing action (%s)\n", trigger_name.c_str());/* 打印当前action块所属的触发器*/
    }

    action->ExecuteOneCommand(current_command_);/*第一次进入current_command_是为0的,后续依次++,此处配合init主函数中的while,会依次执行action的所有Command*/

    // If this was the last command in the current action, then remove the action from the executing list.
    // If this action was oneshot, then also remove it from actions_.
    ++current_command_;
    if (current_command_ == action->NumCommands()) {/* 如果当前action的command都执行完,current_executing_actions_队列弹出该action,继续执行下一个action*/
        current_executing_actions_.pop();
        current_command_ = 0;
        if (action->oneshot()) {
            auto eraser = [&action] (std::unique_ptr<Action>& a) {
                return a.get() == action;
            };
            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
        }
    }
}

void Action::ExecuteOneCommand(std::size_t command) const {
    ExecuteCommand(commands_[command]);
}

void Action::ExecuteCommand(const Command& command) const {
    Timer t;
    int result = command.InvokeFunc();/*执行commands_中的函数指针*/

    if (klog_get_level() >= KLOG_INFO_LEVEL) {
        std::string trigger_name = BuildTriggersString();
        std::string cmd_str = command.BuildCommandString();
        std::string source = command.BuildSourceString();

        INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
             cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
             result, t.duration());
    }/*打印当前执行的command信息*/
}

然后从log上看该while循环执行的各个action块及相关连的command如下:

[ 14.312317] [1970-01-15 23:27:13 GMT+0][pid:1,cpu7,init]init: processing action (early-init)
[ 14.399194] [1970-01-15 23:27:13 GMT+0][pid:1,cpu7,init]init: Command 'start ueventd' action=early-init (/init.rc:29) returned 0 took 0.01s
[ 37.093815] [1970-01-15 23:27:36 GMT+0][pid:1,cpu5,init]init: processing action (wait_for_coldboot_done)
[ 37.134695] [1970-01-15 23:27:36 GMT+0][pid:1,cpu5,init]init: processing action (mix_hwrng_into_linux_rng)
[ 37.162899] [1970-01-15 23:27:36 GMT+0][pid:1,cpu5,init]init: processing action (keychord_init)
[ 37.183120] [1970-01-15 23:27:36 GMT+0][pid:1,cpu5,init]init: processing action (console_init)
[ 37.202701] [1970-01-15 23:27:36 GMT+0][pid:1,cpu5,init]init: processing action (init)
[ 39.603512] [1970-01-15 23:27:39 GMT+0][pid:1,cpu5,init]init: processing action (mix_hwrng_into_linux_rng)
[ 39.631760] [1970-01-15 23:27:39 GMT+0][pid:1,cpu5,init]init: processing action (late-init)
[ 39.795084] [1970-01-15 23:27:39 GMT+0][pid:1,cpu5,init]init: processing action (queue_property_triggers)
[ 39.817942] [1970-01-15 23:27:39 GMT+0][pid:1,cpu5,init]init: processing action (fs)
[ 40.857381] [1970-01-15 23:27:40 GMT+0][pid:1,cpu2,init]init: processing action (post-fs)
[ 40.864238] [1970-01-15 23:27:40 GMT+0][pid:1,cpu2,init]init: Command 'start logd' action=post-fs (/init.rc:303) returned 0 took 0.01s
[ 42.041855] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: Command 'start powerup_reason' action=post-fs (/system/etc/init/powerup_reason.rc:7) returned 0 took 0.00s
[ 42.042027] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: processing action (load_system_props_action)
[ 42.054539] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: processing action (post-fs-data)
[ 42.057279] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: Command 'start debuggerd' action=post-fs-data (/init.rc:357) returned 0 took 0.00s
[ 42.059429] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: Command 'start debuggerd64' action=post-fs-data (/init.rc:358) returned 0 took 0.00s
[ 42.061791] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: Command 'start vold' action=post-fs-data (/init.rc:361) returned 0 took 0.00s
[ 42.401748] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: Command 'start perfd' action=post-fs-data (/init.qcom.rc:333) returned 0 took 0.00s
[ 42.434200] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: processing action (load_persist_props_action)
[ 42.434393] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: Command 'start logd' action=load_persist_props_action (/init.rc:258) returned -1 took 0.00s
[ 42.436173] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: Command 'start logd-reinit' action=load_persist_props_action (/init.rc:259) returned 0 took 0.00s
[ 42.436420] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: processing action (firmware_mounts_complete)
[ 42.436554] [1970-01-15 23:27:41 GMT+0][pid:1,cpu4,init]init: processing action (early-boot) 
[ 50.796254] [1970-01-15 23:27:50 GMT+0][pid:1,cpu5,init]init: processing action (boot)
[ 50.896965] [1970-01-15 23:27:50 GMT+0][pid:1,cpu7,init]init: Command 'start rmt_storage' action=boot (init.target.rc:250) returned -1 took 0.00s
[ 50.950443] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (persist.sys.usb.config=* boot)
[ 50.950559] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (enable_property_trigger)
[ 50.951530] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (security.perf_harden=1)
[ 50.951834] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (ro.debuggable=1)
[ 50.954373] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: Command 'start console' action=ro.debuggable=1 (/init.rc:675) returned 0 took 0.00s
[ 50.954580] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (persist.sys.ssr.restart_level=*)
[ 50.957768] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: Command 'start ssr_setup' action=persist.sys.ssr.restart_level=* (/init.qcom.rc:429) returned 0 took 0.00s
[ 50.957837] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (ro.logdumpd.enabled=1)
[ 50.960202] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: Command 'start logdumpd' action=ro.logdumpd.enabled=1 (/init.qcom.rc:1141) returned 0 took 0.00s
[ 50.960301] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (sys.usb.config=diag,serial_smd,rmnet_ipa,adb sys.usb.configfs=0)
[ 50.972534] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: Command 'start adbd' action=sys.usb.config=diag,serial_smd,rmnet_ipa,adb sys.usb.configfs=0 (init.qcom.usb.rc:572) returned 0 took 0.00s
[ 50.972832] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (ro.kernel.nfc.enable=true)
[ 50.974119] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (init.svc.surfaceflinger=running)
[ 50.976797] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: Command 'start ppd' action=init.svc.surfaceflinger=running (init.target.rc:371) returned 0 took 0.00s
[ 50.976862] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (init.svc.per_mgr=running)
[ 50.994331] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: Command 'start per_proxy' action=init.svc.per_mgr=running (init.target.rc:433) returned 0 took 0.02s
[ 50.994397] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (persist.sys.dload.enable=*)
[ 50.996969] [1970-01-15 23:27:50 GMT+0][pid:1,cpu2,init]init: processing action (defaultcrypto)
[ 60.797719] [1970-01-15 23:28:00 GMT+0][pid:1,cpu0,init]init: processing action (vold.decrypt=trigger_load_persist_props)
[ 60.941446] [1970-01-15 23:28:00 GMT+0][pid:1,cpu1,init]init: Command 'start logd' action=vold.decrypt=trigger_load_persist_props (/init.rc:610) returned -1 took 0.00s
[ 60.943422] [1970-01-15 23:28:00 GMT+0][pid:1,cpu1,init]init: Command 'start logd-reinit' action=vold.decrypt=trigger_load_persist_props (/init.rc:611) returned 0 took 0.00s
[ 60.967500] [1970-01-15 23:28:00 GMT+0][pid:1,cpu5,init]init: processing action (vold.decrypt=trigger_post_fs_data)
[ 60.968585] [1970-01-15 23:28:00 GMT+0][pid:1,cpu5,init]init: processing action (post-fs-data)
[ 60.969306] [1970-01-15 23:28:00 GMT+0][pid:1,cpu5,init]init: Command 'start debuggerd' action=post-fs-data (/init.rc:357) returned -1 took 0.00s
[ 60.969340] [1970-01-15 23:28:00 GMT+0][pid:1,cpu5,init]init: Command 'start debuggerd64' action=post-fs-data (/init.rc:358) returned -1 took 0.00s
[ 60.969375] [1970-01-15 23:28:00 GMT+0][pid:1,cpu5,init]init: Command 'start vold' action=post-fs-data (/init.rc:361) returned -1 took 0.00s
[ 61.141248] [1970-01-15 23:28:00 GMT+0][pid:1,cpu6,init]init: Command 'start perfd' action=post-fs-data (/init.qcom.rc:333) returned -1 took 0.00s
[ 61.170593] [1970-01-15 23:28:00 GMT+0][pid:1,cpu6,init]init: processing action (init.svc.bootanim=running post-fs-data)
[ 61.194827] [1970-01-15 23:28:00 GMT+0][pid:1,cpu6,init]init: processing action (vold.decrypt=trigger_restart_framework)
[ 61.753602] [2020-02-05 07:03:46 GMT+0][pid:1,cpu1,init]init: Command 'start qcom-c_main-sh' action=vold.decrypt=trigger_restart_framework (/init.qcom.rc:503) returned -1 took 0.00s
[ 61.756896] [2020-02-05 07:03:46 GMT+0][pid:1,cpu1,init]init: Command 'start config_bt_addr' action=vold.decrypt=trigger_restart_framework (/init.qcom.rc:504) returned 0 took 0.00s
[ 61.759685] [2020-02-05 07:03:46 GMT+0][pid:1,cpu1,init]init: Command 'start config_bluetooth' action=vold.decrypt=trigger_restart_framework (/init.qcom.rc:505) returned 0 took 0.00s
[ 61.765082] [2020-02-05 07:03:46 GMT+0][pid:1,cpu1,init]init: Command 'start wcnss-service' action=vold.decrypt=trigger_restart_framework (/init.qcom.rc:506) returned -1 took 0.00s
[ 61.765167] [2020-02-05 07:03:46 GMT+0][pid:1,cpu1,init]init: Command 'start cnss_diag' action=vold.decrypt=trigger_restart_framework (/init.qcom.rc:507) returned -1 took 0.00s
[ 61.911523] [2020-02-05 07:03:46 GMT+0][pid:1,cpu3,init]init: processing action (init.svc.zygote=running)
[ 61.911616] [2020-02-05 07:03:46 GMT+0][pid:1,cpu3,init]init: Command 'start ppd' action=init.svc.zygote=running (init.target.rc:380) returned -1 took 0.00s
[ 62.018906] [2020-02-05 07:03:46 GMT+0][pid:1,cpu3,init]init: processing action (sys.ims.QMI_DAEMON_STATUS=1)
[ 62.021461] [2020-02-05 07:03:46 GMT+0][pid:1,cpu3,init]init: Command 'start imsdatadaemon' action=sys.ims.QMI_DAEMON_STATUS=1 (init.target.rc:335) returned 0 took 0.00s
[ 79.325050] [2020-02-05 15:04:03 GMT+8][pid:1,cpu4,init]init: processing action (sys.sysctl.extra_free_kbytes=*)
[ 80.140825] [2020-02-05 15:04:04 GMT+8][pid:1,cpu5,init]init: processing action (sys.usb.config=none sys.usb.configfs=0)
[ 80.291796] [2020-02-05 15:04:04 GMT+8][pid:1,cpu5,init]init: processing action (sys.usb.config=diag,serial_smd,rmnet_ipa,adb sys.usb.configfs=0)
[ 80.502984] [2020-02-05 15:04:05 GMT+8][pid:1,cpu5,init]init: Command 'start adbd' action=sys.usb.config=diag,serial_smd,rmnet_ipa,adb sys.usb.configfs=0 (init.qcom.usb.rc:572) returned 0 took 0.01s
[ 82.900831] [2020-02-05 15:04:07 GMT+8][pid:1,cpu5,init]init: processing action (sys.boot_completed=1)
[ 83.009905] [2020-02-05 15:04:07 GMT+8][pid:1,cpu5,init]init: Command 'start qcom-post-boot' action=sys.boot_completed=1 (/init.qcom.rc:908) returned 0 took 0.02s
[ 83.034072] [2020-02-05 15:04:07 GMT+8][pid:1,cpu5,init]init: Command 'start qti-testscripts' action=sys.boot_completed=1 (/init.qcom.rc:909) returned 0 took 0.01s
[ 83.060367] [2020-02-05 15:04:07 GMT+8][pid:1,cpu5,init]init: Command 'start qrngp' action=sys.boot_completed=1 (init.target.rc:208) returned -1 took 0.01s
[ 83.488821] [2020-02-05 15:04:08 GMT+8][pid:1,cpu6,init]init: Command 'start cs-post-boot' action=sys.boot_completed=1 (init.qcom.test.rc:45) returned 0 took 0.01s
[ 83.522869] [2020-02-05 15:04:08 GMT+8][pid:1,cpu6,init]init: Command 'start smcd' action=sys.boot_completed=1 (/system/etc/init/smcd.rc:23) returned 0 took 0.02s
[ 83.584295] [2020-02-05 15:04:08 GMT+8][pid:1,cpu6,init]init: processing action (sys.boot_completed=1 sys.logbootcomplete=1)

16 守护进程ueventd

从上面的log看来,开启的第一个服务就是ueventd,它在rc文件中的声明如下:

/*在early-init阶段启动该服务*/
service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

ueventd的作用是用来接收uevent来创建和删除设备中dev目录下的设备节点,它使用的二进制文件/sbin/ueventd实际是init二进制文件的一个文件链接即ueventd的二进制文件和init二进制文件相同。然后看看它是如何执行的:
首先,它是在early-init阶段使用command命令start ueventd开启执行,即ExecuteCommand方法中的参数Command对象为Command(do_start,"start ueventd",“init.rc”,line=*),实际执行的语句类似于do_start(“start ueventd”)`,而函数do_start定义在文件system/core/init/builtins.cpp中

static int do_start(const std::vector<std::string>& args) {
    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);/*找到名为ueventd的Service块对象*/
    if (!svc) {
        ERROR("do_start: Service %s not found\n", args[1].c_str());
        return -1;
    }
    if (!svc->Start())/*调用Service的Start方法*/
        return -1;
    return 0;
}

然后看看Service的Start方法做了什么操作

/*
第一步:检查该服务的运行状态
第二步:检查Service块对象中selinux安全上下文信息
第三步:fork一个子进程, 初始化各种环境变量
第四步:系统调用函数execve执行对应的二进制文件,即开始进入该二进制文件的main函数
*/
bool Service::Start() {
    // Starting a service removes it from the disabled or reset state and immediately takes it out of the restarting state if it was in there.
    flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));/*修改该服务的状态 */
    time_started_ = 0;
    // Running processes require no additional work --- if they're in the process of exiting, we've ensured that they will immediately restart on exit, unless they are ONESHOT.
    if (flags_ & SVC_RUNNING) {/*检查是否已经处于运行状态*/
        return false;
    }
    bool needs_console = (flags_ & SVC_CONSOLE);
    if (needs_console && !have_console) {
        ERROR("service '%s' requires console\n", name_.c_str());
        flags_ |= SVC_DISABLED;
        return false;
    }

    struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
        ERROR("cannot find '%s' (%s), disabling '%s'\n",
              args_[0].c_str(), strerror(errno), name_.c_str());
        flags_ |= SVC_DISABLED;
        return false;
    }

    std::string scon;
    if (!seclabel_.empty()) {/* selinux安全上下文检查*/
        scon = seclabel_;
    } else {
        char* mycon = nullptr;
        char* fcon = nullptr;

        INFO("computing context for service '%s'\n", args_[0].c_str());
        int rc = getcon(&mycon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", name_.c_str());
            return false;
        }

        rc = getfilecon(args_[0].c_str(), &fcon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", name_.c_str());
            free(mycon);
            return false;
        }

        char* ret_scon = nullptr;
        rc = security_compute_create(mycon, fcon, string_to_security_class("process"),
                                     &ret_scon);
        if (rc == 0) {
            scon = ret_scon;
            free(ret_scon);
        }
        if (rc == 0 && scon == mycon) {
            ERROR("Service %s does not have a SELinux domain defined.\n", name_.c_str());
            free(mycon);
            free(fcon);
            return false;
        }
        free(mycon);
        free(fcon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", name_.c_str());
            return false;
        }
    }

    NOTICE("Starting service '%s'...\n", name_.c_str());

    pid_t pid = fork();/*fork一个子进程*/
    if (pid == 0) {
        umask(077);
        for (const auto& ei : envvars_) {//添加环境变量信息
            add_environment(ei.name.c_str(), ei.value.c_str());
        }
        for (const auto& si : sockets_) {
            int socket_type = ((si.type == "stream" ? SOCK_STREAM :
                                (si.type == "dgram" ? SOCK_DGRAM :
                                 SOCK_SEQPACKET)));
            const char* socketcon =
                !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();

            int s = create_socket(si.name.c_str(), socket_type, si.perm,
                                  si.uid, si.gid, socketcon);
            if (s >= 0) {
                PublishSocket(si.name, s);
            }
        }

        std::string pid_str = StringPrintf("%d", getpid());
        for (const auto& file : writepid_files_) {
            if (!WriteStringToFile(pid_str, file)) {
                ERROR("couldn't write %s to %s: %s\n",
                      pid_str.c_str(), file.c_str(), strerror(errno));
            }
        }

        if (ioprio_class_ != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
                      getpid(), ioprio_class_, ioprio_pri_, strerror(errno));
            }
        }

        if (needs_console) {
            setsid();
            OpenConsole();
        } else {
            ZapStdio();
        }

        setpgid(0, getpid());

        // As requested, set our gid, supplemental gids, and uid.
//设置一些参数,uid,gid,写入文件等
        if (gid_) {
            if (setgid(gid_) != 0) {
                ERROR("setgid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (!supp_gids_.empty()) {
            if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
                ERROR("setgroups failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (uid_) {
            if (setuid(uid_) != 0) {
                ERROR("setuid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (!seclabel_.empty()) {
            if (setexeccon(seclabel_.c_str()) < 0) {
                ERROR("cannot setexeccon('%s'): %s\n",
                      seclabel_.c_str(), strerror(errno));
                _exit(127);
            }
        }

        std::vector<std::string> expanded_args;
        std::vector<char*> strs;
        expanded_args.resize(args_.size());
        strs.push_back(const_cast<char*>(args_[0].c_str()));
        for (std::size_t i = 1; i < args_.size(); ++i) {
            if (!expand_props(args_[i], &expanded_args[i])) {
                ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
                _exit(127);
            }
            strs.push_back(const_cast<char*>(expanded_args[i].c_str()));
        }
        strs.push_back(nullptr);
/*execve这个函数都是Linux系统上的系统调用,此处会执行/sbin/ueventd二进制文件,这样就进入到ueventd的main函数中了。*/
        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
            ERROR("cannot execve('%s'): %s\n", strs[0], strerror(errno));
        }

        _exit(127);
    }

    if (pid < 0) {
        ERROR("failed to start '%s'\n", name_.c_str());
        pid_ = 0;
        return false;
    }

    time_started_ = gettime();
    pid_ = pid;
    flags_ |= SVC_RUNNING;

    if ((flags_ & SVC_EXEC) != 0) {
        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
             pid_, uid_, gid_, supp_gids_.size(),
             !seclabel_.empty() ? seclabel_.c_str() : "default");
    }
:
    NotifyStateChange("running");
    return true;
}

至此,init创建出一个子进程并让子进程开始运行ueventd二进制文件,即开始转入ueventd二进制文件的main函数:

int main(int argc, char** argv) {
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);/*转入ueventd真正的主函数ueventd_main中*/
    }
    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }
/* 后续代码省略*/
}

ueventd 入口函数ueventd_main主要做了4件事:

  • 第一步:初始化工作,如设置umask、标准输入输出、错误重定向
  • 第二步: 解析ueventd相关的rc文件,把每行的数据存入perm_node结构体中,而perm_node对象会分别加入链表sys_perms或者dev_perms中
  • 第三步:device_init()函数初始化设备,即创建一个socket,接收来自kernel的uevent信息
  • 第四步:对步骤三创建的socket循环调用poll函数,等待POLLIN事件的到来,如果有数据到来则调用函数handle_device_fd()
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.
     */
    umask(000);
    /* Prevent fire-and-forget children from becoming zombies.
     * If we should need to wait() for some children in the future (as opposed to none right now), double-forking here instead of ignoring SIGCHLD may be the better solution.
     */
    signal(SIGCHLD, SIG_IGN);/*不处理SIGCHLD 信号*/
    open_devnull_stdio();/*定向标准输入 输出 错误重定向等的文件目录*/
    klog_init();/*创建节点/dev/__kmsg__,进程使用kernel的log系统输出log*/
    klog_set_level(KLOG_NOTICE_LEVEL);/*设置log等级*/
    NOTICE("ueventd started!\n");
    selinux_callback cb;
    cb.func_log = selinux_klog_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

    std::string hardware = property_get("ro.hardware");
    ueventd_parse_config_file("/ueventd.rc");/*解析ueventd.rc文件*/
    ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());

    boot_device = property_get("ro.boot.bootdevice");
    device_init();
    pollfd ufd;
    ufd.events = POLLIN;
    ufd.fd = get_device_fd();/*get_device_fd()返回device_init()函数中创建的socket的句柄*/

    while (true) {
        ufd.revents = 0;
        int nr = poll(&ufd, 1, -1);//等待ufd上有POLLIN 事件到来
        if (nr <= 0) {
            continue;
        }
        if (ufd.revents & POLLIN) {
            handle_device_fd(); //如果等到数据到来,执行handle_device_fd函数
        }
    }
    return 0;
}

第一步

第二步
解析ueventd相关的rc文件,主要调用函数 ueventd_parse_config_file实现,解析过程和前面解析init.rc相似,只不过此处解析到的内容是存在perm_node结构体中

int ueventd_parse_config_file(const char *fn)
{
    std::string data;
    if (!read_file(fn, &data)) {/*将rc中文件读取到data中*/
        return -1;
    }
    data.push_back('\n'); // TODO: fix parse_config.
    parse_config(fn, data);/*实际的解析在parse_config函数中实现*/
    return 0;
}
static void parse_config(const char *fn, const std::string& data)
{
    char *args[UEVENTD_PARSER_MAXARGS];/*临时存储每行的数据*/
    int nargs = 0;
    parse_state state;
/*
struct parse_state
{
    char *ptr;
    char *text;
    int line;
    int nexttoken;
    void *context;
    void (*parse_line)(struct parse_state *state, int nargs, char **args);
    const char *filename;
    void *priv;
}
*/
    state.filename = fn;
    state.line = 1;
    state.ptr = strdup(data.c_str()); // TODO: fix this code!
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op;
    for (;;) {
        int token = next_token(&state);/*next_token函数从data数据第一字节开始遍历,如果遍历完一个单词,返回T_TEXT ;如果遍历完一行,返回T_NEWLINE;如果遍历完整个文本,返回T_EOF*/
        switch (token) {
        case T_EOF:
            parse_line(&state, args, nargs);
            return;
        case T_NEWLINE://一行遍历完,nargs为字符个数
            if (nargs) {
                parse_line(&state, args, nargs);
                nargs = 0;
            }
            state.line++;
            break;
        case T_TEXT:
            if (nargs < UEVENTD_PARSER_MAXARGS) {
                args[nargs++] = state.text;/*每次遍历完一个单词,都将它存入数组args中*/
            }
            break;
        }
    }
}
/*解析rc中的数据并填充结构体 parse_state state*/
static void parse_line(struct parse_state *state, char **args, int nargs)
{
    int kw = lookup_keyword(args[0]);/lookup_keyword函数搜索关键字 "devname" "dirname" "subsystem"等*/
    int kw_nargs = kw_nargs(kw);
    if (nargs < kw_nargs) {/*长度检查*/
        parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
            kw_nargs > 2 ? "arguments" : "argument");
        return;
    }
/*
#define SECTION 0x01
#define OPTION 0x02
args[0]= 'subsystem',kw='1',kw_nargs='2' 
args[0]= 'devname',kw='2',kw_nargs='2' 
args[0]= '/dev/null',kw='0',kw_nargs='0'
*/
    if (kw_is(kw, SECTION)) {/*如果行数据中第一个字符是"subsystem"*/
        parse_new_section(state, kw, nargs, args);/*创建一个ueventd_subsystem对象 */
    } else if (kw_is(kw, OPTION)) {/*如果行数据中第一个字符是"devname"*/
        state->parse_line(state, nargs, args);/* parse_line_subsystem(state,2,"devname uevent_devname")*/
    } else {
        parse_line_device(state, nargs, args);
    }
}

然后ueventd.rc中的数据如下:

subsystem adf
 devname uevent_devname
# ueventd can only set permissions on device nodes and their associated sysfs attributes, not on arbitrary paths.
# format for /dev rules: devname mode uid gid
# format for /sys rules: nodename attr mode uid gid
# shortcut: "mtd@NN" expands to "/dev/mtd/mtdNN"
/dev/null 0666 root root
/dev/zero 0666 root root
/*省略*/

接下来是将各个设备节点数据存入perm_node结构体中

static void parse_line_device(parse_state*, int nargs, char** args) {
    set_device_permission(nargs, args);
}
void set_device_permission(int nargs, char **args)/*/dev/null 0666 root root*/
{
    char *name;
    char *attr = 0;
    mode_t perm;
    uid_t uid;
    gid_t gid;
    int prefix = 0;
    int wildcard = 0;
    char *endptr;
    int ret;
    char *tmp = 0;
    if (nargs == 0)
        return;
    if (args[0][0] == '#')
        return;
    name = args[0];
    if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
        INFO("/sys/ rule %s %s\n",args[0],args[1]);
        attr = args[1];
        args++;
        nargs--;
    }

    if (nargs != 4) {
        ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);
        return;
    }

    /* If path starts with mtd@ lookup the mount number. */
    if (!strncmp(name, "mtd@", 4)) {
        int n = mtd_name_to_number(name + 4);
        if (n >= 0)
            asprintf(&tmp, "/dev/mtd/mtd%d", n);
        name = tmp;
    } else {
        int len = strlen(name);
        char *wildcard_chr = strchr(name, '*');
        if ((name[len - 1] == '*') &&
            (wildcard_chr == (name + len - 1))) {
            prefix = 1;
            name[len - 1] = '\0';
        } else if (wildcard_chr) {
            wildcard = 1;
        }
    }

    perm = strtol(args[1], &endptr, 8);
    if (!endptr || *endptr != '\0') {
        ERROR("invalid mode '%s'\n", args[1]);
        free(tmp);
        return;
    }

    ret = get_android_id(args[2]);
    if (ret < 0) {
        ERROR("invalid uid '%s'\n", args[2]);
        free(tmp);
        return;
    }
    uid = ret;

    ret = get_android_id(args[3]);
    if (ret < 0) {
        ERROR("invalid gid '%s'\n", args[3]);
        free(tmp);
        return;
    }
    gid = ret;

    add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard);/*创建perm_node结构体并填充数据*/
    free(tmp);
}

第三步
使用函数device_init

  • 创建一个socket,接收来自kernel的uevent信息
  • 对"/sys/class" “/sys/block” "/sys/devices"执行coldboot,让kernel重新生成 device add 事件
void device_init() {
    sehandle = selinux_android_file_context_handle();
    selinux_status_open(true);

    /* is 256K enough? udev uses 16MB! */
    device_fd = uevent_open_socket(256*1024, true);/*创建一个Netlink socked*/
    if (device_fd == -1) {
        return;
    }
    fcntl(device_fd, F_SETFL, O_NONBLOCK);

    if (access(COLDBOOT_DONE, F_OK) == 0) {
        NOTICE("Skipping coldboot, already done!\n");
        return;
    }
    Timer t;
    coldboot("/sys/class");
    coldboot("/sys/block");
    coldboot("/sys/devices");
    close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
    NOTICE("Coldboot took %.2fs.\n", t.duration());
}
int get_device_fd()
{
    return device_fd;
}

首先,看看uevent_open_socket函数的实现,该函数主要做3件事:

  • 1.创建一个本地 socket地址
  • 2.打开一个socket
  • 3.使用bind函数将本地 socket地址与 socket绑定起来
int uevent_open_socket(int buf_sz, bool passcred)
{
    struct sockaddr_nl addr;
    int on = passcred;
    int s;
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;/*指定地址族*/
    addr.nl_pid = getpid();/* 当前进程的pid,用来接收信息*/
    addr.nl_groups = 0xffffffff;/*加入所有多播组*/

    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);/*创建一个Nelink socket,协议为NETLINK_KOBJECT_UEVENT*/
    if(s < 0)
        return -1;
    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz));
    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));

    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {/*地址与socket绑定*/
        close(s);
        return -1;
    }
    return s;
}

然后,对"/sys/class" “/sys/block” "/sys/devices"执行coldboot

static void coldboot(const char *path)
{
    DIR *d = opendir(path);
    if(d) {
        do_coldboot(d);
        closedir(d);
    }
}

/* Coldboot walks parts of the /sys tree and pokes the uevent files to cause the kernel to regenerate device add events that happened before init's device manager was started We drain any pending events from the netlink socket every time we poke another uevent file to make sure we don't overrun the socket's buffer.
1. Coldboot遍历/sys树找到所有uevent文件并向它写入'add'字符,以使内核重新发一次设备的add消息
2. 这样做是因为uventd在的设备管理器启动之前,有部分设备节点已经add过了
3. Coldboot遍历操作同时可以确保不会重复处理netlink socket事件
*/
static void do_coldboot(DIR *d)
{
    struct dirent *de;
    int dfd, fd;

    dfd = dirfd(d);
    fd = openat(dfd, "uevent", O_WRONLY);
    if(fd >= 0) {
        write(fd, "add\n", 4);
        close(fd);
        handle_device_fd();
    }
    while((de = readdir(d))) {
        DIR *d2;
        if(de->d_type != DT_DIR || de->d_name[0] == '.')
            continue;
        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
        if(fd < 0)
            continue;
        d2 = fdopendir(fd);
        if(d2 == 0)
            close(fd);
        else {
            do_coldboot(d2);
            closedir(d2);
        }
    }
}

第四步
如果等到数据到来,

  • 从 socket中读取传过来的数据
  • 然后用函数parse_event将数据解析成uevet
  • handle_device_event和handle_firmware_event处理解析出来的uevent,即添加、删除、修改设备节点
void handle_device_fd()
{
    char msg[UEVENT_MSG_LEN+2];
    int n;
    while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
        if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
            continue;

        msg[n] = '\0';
        msg[n+1] = '\0';

        struct uevent uevent;
/*读取到的数据类似于:
msg= 'remove@/devices/soc/1d00000.qcom,vidc/1d00000.qcom,vidc:arm9_bus_ddr/devfreq/1d00000.qcom,vidc:arm9_bus_ddr'ueventd: event { 'remove', '/devices/soc/1d00000.qcom,vidc/1d00000.qcom,vidc:venus_bus_ddr/devfreq/1d00000.qcom,vidc:venus_bus_ddr', 'devfreq', '', -1, -1 }
*/
        parse_event(msg, &uevent);
/*解析后
 uevent->action='remove'
 uevent->path='/devices/soc/1d00000.qcom,vidc/1d00000.qcom,vidc:arm9_bus_ddr/devfreq/1d00000.qcom,vidc:arm9_bus_ddr'
 uevent->subsystem='devfreq'
uevent->firmware='',
uevent->major=-1
 uevent->minor=-1
*/
        if (selinux_status_updated() > 0) {
            struct selabel_handle *sehandle2;
            sehandle2 = selinux_android_file_context_handle();
            if (sehandle2) {
                selabel_close(sehandle);
                sehandle = sehandle2;
            }
        }
        handle_device_event(&uevent);/*实际处理设备节点函数*/
        handle_firmware_event(&uevent);/*处理firmware相关的uevent*/
    }
}

3. 总结

init进程作为系统中一个特殊的进程,它做了很多的工作

  • 创建基于内存的文件系统/dev、/proc、/sys
  • 重定向标准输入输出
  • 初始化和启动属性服务
  • 初始化&&启动selinux模块
  • 解析rc配置文件并循环执行解析到的action块中的command命令

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考 《深入解析android 5.0系统》《最强android 书架构大剖析》 Android 8.1 开机流程分析

发布了38 篇原创文章 · 获赞 14 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/cw102055234/article/details/104197497