Android7.0 init进程分析

init进程
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv); //这里比较当前进程是不是ueventd,因为init本身也是ueventd进程,
//ueventd进程的工作是负责设备节点创建和权限设定
}

if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv); //watchdogd进程的工作是定时喂狗
}

// Clear the umask.
umask(0); //清楚mask,主要是为了后面创建文件解决权限问题

add_environment("PATH", _PATH_DEFPATH); //添加环境变量#define _PATH_DEFPATH "/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin"

bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);

//创建dev pts proc sys等目录,挂载分区
if (is_first_stage) {
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);
}

open_devnull_stdio(); // 打开/sys/fs/selinux/null 并检查输入流0 输出流1 错误流2是否正常
klog_init(); //打开/dev/kmsg
klog_set_level(KLOG_NOTICE_LEVEL); //设置log输出级别KLOG_NOTICE_LEVEL即5

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)); //创建文件/dev/.booting然后又关闭,主要是为了检查/dev/目录下是否有O_WRONLY | O_CREAT | O_CLOEXEC权限
property_init();

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();
}

selinux_initialize(is_first_stage); //初始化SELinux

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) {
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); //创建epoll监听池
if (epoll_fd == -1) {
ERROR("epoll_create1 failed: %s\n", strerror(errno));
exit(1);
}

signal_handler_init(); //设置子进程退出的信号处理函数,当子进程异常退出的时候,init进程会去捕获异常信息,当捕获到异常信息时候就会调用该函数设置的处理函数进行处理
property_load_boot_defaults(); //加载预置的property /default.prop,主要有两种:ADDITIONAL_DEFAULT_PROPERTIES、BOOTIMAGE_BUILD_PROPERTIES
export_oem_lock_status();
start_property_service(); //启动属性服务
#ifdef BOOT_TRACE
if (boot_trace) {
ERROR("enable boot systrace...");
property_set("debug.atrace.tags.enableflags", "0x3ffffe");
}
#endif

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>()); //添加解析类型动作on
parser.AddSectionParser("import", std::make_unique<ImportParser>()); //添加解析类型包括其他rc文件类型import
parser.ParseConfig("/init.rc"); //开始解析/init.rc

ActionManager& am = ActionManager::GetInstance();

am.QueueEventTrigger("early-init"); //将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"); //将函数添加进map中
// ... 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"); //将late-init块中的服务添加到触发队列中
}

//按照服务的添加顺序,先执行early-init块中定义的所有服务再执行init中的服务,最后执行charger或者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(); //执行子进程对应的命令即init.rc中解析出来的命令
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); //bootchart是性能分析工具用于分析性能,收集cpu占用率、进程等信息

epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout)); //epoll多IO监听方式等待,timeout设置为0则是非阻塞型等待
if (nr == -1) {
ERROR("epoll_wait failed: %s\n", strerror(errno));
} else if (nr == 1) {
((void (*)()) ev.data.ptr)(); / /调用register_epoll_handler中的函数handle_property_set_fd
}
}

return 0;
}

主要分析两方面:属性服务和启动service进程
init进程在启动过程中会启动服务并建立一块内存区域来存储这些属性,当需要读取这些属性的时候直接从这一块内存中读取,当需要修改这些属性的时候通过属性服务Socket来连接属性服务完成。
属性的值最长为92,属性命名只允许"." 、“-”、"_",[a-zA-Z]或者[0-9],不允许存在“..”

一. 属性服务
1. 启动属性服务

void start_property_service() {
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL); //监听socket,用于property修改
if (property_set_fd == -1) {
ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
exit(1);
}

listen(property_set_fd, 8); //监听socket连接,等待队列长度为8
register_epoll_handler(property_set_fd, handle_property_set_fd); //将fd添加到epoll中监听
}

2. 属性服务监听
while (true) {
...
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)(); / /调用register_epoll_handler中的函数handle_property_set_fd
}
}

3. 属性设置
static void handle_property_set_fd(){
if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { //accept连接
return;
}

r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT)); //接收socket消息
...
switch(msg.cmd) {
...
if (!is_legal_property_name(msg.name, strlen(msg.name))){ //属性命名合法性检查
}
if(memcmp(msg.name,"ctl.",4) == 0) {
handle_control_message((char*) msg.name + 4, (char*) msg.value); //控制属性,表示要start或者stop或者restart一个服务
...
}else {
property_set((char*) msg.name, (char*) msg.value); //普通属性,直接设置
}
...
}

