《深入理解Android卷 I》- 第三章 - Init - 讀書筆記

1 概述

init是一個進程观谦,確切地說豁状,它是Linux系統(tǒng)中用戶空間的第一個進程泻红。由于Android是基于Linux內(nèi)核的谊路,所以init也是Android系統(tǒng)中用戶空間的第一個進程缠劝,它的進程號是1
職責(zé):

  • init進程負(fù)責(zé)創(chuàng)建系統(tǒng)中的幾個關(guān)鍵進程惨恭,尤其是Zygote,它更是Java世界的開創(chuàng)者锉罐。
  • Android系統(tǒng)有很多屬性脓规,于是init就提供了一個propertyService(屬性服務(wù))來管理它們抖拦。

2 init分析

init進程入口:
system/core/init/init.cpp

int main(int argc, char** argv) {
    // 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) {
        ...
        //創(chuàng)建一些文件夾
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        //掛載linux系統(tǒng)文件
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        ...
        early_mount();
    }
    //重定向標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出复颈,標(biāo)準(zhǔn)錯誤輸出到 /dev/null
    InitKernelLogging(argv);
    if (!is_first_stage) {
        // Indicate that booting is in progress to background fw loaders, etc.
        //在/dev目錄創(chuàng)建一個空文件.booting來表示正在執(zhí)行初始化
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
        //初始化和屬性相關(guān)的資源
        property_init();
        // 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
        //將內(nèi)核變量設(shè)置到內(nèi)部變量
        // 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策略, 后面有一些初始化文件上下文的操作等
    selinux_initialize(is_first_stage);
    ...
    //初始化子進程退出的信號處理過程
    signal_handler_init();
    //加載/default.prop文件
    property_load_boot_defaults();
    export_oem_lock_status();
    //啟動屬性服務(wù)器(通過socket通信)
    start_property_service();
    set_usb_controller();
    //為Action設(shè)置<command>處理函數(shù)
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);
    //解析init.rc文件
    Parser& parser = Parser::GetInstance();
    //設(shè)置對應(yīng)的解析函數(shù) “service”塊以關(guān)鍵字“service”開始帜讲,表示啟動某個進程的方式和參數(shù)
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
    //“action”塊以關(guān)鍵字“on”開始似将,表示一堆命令的集合
    parser.AddSectionParser("on", std::make_unique<ActionParser>());
    //“import”是用來引入一個init配置文件,來擴展當(dāng)前配置的
    parser.AddSectionParser("import", std::make_unique<ImportParser>());
    parser.ParseConfig("/init.rc");
    //actionManager
    ActionManager& am = ActionManager::GetInstance();
    am.QueueEventTrigger("early-init"); //earkay-init 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");
    // ... 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(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 trigger
     // 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.充電模式下不加載系統(tǒng)服務(wù)
    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) {
            //依次執(zhí)行每個action中攜帶command對應(yīng)的執(zhí)行函數(shù)
            am.ExecuteOneCommand();
            //重啟一些掛掉的進程
            restart_processes();
        }
        //以下決定timeout的時間,將影響while循環(huán)的間隔
        int timeout = -1;
        //有進程需要重啟時块饺,等待該進程重啟
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }
        //有action待處理,不等待
        if (am.HasMoreCommands()) {
            timeout = 0;
        }
        //bootchart_sample應(yīng)該是進行性能數(shù)據(jù)采樣
        bootchart_sample(&timeout);
        epoll_event ev;
        //沒有事件到來的話想诅,最多阻塞timeout時間
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
           PLOG(ERROR) << "epoll_wait failed";
        } else if (nr == 1) {
            //有事件到來来破,執(zhí)行對應(yīng)處理函數(shù)
            //根據(jù)上文知道徘禁,epoll句柄(即epoll_fd)主要監(jiān)聽子進程結(jié)束娘荡,及其它進程設(shè)置系統(tǒng)屬性的請求炮沐。
            ((void (*)()) ev.data.ptr)();
        }
    }
    }

Android在init過程中分別掛載了tmpfs大年,devpts翔试,proc垦缅,sysfs這4類文件系統(tǒng)壁涎。

