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 開機流程分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章