从源码角度看Android系统init进程启动过程
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了从源码角度看Android系统init进程启动过程,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含23681字,纯文字阅读大概需要34分钟。
内容图文
![从源码角度看Android系统init进程启动过程](/upload/InfoBanner/zyjiaocheng/974/0d8dfb25d2b64e26aa6f05844a6adf5d.jpg)
init进程是Linux系统中用户空间的第一个进程,进程号为1。Kernel启动后,在用户空间启动init进程,并调用/system/core/init.cpp中的main方法执行一些重要的工作。
备注:本文将结合Android8.0的源码看init进程的启动过程以及init进程做了哪些重要工作。
1. init进程启动前系统的启动流程
在引入init进程前,我们需要大致了解系统是如何走到init进程的。大致步骤如下:
-
启动电源和系统启动
按下电源,让设备开机时引导芯片代码会从预定义的地方开始执行。加载引导程序BootLoader到RAM中。
-
BootLoader
BootLoader只是Android系统开始前的一个引导程序,主要作用是将OS系统拉起来并运行。
-
启动Linux内核
当内核启动时,会先去设置缓存、加载驱动等,在系统设置完成后,会在系统文件中寻找init.rc文件,并启动init进程。
-
启动init进程
经过前面的步骤,到这一步,init就被正式启动。
init进程启动后,下面就看下它里面的一些重要的职责工作。
2. init进程main函数
main函数是init进程的入口函数,代码位置在:
/system/core/init.cpp
深入到init.cpp的main函数中:
int main(int argc, char** argv) {
...省略...
if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
// Clear the umask.
umask(0);
//创建和挂载启动所需的文件目录
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));
// Don't expose the raw commandline to unprivileged processes.
chmod("/proc/cmdline", 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
//初始化Kernel的Log
InitKernelLogging(argv);
...省略...
}
...省略...
//创建一块共享的内存空间,用于对属性服务进行初始化
property_init();
...省略...
//创建epoll句柄
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
PLOG(ERROR) << "epoll_create1 failed";
exit(1);
}
//初始化子进程退出的信号处理函数
//如果子进程异常退出,init进程会调用该函数中设定的信号处理函数来进程处理
//防止子进程成为僵尸进程
signal_handler_init();
//加载default.prop文件,导入默认的环境变量
property_load_boot_defaults();
export_oem_lock_status();
//启动属性服务器,会调用epoll_ctl设置property fd可读的回调函数
start_property_service();
...省略...
if (bootscript.empty()) {
//解析init.rc文件
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
} else {
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(true);
parser.set_is_vendor_etc_init_loaded(true);
parser.set_is_odm_etc_init_loaded(true);
}
...省略...
//不要在充电器模式下挂载文件系统或启动核心系统服务
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
//基于属性的当前状态运行所有属性触发器
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
//重启需要重启的服务
restart_processes();
// If there's a process that needs restarting, wake up in time for that.
if (process_needs_restart_at != 0) {
epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
}
epoll_event ev;
//循环等待事件发生
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
init进程执行完成后,就进入循环等待epoll_wait的状态。init的main函数中做了大量初始化工,比较复杂。我主要学习了如下几个关键点:
-
创建和挂载启动的文件
-
属性服务
-
进程信号处理
-
解析init.rc文件
-
init启动Zygote
3. 创建和挂载启动的文件
在最开始的时候就创建和挂载启动所需要的文件目录,其中挂载了:
-
tempfs
-
devpts
-
proc
-
sysfs
-
selinuxfs
共挂载了这五个系统,这些都是系统运行时的目录,只有当系统运行时才会存在,当系统停止时,这些目录就不会存在。
4. 属性服务
在Windows上有一个注册表管理器,主要采用的是键值对的形式来记录用户、软件的一些信息。即使系统或软件重启,还是能根据之前注册表中的记录,进行初始化工作。在Android系统中,也有一个类似的机制,叫作属性服务。
当某个进程通过property_set()方法修改属性后,init进程会先检查权限,当权限验证通过后,就会去修改相应的属性值,而属性值一旦改变,就会触发相应的触发器(即rc文件中的on开头的语句),在Android Shared Memmory(共享内存区)中有一个_system_property_area_区域,里面存储这所有的属性值。某个进程通过property_get()方法,获取的也是这个共享内存区域的属性值。
在init.cpp的main函数中与属性服务相关的代码有如下两行:
路径:system/core/init/init.cpp
property_init();
start_property_service();
这两行代码主要是初始化属性服务配置并启动属性服务。
4.1 初始化共享内存并启动属性服务
4.1.1 property_init
深入到/system/core/init/property_service.cpp中:
void property_init() {
if (__system_property_area_init()) {
LOG(ERROR) << "Failed to initialize property area";
exit(1);
}
}
这个方法的主要核心是执行__system_property_area_init()方法,初始化跨进程的共享内存。
4.1.2 start_property_service
深入到/system/core/init/property_service.cpp中:
void start_property_service() {
property_set("ro.property_service.version", "2");
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);//注释1
if (property_set_fd == -1) {
PLOG(ERROR) << "start_property_service socket creation failed";
exit(1);
}
listen(property_set_fd, 8);//注释2
register_epoll_handler(property_set_fd, handle_property_set_fd);//注释3
}
注释解析说明:
注释1:创建名为“property_service”的Socket。
注释2:调用listen函数对property_set_fd进行监听,这里的Socket就成为了server,即属性服务。其中,参数8表示可以同时为8个试图设置属性的用户提供服务。
注释3:将property_set_fd放入到epoll中,用epoll来监听property_set_fd,当property_set_fd中有数据变动时,init进程将调用handle_property_set_fd函数进行处理。
其中,epoll是Linux内核为处理大批量文件描述符而做了改进的poll,能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
4.2 服务端请求属性服务处理数据
上面说了当客户端请求属性服务处理数据时,会调用handle_property_set_fd函数进行处理。
4.2.1 handle_property_set_fd
深入到/system/core/init/property_service.cpp的handle_property_set_fd方法中:
static void handle_property_set_fd() {
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
...省略...
uint32_t timeout_ms = kDefaultSocketTimeout;//设置2秒超时
uint32_t cmd = 0;
if (!socket.RecvUint32(&cmd, &timeout_ms)) {
PLOG(ERROR) << "sys_prop: error while reading command from the socket";
socket.SendUint32(PROP_ERROR_READ_CMD);
return;
}
switch (cmd) {
case PROP_MSG_SETPROP: {
char prop_name[PROP_NAME_MAX];
char prop_value[PROP_VALUE_MAX];
//如果2秒内Socket没有读取到数据则直接返回
if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
!socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
return;
}
prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;
//调用handle_property_set方法去处理数据
handle_property_set(socket, prop_value, prop_value, true);
break;
}
...省略...
}
}
从上面可以看到Android8.0继续封装了handle_property_set方法去处理数据。
4.2.2 handle_property_set
深入到/system/core/init/property_service.cpp的handle_property_set方法中:
static void handle_property_set(SocketConnection& socket,
const std::string& name,
const std::string& value,
bool legacy_protocol) {
const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";
if (!is_legal_property_name(name)) {//检查属性名是否合法,不合法直接return
LOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name \"" << name << "\"";
socket.SendUint32(PROP_ERROR_INVALID_NAME);
return;
}
struct ucred cr = socket.cred();
char* source_ctx = nullptr;
getpeercon(socket.socket(), &source_ctx);
//如果属性名称以“ctl.”开头,则说明是控制属性
if (android::base::StartsWith(name, "ctl.")) {
//检查权限
if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
//如果满足权限,则直接设置以ctl.开头的属性
handle_control_message(name.c_str() + 4, value.c_str());
if (!legacy_protocol) {
socket.SendUint32(PROP_SUCCESS);
}
} else {
LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
<< " service ctl [" << value << "]"
<< " uid:" << cr.uid
<< " gid:" << cr.gid
<< " pid:" << cr.pid;
if (!legacy_protocol) {
socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
}
}
} else {//不是以“ctl.”开头,则说明是普通属性
if (check_mac_perms(name, source_ctx, &cr)) {
//调用property_set方法来设置普通属性
uint32_t result = property_set(name, value);
if (!legacy_protocol) {
socket.SendUint32(result);
}
} else {
LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
if (!legacy_protocol) {
socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
}
}
}
freecon(source_ctx);
}
综上所述,会先检查属性名是否合法,在满足合法的条件后;判断属性名是否是以“ctl.”开头;如果是,则通过handle_control_message去设值;如果不是,则通过调用property_set方法来设值。
4.2.3 property_set
深入到/system/core/init/property_service.cpp的property_set方法中:
uint32_t property_set(const std::string& name, const std::string& value) {
size_t valuelen = value.size();
//判断属性名是否合法
if (!is_legal_property_name(name)) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
return PROP_ERROR_INVALID_NAME;
}
//属性名不能过长
if (valuelen >= PROP_VALUE_MAX) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "value too long";
return PROP_ERROR_INVALID_VALUE;
}
if (name == "selinux.restorecon_recursive" && valuelen > 0) {
if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
LOG(ERROR) << "Failed to restorecon_recursive " << value;
}
}
//从属性存储空间中查找该属性
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
//属性如果存在
if (pi != nullptr) {
// 如果属性名是以“.ro”开头,表示只读,不能修改,直接返回
if (android::base::StartsWith(name, "ro.")) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "property already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
}
// 如果属性存在,则就去更新
__system_property_update(pi, value.c_str(), valuelen);
} else {
//属性如果不存在,则添加该属性
int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "__system_property_add failed";
return PROP_ERROR_SET_FAILED;
}
}
// 处理以“persist.”开头的属性名
if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
write_persistent_property(name.c_str(), value.c_str());
}
property_changed(name, value);
return PROP_SUCCESS;
}
综上所述,property_set主要是处理普通属性。先判断属性名是否合法,属性名长度是否过长?在满足一系列条件判断后,再到存储空间中去查找是否有该属性;如果有,且是以“.ro”开头的,表示只读,不能修改,直接return,如果有,但不是以“.ro”开头,则去update;如果存储空间中没有该属性,则添加该属性,并赋值。最后还调用了write_persistent_property方法处理了以“persist.”开头的属性名。需要注意的是persist用于持久保存某些属性值,但会带来额外的IO操作。
如果是以“ctl.”开头,则表示控制消息,控制消息主要用来执行一些命令,例如:
-
查看开机动画(setprop ctl.start bootanim)
-
关闭开机动画(setprop ctl.stop bootanim)
-
进入recovery模式(setprop ctl.start pre-recovery)
5. 进程信号处理
在/system/core/init/init.cpp的main方法中,通过调用signal_handler_init方法来初始化信号处理进程。
5.1 signal_handler_init
深入到/system/core/init/signal_handler.cpp的signal_handler_init方法中:
void signal_handler_init() {
// 创建socket pair
int s[2];
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
PLOG(ERROR) << "socketpair failed";
exit(1);
}
signal_write_fd = s[0];
signal_read_fd = s[1];
// 当捕获到信号SIGCHLD,则写入signal_write_fd
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = SIGCHLD_handler;
//SA_NOCLDSTOP使init进程只有在其子进程终止时才会收到SIGCHLD信号
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, 0);
//进入waitpid来处理子进程是否退出的情况
ServiceManager::GetInstance().ReapAnyOutstandingChildren();//详情见5.2
//调用epoll_ctl方法来注册epoll的回调函数
register_epoll_handler(signal_read_fd, handle_signal);//详情见5.3
}
init进程是所有用户空间进程的父进程,当子进程终止时会产生SIGCHLD信号,此时init进程会调用信号安装函数sigaction,然后将SIGCHLD作为参数传递给sigaction结构体,从而完成信号处理的过程。
其中,SIGCHLD_handler是写入数据的函数:
static void SIGCHLD_handler(int) {
//向signal_write_fd写入1,直到成功为止
if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
PLOG(ERROR) << "write(signal_write_fd) failed";
}
}
handle_signal是读取数据的函数:
static void handle_signal() {
// Clear outstanding requests.
char buf[32];
//读取signal_read_fd中的数据,并放入buf
read(signal_read_fd, buf, sizeof(buf));
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
}
5.2 ServiceManager::GetInstance().ReapAnyOutstandingChildren
下面来看看处理子进程是否退出的流程:
深入到/system/core/init/service.cpp的ServiceManager::ReapAnyOutstandingChildren中:
void ServiceManager::ReapAnyOutstandingChildren() {
while (ReapOneProcess()) {
}
}
继续深入到ReapOneProcess方法中:
bool ServiceManager::ReapOneProcess() {
int status;
//等待任意子进程,如果子进程没有退出则返回0,否则则返回该子进程pid
pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
if (pid == 0) {
return false;
} else if (pid == -1) {
PLOG(ERROR) << "waitpid failed";
return false;
}
//根据pid查找到相应的service
Service* svc = FindServiceByPid(pid);
std::string name;
std::string wait_string;
if (svc) {
name = android::base::StringPrintf("Service '%s' (pid %d)",
svc->name().c_str(), pid);
if (svc->flags() & SVC_EXEC) {
wait_string =
android::base::StringPrintf(" waiting took %f seconds", exec_waiter_->duration_s());
}
} else {
name = android::base::StringPrintf("Untracked pid %d", pid);
}
if (WIFEXITED(status)) {
LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
} else if (WIFSIGNALED(status)) {
LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
} else if (WIFSTOPPED(status)) {
LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status) << wait_string;
} else {
LOG(INFO) << name << " state changed" << wait_string;
}
if (!svc) {
return true;
}
svc->Reap();
//当flags为EXEC时,重置相应的服务
if (svc->flags() & SVC_EXEC) {
exec_waiter_.reset();
}
//当flags为EXEC时,释放相应的服务
if (svc->flags() & SVC_TEMPORARY) {
RemoveService(*svc);
}
return true;
}
5.3 register_epoll_handler
register_epoll_handler是用来注册epoll的回调函数
深入到/system/core/init/init.cpp的register_epoll_handler方法中:
void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = reinterpret_cast<void*>(fn);
//将fd的可读事件加入到epoll_fd的监听队列中
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
PLOG(ERROR) << "epoll_ctl failed";
}
}
当fd为可读的时候,则会触发调用(*fn)函数。
5.4 要点总结:
5.4.1 signal_handler_init的作用
主要是防止init进程的子进程成为僵尸进程,系统会在子进程暂停或停止的时候发出SIGCHLD信号,而signal_handler_init方法会对系统发出的SIGCHLD信号进行处理,这样就防止了僵尸进程的出现。
5.4.2 什么是僵尸进程?
在Linux中,父进程是通过fork的方式创建子进程,在子进程终止的时候,如果父进程不知道子进程已经停止,那么此时就算子进程退出,在系统进程表中还会保留一些子进程的信息(如进程号、运行时间、状态等),那么这个子进程就成为了所谓的僵尸进程。但系统进程表中的资源空间是有限的,如果被僵尸进程耗尽,系统就无法创建新的进程。
6. 解析init.rc文件
init.rc是由Android初始化语言(Android Init Language)编写的脚本。rc文件的语法是以行尾为单位,以空格间隔,用#开始代表注释行。rc文件主要包含了5中类型的语句,分别是:Action、Command、Option、Service和Import。init.rc文件的目录在system/core/rootdir/init.rc中。
6.1 Action
Action类型的语句是通过触发器触发,格式如下:
on <trigger> [&& <trigger>] //设置触发器
<command>
<command> //动作触发后要执行的命令
即以on开头的语句来决定执行相应service的时机,具体时机有如下几个:
-
on early-init 在初始化早期触发
-
on init 在初始化时触发
-
on late-init 在初始化晚期触发
-
on boot/charger 当系统启动或充电时触发
-
on property:= 当属性值满足条件时触发
6.2 Command
列举一些常用的命令:
-
loglevel : 设置log级别
-
start : 启动指定的服务,如果已经启动则跳过
-
stop : 停止正在运行的服务
-
export : 设定环境变量
-
setprop : 设置属性值
-
write
: 向文件path中写入字符串
6.3 Service
以service开头的是由init进程启动,一般都是运行在init的某个子进程中。
service类型的语句,格式如下:
service <name> <pathname> [<argument>]* //<service的名><执行程序的路径><传递参数>
<option> //option是service的修饰词,影响什么时候、如何启动service
<option>
注意:在Android8.0对init.rc文件进行拆分,每个服务对应一个rc文件。
这里以64位处理器为例,Zygote启动脚本则在init.zygote64.rc中定义。
init.zygote64.rc的代码如下:(目录:system/core/rootdir/init.zygote64.rc)
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
根据上面的Service类型语句格式大致解析下:
-
service用于通知init进程创建名为zygote进程
-
执行程序的路径是/system/bin/app_process64
-
传递的参数是/system/bin --zygote --start-system-server
-
class main 是指Zygote的classname为main
init.rc中Action、Service语句都有相应的XXXParser类来解析。即Action类型会有ActionParser来进行解析,Service类型会有ServiceParser来解析。
因为后面分析Zygote,所以主要看下ServiceParser,ServiceParser的代码目录是:
system/core/init/service.cpp
深入到service.cpp中查看:
bool ServiceParser::ParseSection(const std::vector<std::string>& args,
std::string* err) {
if (args.size() < 3) { //判断service是否有name与可执行程序
*err = "services must have a name and a program";
return false;
}
const std::string& name = args[1];
if (!IsValidName(name)) { //检查service的name是否有效
*err = StringPrintf("invalid service name '%s'", name.c_str());
return false;
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, str_args);//注释1
return true;
}
bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
const std::string& filename, int line,
std::string* err) const {
return service_ ? service_->ParseLine(args, err) : false;//注释2
}
void ServiceParser::EndSection() {
if (service_) {
ServiceManager::GetInstance().AddService(std::move(service_));//注释3
}
}
void ServiceManager::AddService(std::unique_ptr<Service> service) {
Service* old_service = FindServiceByName(service->name());
if (old_service) {
LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
return;
}
services_.emplace_back(std::move(service));
}
注释说明:
-
注释1:调用ParseSection方法,根据传入的参数创建出一个Service参数
-
注释2:调用ParseLineSection方法解析出Service语句中的每一个子项,并将内容添加到Service对象中
-
注释3:解析完所有数据后,调用EndSection函数,内部调用ServiceManager中的AddService函数,将Service对象加入vector类型的Service链表中。
6.4 Option
option是配合service使用,是Service中的可选项。
* socket : 创建名为/dev/socket/<name>的socket
* user : 设置执行服务的用户,默认是root
* group : 设置执行服务的用户组,默认是root
* onrestart : 当服务重启时执行相应的命令
* oneshot : service退出后不再重启
* disabled : 不随class自动启动,只有根据service名才启动
7. init启动Zygote
这里我们同样以64位处理器为例,由前面可知,Zygote启动脚本在system/core/rootdir/init.zygote64.rc中:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
从上面可知zygote的classname是main
在init.rc中有如下两行配置:
目录:system/core/rootdir/init.rc
on nonencrypted
class_start main //注释1
class_start late_start
其中,class_start是一个COMMAND,对应的函数是do_class_start。注释1处表明启动那些classname为main的service,而zygote的classname是main,所以这里是用来启动Zygote的。
深入到do_class_start函数中:
目录:system/core/init/builtins.cpp
static int do_class_start(const std::vector<std::string>& args) {
ServiceManager::GetInstance().
ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
return 0;
}
do_class_start()函数中会遍历前面的Vector类型的Service链表,找到classname为main的Zygote,并调用system/core/init/service.cpp中的StartIfNotDisabled()函数:
bool Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) { //注释1
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return true;
}
在注释1处,如果Service中没有在其对应的rc文件中设置disabled选项,则会调用Start函数启动该Service,因为Zygote对应的init.zygote64.rc中没有设置disable选项,所以直接调用Start函数启动该Service
bool Service::Start() {
// 如果Service已经运行,则直接return
if (flags_ & SVC_RUNNING) {
return false;
}
...省略...
// 判断需要启动的Service的对应的执行文件是否存在,不存在则不启动该Service
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
flags_ |= SVC_DISABLED;
return false;
}
...省略...
// 如果子进程没有启动,则调用fork函数创建子进程,并返回pid的值
pid_t pid = -1;
if (namespace_flags_) {
pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork();
}
// 如果pid=0,则表示当前代码逻辑在子进程中运行
if (pid == 0) {
umask(077);
...省略...
//调用execve函数,启动Service子进程
if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) { //注释1
PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
}
_exit(127);
}
...省略...
NotifyStateChange("running");
return true;
}
综上所述:
-
首先判断Service是否已经启动,如果运行了,则直接返回。
-
如果子进程没有启动,就调用fork函数创建子进程,并返回pid的值
-
如果返回pid的值是0,则表示当前代码逻辑在子进程中运行
-
注释1处在子进程中调用execve函数,Service子进程就会被启动,并进入到该Service的main函数中。
如果该Service是Zygote,从前面的system/core/rootdir/init.zygote64.rc文件中我们可以知道Zygote执行程序的路径是/system/bin/app_process64,对应的文件是app_main.cpp,这样就会进入app_main.cpp的main函数中,即Zygote的main函数中:
代码路径:frameworks/base/cmds/app_process/app_main.cpp
main函数如下:
int main(int argc, char* const argv[])
{
...省略...
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote); //注释1
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}
在注释1处调用runtime的start函数启动Zygote,到此Zygote进程就启动了。
![从源码角度看Android系统init进程启动过程 - 文章图片](/upload/getfiles/0001/2021/5/13/20210513041708610.jpg)
8. init进程启动总结
init进程是Linux系统中用户空间的第一个进程,主要做了如下工作:
-
创建和挂载启动所需要的文件目录
-
创建一块共享内存,初始化和启动属性服务
-
解析各个rc文件,启动相应属性服务进程(如:Zygote)
-
初始化epoll,依次设置signal、property、keychord这三个fd可读时相对应的回调函数
-
子进程信号处理,防止系统中出现僵尸进程
非常感谢您的耐心阅读,希望我的文章对您有帮助。欢迎点评、转发或分享给您的朋友或技术群。
内容总结
以上是互联网集市为您收集整理的从源码角度看Android系统init进程启动过程全部内容,希望文章能够帮你解决从源码角度看Android系统init进程启动过程所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。