2.1 配置文件解析

system/core/rootdir/init.rc
其中init.rc文件在Android系統(tǒng)運行過程中用于通用的環(huán)境設(shè)置與進程相關(guān)的定義,init.{hardware}.rc(例如庞溜,高通有init.qcom.rc流码,MTK有init.mediatek.rc)用于定義Android在不同平臺下的特定進程和環(huán)境設(shè)置等漫试。此處解析函數(shù)傳入的參數(shù)為“/init.rc”碘赖,解析的是運行時與init進程同在根目錄下的init.rc文件驾荣。
主要有兩部分:

  • on
    on <trigger>
    <command>
    <command>
    <command>
    Action其實就是一序列的CommandsAction都有一個trigger普泡,它被用于決定action的執(zhí)行時間播掷。當(dāng)一個符合action觸發(fā)條件的事件發(fā)生時,action會被加入到執(zhí)行隊列的末尾撼班。
    隊列中的每一個action都會被提取出,而這個action中的每個command都將被依次執(zhí)行砰嘁。Init在這些命令的執(zhí)行期間還控制著其他的活動

on early-init
//Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
...
start ueventd

  • service
    service <name> <pathname> [ <argument> ]*
    Services是一個程序件炉,他在初始化時啟動勘究,并在退出時重啟(可選)。

service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0

借助系統(tǒng)環(huán)境變量或Linux命令斟冕,on列表用于創(chuàng)建所需目錄口糕,以及為某些特定文件指定權(quán)限,而服務(wù)列表用來記錄init進程需要啟動的一些子進程磕蛇。如上面代碼所示走净,service關(guān)鍵字后的第一個字符串表示服務(wù)(子進程)的名稱,第二個字符串表示服務(wù)的執(zhí)行路徑孤里。

2.1.1 解析init.rc

system/core/inti/inti_pareser.cpp

bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {
        return ParseConfigDir(path);
    }
    //init.cpp中傳入的是"/init.rc"是文件
    return ParseConfigFile(path);
}

Parser::ParseConfigFile(const std::string& path) {
    Timer t;
    std::string data;
    //讀取指定文件的內(nèi)容,以string保存
    if (!read_file(path.c_str(), &data)) {
        return false;
    }
   ...
   ParseData(path, data);
    ...
}

Parser::ParseData(const std::string& filename, const std::string& data)根據(jù)關(guān)鍵字解析出服務(wù)和動作橘洞。動作與服務(wù)會分別放在了Action::ActionManager.actions_Service::ServiceManager.services_

bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {
        return ParseConfigDir(path);
    }
    //init.cpp中傳入的是"/init.rc"是文件
    return ParseConfigFile(path);
}


Parser::ParseConfigFile(const std::string& path) {
    Timer t;
    std::string data;
    //讀取指定文件的內(nèi)容捌袜,以string保存
    if (!read_file(path.c_str(), &data)) {
        return false;
    }
   ...
   ParseData(path, data);
    ...
}

Parser::ParseData(const std::string& filename, const std::string& data)根據(jù)關(guān)鍵字解析出服務(wù)和動作。動作與服務(wù)會分別放在了Action::ActionManager.actions_Service::ServiceManager.services_