close(s); //最后关闭socket
}

3.1 控制属性设置
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;
}
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());
}
}

3.2 普通属性设置
int property_set(const char* name, const char* value){
int rc = property_set_impl(name, value);
...
}

static int property_set_impl(const char* name, const char* value) {
if (!is_legal_property_name(name, namelen)) return -1; //命名合法性检查
if (valuelen >= PROP_VALUE_MAX) return -1; //长度合法性检查

prop_info* pi = (prop_info*) __system_property_find(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); //更新属性值
} else {
int rc = __system_property_add(name, namelen, value, valuelen); //直接添加新属性
if (rc < 0) {
return rc;
}
}

//网络属性解析为DNS
if (strncmp("net.", name, strlen("net.")) == 0) {
if (strcmp("net.change", name) == 0) {
return 0;
}
property_set("net.change", name);
} else if (persistent_properties_loaded &&
strncmp("persist.", name, strlen("persist.")) == 0) {
write_persistent_property(name, value);//永久性属性,直接写入ROM
}
property_changed(name, value);
}

二、init.rc文件解析
const BuiltinFunctionMap function_map; //关键字和函数映射表,主要为了解析时根据action名字找到对应的执行函数
Action::set_function_map(&function_map);
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();

将builtints.cpp中的函数表添加值action管理中
static const Map builtin_functions = {
{"bootchart_init", {0, 0, do_bootchart_init}},
{"chmod", {2, 2, do_chmod}},
{"chown", {2, 3, do_chown}},
{"class_reset", {1, 1, do_class_reset}},
{"class_start", {1, 1, do_class_start}},
{"class_stop", {1, 1, do_class_stop}},
{"copy", {2, 2, do_copy}},
{"domainname", {1, 1, do_domainname}},
{"enable", {1, 1, do_enable}},
{"exec", {1, kMax, do_exec}},
{"export", {2, 2, do_export}},
{"hostname", {1, 1, do_hostname}},
{"ifup", {1, 1, do_ifup}},
{"init_user0", {0, 0, do_init_user0}},
{"insmod", {1, kMax, do_insmod}},
{"installkey", {1, 1, do_installkey}},
{"load_persist_props", {0, 0, do_load_persist_props}},
{"load_system_props", {0, 0, do_load_system_props}},
{"loglevel", {1, 1, do_loglevel}},
{"mkdir", {1, 4, do_mkdir}},
{"mount_all", {1, kMax, do_mount_all}},
{"mount", {3, kMax, do_mount}},
{"powerctl", {1, 1, do_powerctl}},
{"restart", {1, 1, do_restart}},
{"restorecon", {1, kMax, do_restorecon}},
{"restorecon_recursive", {1, kMax, do_restorecon_recursive}},
{"rm", {1, 1, do_rm}},
{"rmdir", {1, 1, do_rmdir}},
{"setprop", {2, 2, do_setprop}},
{"setrlimit", {3, 3, do_setrlimit}},
{"start", {1, 1, do_start}},
{"stop", {1, 1, do_stop}},
{"swapon_all", {1, 1, do_swapon_all}},
{"symlink", {2, 2, do_symlink}},
{"sysclktz", {1, 1, do_sysclktz}},
{"trigger", {1, 1, do_trigger}},
{"verity_load_state", {0, 0, do_verity_load_state}},
{"verity_update_state", {0, 0, do_verity_update_state}},
{"wait", {1, 2, do_wait}},
{"write", {2, 2, do_write}},
};
return builtin_functions;
}
parser.AddSectionParser("service",std::make_unique<ServiceParser>()); //主要有两种类型:service和on,import是包含其他rc文件进来解析
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
parser.ParseConfig("/init.rc"); //解析入口文件是/init.rc


2.1 ParseConfig函数
bool Parser::ParseConfigFile(const std::string& path) {
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);
for (const auto& sp : section_parsers_) {
sp.second->EndFile(path);
}
...
return true;
}