void Parser::ParseData(const std::string& filename, const std::string& data) {
    std::vector<char> data_copy(data.begin(), data.end());
    data_copy.push_back('\0');
    parse_state state;
    ...
    SectionParser* section_parser = nullptr;
    std::vector<std::string> args;
    for (;;) {
        //next_token以行為單位分割參數(shù)傳遞過來的字符串
        //最先走到T_TEXT分支
        switch (next_token(&state)) {
        case T_EOF:
            if (section_parser) {
                //EOF,解析結(jié)束
                section_parser->EndSection();
            }
            return;
        case T_NEWLINE:
            state.line++;
            if (args.empty()) {
                break;
            }
             //在init.cpp::main()創(chuàng)建parser時炸枣,為service虏等,on,import定義了對應(yīng)的parser 
            //這里就是根據(jù)第一個參數(shù)适肠,判斷是否有對應(yīng)的parser
            if (section_parsers_.count(args[0])) {
                if (section_parser) {
                   //結(jié)束上一個parser的工作霍衫,將構(gòu)造出的對象加入到對應(yīng)的service_list與action_list中
                    section_parser->EndSection();
                }
                //獲取參數(shù)對應(yīng)的parser
                section_parser = section_parsers_[args[0]].get();
                std::string ret_err;
                //調(diào)用實際parser的ParseSection函數(shù)
                if (!section_parser->ParseSection(args, &ret_err)) {
                    parse_error(&state, "%s\n", ret_err.c_str());
                    section_parser = nullptr;
                }
            } else if (section_parser) {
                //如果第一個參數(shù)不是service,on侯养,import
                //則調(diào)用前一個parser的ParseLineSection函數(shù)
                //這里相當(dāng)于解析一個參數(shù)塊的子項
                std::string ret_err;
                if (!section_parser->ParseLineSection(args, state.filename,state.line, &ret_err)) {
                    parse_error(&state, "%s\n", ret_err.c_str());
                }
            }
            args.clear();
            break;
        case T_TEXT:
            //將本次解析的內(nèi)容寫入到args中
            args.emplace_back(state.text);
            break;
        }
    }
}

2.1.2 解析Service

system/core/init/service.cpp
從上面代碼可知解析init.rc時section_parser->ParseSection(args, &ret_err)來接解析敦跌,在之前為每種section都設(shè)置了解析函數(shù),service對應(yīng)的解析函數(shù)就service.cpp::ServiceParser::ParseSection(onst std::vector<std::string>& args, std::string* err)

bool ServiceParser::ParseSection(const std::vector<std::string>& args,std::string* err) {
    ...
    const std::string& name = args[1];
    ...
    //service section 對應(yīng)為"service ueventd /sbin/ueventd...",下面就是講name后的參數(shù)全部放在一個vector中
     std::vector<std::string> str_args(args.begin() + 2, args.end());
     //構(gòu)造一個service對象
    service_ = std::make_unique<Service>(name, "default", str_args);
    return true;
}

在解析將一個section解析結(jié)束后會調(diào)用ServiceParser::EndSection()

void ServiceParser::EndSection() {
    if (service_) {
        ServiceManager::GetInstance().AddService(std::move(service_));
    }
}
//
 void ServiceManager::AddService(std::unique_ptr<Service> service) {
    Service* old_service = FindServiceByName(service->name());
    if (old_service) {
        return;
    }
    //添加到services_中 (`ServicePaser::ServiceManager`中的一個vector)
    services_.emplace_back(std::move(service));
}

2.1.3 解析action

ActionParser定義于system/core/init/action.cpp中逛揩。Action的解析過程柠傍,其實與Service差不多

//解析 on <trigger>
bool ActionParser::ParseSection(const std::vector<std::string>& args, std::string* err) {
    std::vector<std::string> triggers(args.begin() + 1, args.end());
   ...
   auto action = std::make_unique<Action>(false);
   //根據(jù)參數(shù),填充action的trigger域
    if (!action->InitTriggers(triggers, err)) {
        return false;
    }
    action_ = std::move(action);
    return true;
}

//解析 <command>
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) {
   ...
   //找出action對應(yīng)的執(zhí)行函數(shù)
   auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
    ...
    AddCommand(function, args, filename, line);
    return true;
}

//構(gòu)造出command辩稽,加入到action對象的commands_中
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解析
void ActionParser::EndSection() {
    if (action_ && action_->NumCommands() > 0) {
        ActionManager::GetInstance().AddAction(std::move(action_));
    }
}

2.1.4 init控制service

啟動zygote
actionon late-init中會執(zhí)行 triger boot,on boot下有個class_start core 的commond惧笛,對應(yīng)的處理函數(shù)式Builtins::do_class_start(const std::vector<std::string>& args)
system/core/init/Builtins.cpp

static int do_class_start(const std::vector<std::string>& args) {
        /* Starting a class does not start services
         * which are explicitly disabled.  They must
         * be started individually.
         */
         //傳遞一個匿名函數(shù),在找到對應(yīng)的service后執(zhí)行StartIfNotDisabled()
    ServiceManager::GetInstance().ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
    return 0;
}

//init.${zygote}.rc中 zygote  class main
//以下在/system/core/init/service.cpp中
void ServiceManager::ForEachServiceInClass(const std::string& classname, void (*func)(Service* svc)) const {
    for (const auto& s : services_) {
        if (classname == s->classname()) {
            func(s.get());
        }
    }
}

bool Service::StartIfNotDisabled() {
    if (!(flags_ & SVC_DISABLED)) {
        return Start();
    } else {
        flags_ |= SVC_DISABLED_START;
    }
    return true;
}

bool Service::Start() {
...
//已經(jīng)在運行了逞泄,不處理
    if (flags_ & SVC_RUNNING) {
        return false;
    }
    ...
    //判斷可執(zhí)行文件是否存在
    //zygote對應(yīng)的可執(zhí)行文件是/system/bin/app_process
    if (stat(args_[0].c_str(), &sb) == -1) {
        flags_ |= SVC_DISABLED;
        return false;
    }
    ...
    //fork 子進程
    if (namespace_flags_) {
       ...
    } else {
        pid = fork();
    }
    if (pid == 0) {
    //pid為零患整,我們在子進程中
        //添加環(huán)境變量信息
        for (const auto& ei : envvars_) {
            add_environment(ei.name.c_str(), ei.value.c_str());
        }
        //創(chuàng)建sokcet
        CreateSockets(scon);
        //設(shè)置一些參數(shù),uid,gid,寫入文件等
        ...
        /*執(zhí)行/system/bin/app_process喷众,這樣就進入到app_process的main函數(shù)中了各谚。fork、execve這兩個函數(shù)都是Linux系統(tǒng)上常用的系統(tǒng)調(diào)用侮腹。*/
        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
          ...
        }
    }
    //父進程init的處理嘲碧,設(shè)置service信息,如啟動時間進程號父阻,以及狀態(tài)等
    ...
}

fork,execve拓展閱讀
linux c語言 fork() 和 exec 函數(shù)的簡介和用法
Linux下Fork與Exec使用

重啟zygote

2.1.5 注冊子進程信號處理器

signal_handler_init()次函數(shù)在解析init.rc前先被調(diào)用愈涩。
system/core/init/Signal_Handler.cpp

void signal_handler_init() {
    // Create a signalling mechanism for SIGCHLD.
    int s[2];
    //通過socketpair創(chuàng)建兩個socket望抽,分別負(fù)責(zé)讀寫
    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -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));
    //設(shè)置處理處理信號為"SIGCHLD"的消息的函數(shù)
    act.sa_handler = SIGCHLD_handler;
    act.sa_flags = SA_NOCLDSTOP;
    //信號注冊,將監(jiān)聽及對應(yīng)的信號處理注冊到內(nèi)核
    sigaction(SIGCHLD, &act, 0);
    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
    register_epoll_handler(signal_read_fd, handle_signal);
}

sigaction注冊到內(nèi)核履婉,監(jiān)聽SIGCHLD信號煤篙,交由SIGCHLD_handler處理,SIGCHLD_handler通過signal_wirte_fd寫入信息

//[system/core/init/Signal_Handler.cpp]
static void SIGCHLD_handler(int) {
    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) 
    ...
}

sinal_read_fdsignal_wirte_fd是一組毁腿,所以sinal_read_fd能接收到write的信息辑奈,在signal_handler_init的最后調(diào)用了register_epoll_handler(signal_read_fd, handle_signal)來注冊處理signal_read_fd的處理函數(shù),為handle_signal()

//[system/core/init/init.cpp]
void register_epoll_handler(int fd, void (*fn)()) {
    epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.ptr = reinterpret_cast<void*>(fn);
    //epoll_fd增加一個監(jiān)聽對象fd,fd上有數(shù)據(jù)到來時已烤,調(diào)用fn處理
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1)
    ...
}