2.2 ParseData函数
void Parser::ParseData(const std::string& filename, const std::string& data) {
std::vector<char> data_copy(data.begin(), data.end()); //拷贝一份文件数据(data),将string转char[],因为parse_state结构struct中ptr为char*
data_copy.push_back('\0'); //添加结束符

parse_state state;
state.filename = filename.c_str();
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;

SectionParser* section_parser = nullptr;
std::vector<std::string> args;

for (;;) {
switch (next_token(&state)) {
case T_EOF: //
if (section_parser) {
section_parser->EndSection();
}
return;
case T_NEWLINE: //一行结束
state.line++;
if (args.empty()) { //这一行什么参数都没有,说明是空行,直接跳过去解析下一行
break;
}
if (section_parsers_.count(args[0])) { //如果当前map中存在这样的key
if (section_parser) {
section_parser->EndSection();
}
section_parser = section_parsers_[args[0]].get(); //获取section类型service或者action section_parsers_是map,解析出来的on或者service作为key可以或者对应的解析类进行解析
std::string ret_err;
if (!section_parser->ParseSection(args, &ret_err)) { //具体命令解析
parse_error(&state, "%s\n", ret_err.c_str());
section_parser = nullptr;
}
} else if (section_parser) {
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(); //args只保留一行的参数,所以在开始下一行解析前清空args
break;
case T_TEXT:
args.emplace_back(state.text); //保存解析出来的参数
break;
}
}
}

具体参数解析函数
int next_token(struct parse_state *state)
{
char *x = state->ptr;
char *s;

if (state->nexttoken) {
int t = state->nexttoken;
state->nexttoken = 0;
return t;
}

for (;;) {
switch (*x) {
case 0: //解析结束
state->ptr = x;
return T_EOF;
case '\n': //换行
x++;
state->ptr = x;
return T_NEWLINE;
case ' ': //跳过空格
case '\t': //制表符
case '\r': //回车符
x++;
continue;
case '#': //注释行
while (*x && (*x != '\n')) x++; //跳过注释行
if (*x == '\n') {
state->ptr = x+1; //指向下一行的开始字符
return T_NEWLINE;
} else {
state->ptr = x; //文件最后一行是注释行的情况,读完这行就结束了
return T_EOF;
}
default:
goto text; //service或者on定义的服务或者动作的情况
}
}

textdone:
state->ptr = x; //当前解析指针指向下一个字符
*s = 0;
return T_TEXT; //解析完一个参数
text:
state->text = s = x; //保存该字符
textresume:
for (;;) {
switch (*x) {
case 0:
goto textdone; //文件解析结束
case ' ': //空格
case '\t': //制表符
case '\r': //回车
x++; //指向下一个字符
goto textdone;
case '\n':
state->nexttoken = T_NEWLINE; //一行解析完毕
x++; //指向下一行
goto textdone;
case '"': //双引号开始
x++;
for (;;) {
switch (*x) {
case 0: //解析结束
/* unterminated quoted thing */
state->ptr = x;
return T_EOF;
case '"': //双引号结束
x++;
goto textresume;
default:
*s++ = *x++; //指向引号中的下一个字符
}
}
break;
case '\\': //\
x++;
switch (*x) {
case 0: //解析结束
goto textdone;
case 'n':
*s++ = '\n';
break;
case 'r':
*s++ = '\r';
break;
case 't':
*s++ = '\t';
break;
case '\\':
*s++ = '\\';
break;
case '\r':
/* \ <cr> <lf> -> line continuation */
if (x[1] != '\n') {
x++;
continue;
}
case '\n':
/* \ <lf> -> line continuation */
state->line++;
x++;
/* eat any extra whitespace */
while((*x == ' ') || (*x == '\t')) x++;
continue;
default:
/* unknown escape -- just copy */
*s++ = *x++;
}
continue;
default:
*s++ = *x++;
}
}
return T_EOF; //解析完毕
}

最后所有解析出来的action存放在ActionManager中的actions_
解析出来的service存放在ServiceManager中的service_

几个核心类

服务 service
class Service {
...
std::string name_; //服务的名字
std::string classname_; //服务类别 core main
Action onrestart_; //重启服务action
std::vector<std::string> args_; //服务参数
....
}

class ServiceManager {
...
std::vector<std::unique_ptr<Service>> services_; / /存储所有服务
}

class ServiceParser : public SectionParser {
...
std::unique_ptr<Service> service_; //解析出来的所有服务
}

动作 on
class Command {
...
BuiltinFunction func_; //对应的执行函数
std::vector<std::string> args_; //解析出来的参数
std::string filename_; //command所在rc文件名
int line_; //具体命令行数
};

class Action {
....
std::map<std::string, std::string> property_triggers_; //触发属性如userdebug版本触发等
std::string event_trigger_; //事件触发,如boot完成等
std::vector<Command> commands_; //一个on块可能对应多个command
bool oneshot_; //被销毁后是否自动重启
static const KeywordMap<BuiltinFunction>* function_map_; //触发函数映射表

}

class ActionManager {
...
std::vector<std::unique_ptr<Action>> actions_; //所有action
std::queue<std::unique_ptr<Trigger>> trigger_queue_; //触发队列
std::queue<const Action*> current_executing_actions_; //当前正在执行的action列表
std::size_t current_command_; //当前正在执行的command数
}

class ActionParser : public SectionParser {
...
std::unique_ptr<Action> action_; //解析出来的action链表
}

三、 服务管理
3.1 启动服务
在init.rc中
start logd
class_start main
class_start core
..
class_start 对应的服务函数是do_class_start
static int do_class_start(const std::vector<std::string>& args) {
ServiceManager::GetInstance().
ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
return 0;
}

在services_所有服务中查找classname的服务,如果找到则调用函数 s->StartIfNotDisabled()
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)) { //如果服务没有被disable掉则启动服务
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return true;
}