//[system/core/init/Signal_handler.cpp]
static void handle_signal() {
    // Clear outstanding requests.
    char buf[32];
    read(signal_read_fd, buf, sizeof(buf));
    //調(diào)用ReapAnyOutstandingChildren做真正的子線程處理
    ServiceManager::GetInstance().ReapAnyOutstandingChildren();
}

20160807170210926.png
圖片來自-《Android7.0 init進程源碼分析》-ZhangJian的博客

//[system/core/init/service.cpp]
void ServiceManager::ReapAnyOutstandingChildren() {
    while (ReapOneProcess()) {
    }
}

bool ServiceManager::ReapOneProcess() {
    int status;
    //用waitpid函數(shù)獲取狀態(tài)發(fā)生變化的子進程pid
    //waitpid的標(biāo)記為WNOHANG鸠窗,即非阻塞,返回為正值就說明有進程掛掉了
    pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
    if (pid == 0) return false;
    else if (pid == -1) return false;
    ...
    //利用FindServiceByPid函數(shù)胯究,找到pid對應(yīng)的服務(wù)稍计。
    Service* svc = FindServiceByPid(pid);
    //調(diào)用Reap()處理,判斷service是否需要移除
    if (svc->Reap()) {
        waiting_for_exec = false;
        //移除服務(wù)
        RemoveService(*svc);
    }
    return true;
}

bool Service::Reap() {
    //清理未攜帶SVC_ONESHOT 或 攜帶了SVC_RESTART標(biāo)志的子進程     
    if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART){
        KillProcessGroup(SIGKILL);
    }
    // Remove any sockets we may have created.
    //移除service中創(chuàng)建的socket
    for (const auto& si : sockets_) {
        unlink(tmp.c_str());
    }
    ...
    pid_ = 0;
    flags_ &= (~SVC_RUNNING);
    // Oneshot processes go into the disabled state on exit,
    // except when manually restarted.
    //對于攜帶了SVC_ONESHOT并且未攜帶SVC_RESTART的service裕循,將這類服務(wù)的標(biāo)志置為SVC_DISABLED臣嚣,不再啟動
    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
        flags_ |= SVC_DISABLED;
    }
    // Disabled and reset processes do not get restarted automatically.
    if (flags_ & (SVC_DISABLED | SVC_RESET))  {
        NotifyStateChange("stopped");
        return false;
    }
    time_t now = gettime();
     //未攜帶SVC_RESTART的SVG_CRITICAL(重要的)服務(wù),在規(guī)定的間隔內(nèi)剥哑,crash字?jǐn)?shù)過多時硅则,會導(dǎo)致整機重啟;
    if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
        if (time_crashed_ + CRITICAL_CRASH_WINDOW >= now) {
            if (++nr_crashed_ > CRITICAL_CRASH_THRESHOLD) {
                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
                return false;
            }
        } else {
            time_crashed_ = now;
            nr_crashed_ = 1;
        }
    }
    //將待重啟service的標(biāo)志位置為SVC_RESTARTING(init進程將根據(jù)該標(biāo)志位株婴,重啟服務(wù))
    flags_ &= (~SVC_RESTART);
    flags_ |= SVC_RESTARTING;
    // Execute all onrestart commands for this service.
    //執(zhí)行在init.rc文件中service下面所有onrestart選項
    onrestart_.ExecuteAllCommands();
    NotifyStateChange("restarting");
    return false;
}

被標(biāo)記為SVC_RESTARTING的service將在init中的restart_processes()中重啟怎虫,所以zygote能在此被重啟
流程可簡化為:

20160807172447044.png
圖片來自-《Android7.0 init進程源碼分析》-ZhangJian的博客

2.1.6 總結(jié)

整體流程和《深入理解Android卷I》是一樣的,只是更好的面向?qū)ο笕ヌ幚?封裝更好督暂。還有就是I/O方式變了揪垄,epoll更加靈活,沒有描述符限制,更多可參考
IO多路復(fù)用之epoll總結(jié)
Linux IO模式及 select逻翁、poll饥努、epoll詳解

2.2 屬性服務(wù)

2.2.1 初始化

Init.cppmain函數(shù)中執(zhí)行了property_init()進行初始化

//[system/core/init/Property_service.cpp]
void property_init() {
    if (__system_property_area_init()) {
        exit(1);
    }
}

//[bionic/libc/inlcude/System_properties.cpp]
int __system_property_area_init()
{
    free_and_unmap_contexts();
    mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
    if (!initialize_properties()) {
        return -1;
    }
  ...
  //分配內(nèi)存
    if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
    ... 
     }
    initialized = true;
    return fsetxattr_failed ? -2 : 0;
}

//[bionic/libc/inlcude/System_properties.cpp]
static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) {
...
    if (access_rw) {
    //這里才是真正分配內(nèi)存的地方,大小為 [128 * 1024]
    //prop_area也改為了class
        __system_property_area__ = map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
    }
     ...
    return __system_property_area__;
}

在書中說將pa賦值__system_property_area__是為了完成內(nèi)存共享八回,這7.0這部分改動很大酷愧,有點看不明白,大致流程和卷一的流程是一樣的缠诅。網(wǎng)上這部分的資料也沒找到溶浴,還有就是property_init()是在!is_first_stage情況下執(zhí)行的,那么property_load_boot_defaults()第一次執(zhí)行加載的放在哪里?希望有前輩指教管引。
不過好像也不影響我們理解:創(chuàng)建一個文件句柄士败,分配一個匿名共享內(nèi)存區(qū)用于存放屬性.

2.2.2 啟動屬性服務(wù)

在執(zhí)行property_load_boot_defaults()Init.cpp::main中接著執(zhí)行了start_property_service()

//[system/core/init/Property_service.cpp]
void start_property_service() {
//創(chuàng)建socket
    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,0666, 0, 0, NULL);
   ...
    listen(property_set_fd, 8);
    //注冊處理函數(shù)
    register_epoll_handler(property_set_fd, handle_property_set_fd);
}

Init::main()在的最后又這么一段斷碼,通過取出注冊的處理函數(shù),然后處理消息谅将。

epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
    ...       
else if (nr == 1) {
    ((void (*)()) ev.data.ptr)();
}

所以響應(yīng)設(shè)置屬性的請求就在handle_property_set_fd()中執(zhí)行漾狼。

2.2.3 處理屬性設(shè)置

//[system/core/init/Property_service.cpp]
static void handle_property_set_fd()
{
    ...
    //接受連接
    int s = accept(property_set_fd, nullptr, nullptr);
    ...
    //取出客戶端進程的權(quán)限等
    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0)
    ...
    //接受請求數(shù)據(jù)
    r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
    switch(msg.cmd) {
    case PROP_MSG_SETPROP:
        msg.name[PROP_NAME_MAX-1] = 0;
        msg.value[PROP_VALUE_MAX-1] = 0;
        ...
        //如果是ctl開頭的消息,則認(rèn)為是控制消息饥臂,控制消息用來執(zhí)行一些命令逊躁,例如用adb shell登錄后,輸入setprop ctl.start bootanim就可以查看開機動畫了隅熙,關(guān)閉的話就輸入setpropctl.stop bootanim
        if(memcmp(msg.name,"ctl.",4) == 0) {
            // 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 {
        //檢查客戶端進程是否有足夠的權(quán)限
            if (check_mac_perms(msg.name, source_ctx, &cr)) {
                //設(shè)置屬性
                property_set((char*) msg.name, (char*) msg.value);
            }
            ...
        }
        ...
}

//設(shè)置
int property_set(const char* name, const char* value) {
    int rc = property_set_impl(name, value);
  ...
    return rc;
}

static int property_set_impl(const char* name, const char* value) {
    //更具不同的屬性名稱進行各種處理    
    ...
    //這個函數(shù)將觸發(fā) init.rc中的trigger 以執(zhí)行command
    /*
on property:persist.service.adb.enable=1
start adb
當(dāng)persist.service.adb.enable屬性置為1后稽煤,就會執(zhí)行start adbd這個command,這是通過property_changed函數(shù)來完成的    
    */
    property_changed(name, value);
    return 0;
}