bool Service::Start() {
...
pid_t pid = fork(); //创建子进程执行服务,执行服务就是init.rc定义服务中对应的可执行文件
if (pid == 0) {

...
//查找并启动socket连接
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);
}
}

...
if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) { //执行命令启动服务
ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
}
}

NotifyStateChange("running"); //父进程设置该服务状态为running
...
}


服务或者动作的执行


所有服务或者动作的执行都是在init.cpp中的while中am.ExecuteOneCommand()

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_) { //从action队列中
if (trigger_queue_.front()->CheckTriggers(*action)) { //如果action的触发类型属于当前触发类型如init,则将该action添加到执行队列中
current_executing_actions_.emplace(action.get());
}
} //经过for循环后,所有为当前触发类型(如init)的action被添加到触发队列中
trigger_queue_.pop(); //将触发类型从触发队列中删除
}

if (current_executing_actions_.empty()) { / /如果执行队列为空说明action为空或者触发队列为空,action队列一般不会为空,也就是说只有触发队列为空的情况(不可能存在定义某种触发类型一个action都没有的情况)
return;
}

auto action = current_executing_actions_.front(); //每次只取一个action

if (current_command_ == 0) {
std::string trigger_name = action->BuildTriggersString();
INFO("processing action (%s)\n", trigger_name.c_str());
}

action->ExecuteOneCommand(current_command_); //执行action,一个action块里面可能包含有多条命令

// 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块中的所有命令
current_executing_actions_.pop(); //将这个action从执行队列中移除
current_command_ = 0; //重置计数值
if (action->oneshot()) { //如果action只执行一次,就是说被销毁了不需要重启的情况
auto eraser = [&action] (std::unique_ptr<Action>& a) { //在action队列中查找到该action
return a.get() == action;
};
actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser)); //将action从action队列中删除
}
}
}

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

void Action::ExecuteCommand(const Command& command) const {
Timer t;
int result = command.InvokeFunc(); //调用map中映射的函数解析command行为,比如start --》 do_start()
...
}

在action执行中启动服务
./init.rc:550: class_start core
./init.rc:555: class_start main
./init.rc:556: class_start late_start
./init.rc:562: class_start charger
./init.rc:578: class_start main
./init.rc:583: class_start main
./init.rc:584: class_start late_start

class_start 对应do_class_start()函数
static int do_class_start(const std::vector<std::string>& args) {
ServiceManager::GetInstance().
ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); }); //如果服务被disable掉这里是不会被启动的,参数是启动的服务类别
return 0;
}

void ServiceManager::ForEachServiceInClass(const std::string& classname,
void (*func)(Service* svc)) const {
for (const auto& s : services_) {
if (classname == s->classname()) { //在解析出来的所有服务中寻找为classname这种类别的服务,只要符合就调用StartIfNotDisabled函数
func(s.get());
}
}
}

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


分析到这里,我们知道了init进程是怎样解析init.rc文件的,解析出来的动作和服务是怎样存储起来的,已经怎样执行action定义的command和怎样启动service的。









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值