2.2.4 客戶端發(fā)送設(shè)置請求

客戶端通過property_set發(fā)送請求囚戚,property_setlibcutils庫提供

//[system/core/libcutils/Properties.c]
int property_set(const char *key, const char *value)
{
    return __system_property_set(key, value);
}

//[bionic/libc/System_properties.cpp]
int __system_property_set(const char *key, const char *value)
{
    ...
    const int err = send_prop_msg(&msg);
    ...
}

//[bionic/libc/System_properties.cpp]
static int send_prop_msg(const prop_msg *msg){
    ...
    sockaddr_un addr;
    memset(&addr, 0, sizeof(addr));
    strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
    addr.sun_family = AF_LOCAL;
    socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
    //建立和屬性服務(wù)器的socket連接
    if (TEMP_FAILURE_RETRY(connect(fd, reinterpret_cast<sockaddr*>(&addr), alen)) < 0) {
        close(fd);
        return -1;
    }
    //通過socket發(fā)送出去
    const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0));
}

3 總結(jié)

總體來說流程和原書是一致的酵熙,只是在實現(xiàn)采用了面向?qū)ο蟪鄯唬ㄐ欧绞揭膊捎昧烁痈咝У姆绞铰痰辏尤肓薙ELinux的東西,對屬性初始化那一部分還有點迷惑庐橙。但是更具原書的流程走,也不是很費力借嗽。其中的一些細(xì)節(jié)态鳖,后面再去填坑。
感謝以下博文的幫助:
Android系統(tǒng)啟動-init篇
Android7.0 init進程源碼分析
Android的init進程啟動過程

上一篇 《第二章 - JNI》讀書筆記
上一篇 《第四章 - Zygote》讀書筆記

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恶导,一起剝皮案震驚了整個濱河市浆竭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惨寿,老刑警劉巖邦泄,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異裂垦,居然都是意外死亡顺囊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門蕉拢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來特碳,“玉大人,你說我怎么就攤上這事晕换∥缗遥” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵闸准,是天一觀的道長益愈。 經(jīng)常有香客問我,道長夷家,這世上最難降的妖魔是什么蒸其? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任敏释,我火速辦了婚禮,結(jié)果婚禮上枣接,老公的妹妹穿的比我還像新娘颂暇。我一直安慰自己,他們只是感情好但惶,可當(dāng)我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著县爬,像睡著了一般财喳。 火紅的嫁衣襯著肌膚如雪耳高。 梳的紋絲不亂的頭發(fā)上泌枪,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天碌燕,我揣著相機與錄音修壕,去河邊找鬼慈鸠。 笑死林束,一個胖子當(dāng)著我的面吹牛稽亏,可吹牛的內(nèi)容都是我干的截歉。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼锨阿,長吁一口氣:“原來是場噩夢啊……” “哼墅诡!你這毒婦竟也來了末早?” 一聲冷哼從身側(cè)響起然磷,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤姿搜,失蹤者是張志新(化名)和其女友劉穎舅柜,沒想到半個月后业踢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡太伊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年僚焦,在試婚紗的時候發(fā)現(xiàn)自己被綠了芳悲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片名扛。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡肮韧,死狀恐怖弄企,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情意乓,我是刑警寧澤届良,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布伙窃,位于F島的核電站,受9級特大地震影響放祟,放射性物質(zhì)發(fā)生泄漏跪妥。R本人自食惡果不足惜眉撵,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一纽疟、第九天 我趴在偏房一處隱蔽的房頂上張望散吵。 院中可真熱鬧蟆肆,春花似錦炎功、人聲如沸官紫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沉帮。三九已至穆壕,卻和暖如春喇勋,著一層夾襖步出監(jiān)牢的瞬間偎行,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工熄云, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人练般。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓薄料,卻偏偏與公主長得像,于是被迫代替她去往敵國和親虑稼。 傳聞我的和親對象是個殘疾皇子蛛倦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容