前言
在上一篇文章 Android体系架构 中,咱们说能够从线程和进程的角度来动态了解Android的体系分层,仍是给出经典图:
在这张图中,咱们知道Loader层和Kernel层暂时不是咱们要研究的要点,由于它们是Linux内核层,而在Android体系用户态中,会先发动native层即C++代码部分,而这部分的作业就由init进程所负责。
作为Android体系用户态的第一个进程,它是Android体系发动流程中的一个关键步骤,被赋予了许多及其重要的作业责任,比方创立Zygote进程、创立特点服务、监听子进程中止等等。
本文源码:aospxref.com/android-8.1…
正文
话不多说,咱们就来剖析剖析该进程的作业流程。
开端init进程
init进程的进程号固定为1,经过如下今个步骤引进init进程:
- 发动电源以及体系发动:当电源键按下时从固化在ROM中的芯片代码开端履行,加载引导程序BootLoader到RAM中,然后开端履行。
- 引导程序BootLoader:BootLoader是Android体系开端运转前的一个程序,用来拉起体系OS。
- Linux内核发动:当内核发动时,会设置缓存、加载驱动等,当内核加载好后,会发动init进程。
- init进程发动:用户空间第一个进程发动,该进程所做的业务比较多,后边具体剖析。
init进程的进口函数
init进程开端的地方便是有个main进口函数,关于init进程的效果,咱们直接来剖析该函数即可,函数比较长,首要代码如下:
[init.cpp](http://aospxref.com/android-8.1.0_r81/xref/system/core/init/init.cpp)
int main(int argc, char** argv) {
//第一次履行,冷发动,用来办理注册的设备
992 if (!strcmp(basename(argv[0]), "ueventd")) { //注释<1>
993 return ueventd_main(argc, argv);
994 }
995 //第二次运转,敞开看门狗程序
996 if (!strcmp(basename(argv[0]), "watchdogd")) { //注释<2>
997 return watchdogd_main(argc, argv);
998 }
999
1000 if (REBOOT_BOOTLOADER_ON_PANIC) {
1001 InstallRebootSignalHandlers();
1002 }
1003
1004 add_environment("PATH", _PATH_DEFPATH);
1005
1006 bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
1007 //第三次运转
1008 if (is_first_stage) { //注释<3>
1009 boot_clock::time_point start_time = boot_clock::now();
1010
1011 // Clear the umask.
1012 umask(0);
1013
1014 //创立和挂载发动所需求的文件目录
1016 mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
1017 mkdir("/dev/pts", 0755);
1018 mkdir("/dev/socket", 0755);
1019 mount("devpts", "/dev/pts", "devpts", 0, NULL);
1020 #define MAKE_STR(x) __STRING(x)
1021 mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
1023 chmod("/proc/cmdline", 0440);
1024 gid_t groups[] = { AID_READPROC };
1025 setgroups(arraysize(groups), groups);
1026 mount("sysfs", "/sys", "sysfs", 0, NULL);
1027 mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
1028 mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
1029 mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
1030 mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
1031
1032 //初始化Kernel的Log
1034 InitKernelLogging(argv);
...
1069 }
1070 //第四次运转
...
1082 //初始化特点服务 见俩除注释4注释
1083 property_init(); //注释<4>
1084 ...
1111 //创立epoll句柄
1112 epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1113 if (epoll_fd == -1) {
1114 PLOG(ERROR) << "epoll_create1 failed";
1115 exit(1);
1116 }
1117 //用于设置子进程信号处理函数 //注释<5>
1118 signal_handler_init();
1119 //导入默许的环境变量
1120 property_load_boot_defaults();
1121 export_oem_lock_status();
//发动特点服务
1122 start_property_service(); //注释<4>
...
1136 if (bootscript.empty()) {
//解析init.rc装备文件 //注释<6>
1137 parser.ParseConfig("/init.rc");
1138 parser.set_is_system_etc_init_loaded(
1139 parser.ParseConfig("/system/etc/init"));
1140 parser.set_is_vendor_etc_init_loaded(
1141 parser.ParseConfig("/vendor/etc/init"));
1142 parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
1143 } else {
1144 parser.ParseConfig(bootscript);
1145 parser.set_is_system_etc_init_loaded(true);
1146 parser.set_is_vendor_etc_init_loaded(true);
1147 parser.set_is_odm_etc_init_loaded(true);
1148 }
1149
1150 ...
1183 while (true) {
1184 // By default, sleep until something happens.
1185 int epoll_timeout_ms = -1;
1186
1187 if (do_shutdown && !shutting_down) {
1188 do_shutdown = false;
1189 if (HandlePowerctlMessage(shutdown_command)) {
1190 shutting_down = true;
1191 }
1192 }
1193
1194 if (!(waiting_for_prop || sm.IsWaitingForExec())) {
//内部遍历履行每个action中携带的command对应的履行函数
1195 am.ExecuteOneCommand();
1196 }
1197 if (!(waiting_for_prop || sm.IsWaitingForExec())) {
1198 //重启死去的进程
restart_processes();
1199
1200 ...
1217 }
1218
1219 return 0;
1220 }
依照履行流程,init函数实际上被履行了4次。
ueventd
在Linux2.6之后,udev取代了DevFs,即userspace/dev。效果首要是办理/dev目录下的设备节点,以及当硬件设备插入或许拔出体系时负责处理用户空间对应的作业,在Android中对应的用户空间程序便是ueventd。
从全局来看,ueventd发动的时分,会为一切当前注册的设备从头生成uevent,首要是遍历/sys目录中的uevent文件,而且向其写入add,然后导致内核生成而且从头发送uevent作业信息给一切注册的设备。
为什么要这样做,由于这些设备注册的时分ueventd还没有运转,所以没法接收到完好的设备信息,所以要从头激活一遍,这个过程也叫做冷发动,init进程的运转需求等待冷发动完成。
关于Linux部分常识,笔者也在努力学习,后续在其他文章输出。
注:这儿为什么叫做ueventd,要加个d的原因便是该进程是看护进程daemon的意思,在手机体系中能够发现该进程:
该进程的父进程号是1,也便是本章所说的init进程。
watchdogd
watchdogd是独立的看门狗进程,访问的目录是/dev/watchdog,是一个独立的硬件,在嵌入式设备中一般用来检查设备的存活状况,在一段时刻超时后,看门狗硬件会以为体系以及跑飞,然后重启设备。
first_stage(第一阶段)
这个阶段首要创立和挂载发动所需求的文件目录,其间挂载了tmpfs、devpts、proc、sysfs和selinuxfs共5种文件体系,这些都是体系运转时目录,即只在体系运转时才存在,体系中止时就会消失。
second_stage(第二阶段)
第二阶段的init函数代码触及内容十分多,而且是在Android用户态中见到的真实程序。
在该阶段做的作业比较多,能够分为下面几个方面:
- 初始化特点服务,而且监听特点服务的改动请求。
- 监听子进程中止信号,处理僵尸进程,对有必需求的子进程进行重启。
- 经过解析init.rc文件,来发动其他进程,首要便是Zygote进程。
这部分代码逻辑比较多,可能细节之处由于笔者Linux和C++能力有限无法正确表达,可是大体流程能够先过一遍。
咱们来挨个剖析该阶段所做的操作。
特点服务
Windows渠道上有一个注册表办理器,注册表的内容采用键值对的形式来记载用户、软件的一些运用信息,即便体系或许软件重启也不会丢失,Android供给了一个类似的机制,叫做特点服务。
关于特点服务的代码包括初始化特点服务和敞开服务,首要代码便是:
//system/core/init/init.cpp
//初始化
property_init()
//敞开特点服务
start_property_service();
特点服务初始化与发动
上面初始化的函数如下:
//system/core/init/property_service.cpp
void property_init() {
72 if (__system_property_area_init()) {
73 LOG(ERROR) << "Failed to initialize property area";
74 exit(1);
75 }
76 }
这儿调用了__system_property_area_init()函数,该函数在规范C库下面:
//bionic/libc/bionic/system_properties.cpp
int __system_property_area_init() {
1122 ...
//调用该办法 映射体系特点区域
1134 if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
1135 free_and_unmap_contexts();
1136 return -1;
1137 }
1138 ...
1140 }
//bionic/libc/bionic/system_properties.cpp
static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) {
868 ...
877 if (access_rw) {
878 __system_property_area__ =
//调用map映射
879 map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
880 } else {
881 __system_property_area__ = map_prop_area(filename);
882 }
883 return __system_property_area__;
884 }
//bionic/libc/bionic/system_properties.cpp
static prop_area* map_prop_area_rw(const char* filename, const char* context,
213 bool* fsetxattr_failed) {
//翻开一个文件
217 const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
218 ...
253 pa_size = PA_SIZE;
254 pa_data_size = pa_size - sizeof(prop_area);
255 //调用熟悉的mmap函数
256 void* const memory_area = mmap(nullptr, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
257 if (memory_area == MAP_FAILED) {
258 close(fd);
259 return nullptr;
260 }
262 prop_area* pa = new (memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
264 close(fd);
265 return pa;
266 }
能够发现这儿最后调用了mmap函数,即内存映射函数,创立了一块同享区域。关于同享内存能够检查文章: # Android IPC | 内存映射详解,请求完同享内存后,接着便是发动服务,代码如下:
//system/core/init/property_service.cpp
void start_property_service() {
744 property_set("ro.property_service.version", "2");
745 //创立一个socket
746 property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
747 false, 0666, 0, 0, nullptr, sehandle);
748 if (property_set_fd == -1) {
749 PLOG(ERROR) << "start_property_service socket creation failed";
750 exit(1);
751 }
752 //监听socket
753 listen(property_set_fd, 8);
754 //运用epoll监听,当收到作业时,调用handle_property_set_fd进行处理
755 register_epoll_handler(property_set_fd, handle_property_set_fd);
756 }
这儿会创立一个非堵塞的Socket,然后调用listen函数对property_set_fd进行监听,这样创立的Socket就成为了Server端,关于Socket的常识,后边深化了解Linux时再讨论。其间listen的第二个参数为8,意味着特点服务最多能够一起8个企图设置特点的用户供给服务。
在Linux中一切皆文件,这儿的register_epoll_handler函数的效果便是将fd的参加到epoll_fd的监听行列中,当文件中有数据改动时,将会调用handle_property_set_fd函数进行处理。
关于Linux底层的epoll循环,后边在Linux篇中介绍。
服务处理客户端请求
所以这儿咱们简略来看一下处理函数,代码如下:
//system/core/init/property_service.cpp
static void handle_property_set_fd() {
459 ...
476 //获取到指令CMD
477 uint32_t cmd = 0;
478 if (!socket.RecvUint32(&cmd, &timeout_ms)) {
479 PLOG(ERROR) << "sys_prop: error while reading command from the socket";
480 socket.SendUint32(PROP_ERROR_READ_CMD);
481 return;
482 }
483
484 switch (cmd) {
485 case PROP_MSG_SETPROP: {
486 char prop_name[PROP_NAME_MAX];
487 char prop_value[PROP_VALUE_MAX];
488
489 if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
490 !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
491 PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
492 return;
493 }
494
495 prop_name[PROP_NAME_MAX-1] = 0;
496 prop_value[PROP_VALUE_MAX-1] = 0;
497 //设置特点
498 handle_property_set(socket, prop_value, prop_value, true);
499 break;
500 }
501
502 ...
521 }
//system/core/init/property_service.cpp
//参数为socket、要设置特点的key\value
static void handle_property_set(SocketConnection& socket,
411 const std::string& name,
412 const std::string& value,
413 bool legacy_protocol) {
414 const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2";
//这儿要检查特点名是否合法
415 if (!is_legal_property_name(name)) {
416 LOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name "" << name << """;
417 socket.SendUint32(PROP_ERROR_INVALID_NAME);
418 return;
419 }
420
421 struct ucred cr = socket.cred();
422 char* source_ctx = nullptr;
423 getpeercon(socket.socket(), &source_ctx);
424 //特点名以"ctl."开端
425 if (android::base::StartsWith(name, "ctl.")) {
426 if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
//处理以ctl.最初的特点
427 handle_control_message(name.c_str() + 4, value.c_str());
428 ...
441 } else {
442 if (check_mac_perms(name, source_ctx, &cr)) {
//处理一般特点
443 ...
453 }
454
455 freecon(source_ctx);
456 }
这儿能够发现特点也是分为俩种的,一种是以ctl.最初的操控特点,一种是一般特点,咱们来看看设置一般特点的办法完成:
//system/core/init/property_service.cpp
uint32_t property_set(const std::string& name, const std::string& value) {
283 if (name == "selinux.restorecon_recursive") {
284 return PropertySetAsync(name, value, RestoreconRecursiveAsync);
285 }
286
287 return PropertySetImpl(name, value);
288 }
//system/core/init/property_service.cpp
static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
170 size_t valuelen = value.size();
171 //判别特点名是否合法
172 if (!is_legal_property_name(name)) {
173 LOG(ERROR) << "property_set("" << name << "", "" << value << "") failed: bad name";
174 return PROP_ERROR_INVALID_NAME;
175 }
176
177 if (valuelen >= PROP_VALUE_MAX) {
178 LOG(ERROR) << "property_set("" << name << "", "" << value << "") failed: "
179 << "value too long";
180 return PROP_ERROR_INVALID_VALUE;
181 }
182
183 prop_info* pi = (prop_info*) __system_property_find(name.c_str());
184 if (pi != nullptr) {
185 //以ro最初的特点不能够更改
186 if (android::base::StartsWith(name, "ro.")) {
187 LOG(ERROR) << "property_set("" << name << "", "" << value << "") failed: "
188 << "property already set";
189 return PROP_ERROR_READ_ONLY_PROPERTY;
190 }
191 //更新特点
192 __system_property_update(pi, value.c_str(), valuelen);
193 } else {
//增加特点
194 int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
195 if (rc < 0) {
196 LOG(ERROR) << "property_set("" << name << "", "" << value << "") failed: "
197 << "__system_property_add failed";
198 return PROP_ERROR_SET_FAILED;
199 }
200 }
//以persist最初的特点需求耐久化
204 if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
205 write_persistent_property(name.c_str(), value.c_str());
206 }
207 property_changed(name, value);
208 return PROP_SUCCESS;
209 }
不同的特点有不同的处理办法,从这儿咱们也能够知道Android体系中的特点能够分为下面3类:
- 以ctl.最初的,表明操控音讯,操控音讯用来履行一些指令。
- 以ro.最初的,表明只读,不能设置,所以直接回来。
- 以persist.最初的,则需求把这些值写到对应的文件,由于是耐久化保存的,所以会有额定IO操作。
关于体系特点咱们能够经过shell指令:getprop检查:
这儿有十分多的特点,比方dalvik的特点:
耐久化的特点:
不行修正的特点:
一起能够运用setprop来设置体系特点:
好了,init进程的一个重要任务初始化特点服务咱们就介绍到这,咱们知道特点服务就相当于一个注册表,运用键值对的办法来保存一些简略信息,而且这些键分了好几种类型,然后运用socket来当作服务端,来监听同享文件改动,然后处理音讯。
设置子进程信号处理函数
在文章刚开端的Android体系架构图中,咱们能够知道init进程其实是Android体系用户空间的鼻祖进程,即其他用户空间一切进程都是它的子进程。
所以利用这个特性咱们能够在init进程中监听其子进程的状况,这是由于当进程的运转状况改动或许中止时会产生某种signal信号,在init进程中想办法监听相对应的信号,然后做处理。
在main()函数中便是signal_handler_init()函数来完成的逻辑,代码如下:
/system/core/init/signal_handler.cpp
void signal_handler_init() {
49 //一切都是为了处理SIGCHLD信号
50 int s[2];
51 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
52 PLOG(ERROR) << "socketpair failed";
53 exit(1);
54 }
55
56 signal_write_fd = s[0];
57 signal_read_fd = s[1];
58 //当捕获到SIGCHLD信号时,则写入signal_write_fd
60 struct sigaction act;
61 memset(&act, 0, sizeof(act));
62 act.sa_handler = SIGCHLD_handler;
63 act.sa_flags = SA_NOCLDSTOP;
//注册捕获SIGCHLD信号,当有信号时写入文件
64 sigaction(SIGCHLD, &act, 0);
65
66 ServiceManager::GetInstance().ReapAnyOutstandingChildren();
67 //监听文件改动
68 register_epoll_handler(signal_read_fd, handle_signal);
69 }
首要是Linux的机制当子进程中止时会产生SIGCHLD信号,然后init进程调用sigaction函数来捕获SIGCHLD信号,当捕获到了会调用SIGCHLD_handler函数:
/system/core/init/signal_handler.cpp
static void SIGCHLD_handler(int) {
43 if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
44 PLOG(ERROR) << "write(signal_write_fd) failed";
45 }
46 }
会发现这儿会往文件里写数据1,然后再经过register_epoll_handler函数来监听该文件改动,然后触发handle_signal函数:
/system/core/init/signal_handler.cpp
static void handle_signal() {
36 char buf[32];
37 read(signal_read_fd, buf, sizeof(buf));
38 //首要的逻辑
39 ServiceManager::GetInstance().ReapAnyOutstandingChildren();
40 }
能够发现首要逻辑在ReapAnyOutstandingChildren()函数中:
/system/core/init/service.cpp
void ServiceManager::ReapAnyOutstandingChildren() {
1217 while (ReapOneProcess()) {
1218 }
1219 }
这儿会调用ReapOneProcess()函数:
/system/core/init/service.cpp
bool ServiceManager::ReapOneProcess() {
1158 siginfo_t siginfo = {};
1159 //这儿会一向调用waitid这个体系调用
1161 if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
1162 PLOG(ERROR) << "waitid failed";
1163 return false;
1164 }
1165
1166 auto pid = siginfo.si_pid;
1167 if (pid == 0) return false;
1168 //依据pid找到服务
1179 Service* svc = FindServiceByPid(pid);
...
//处理service的逻辑
1204 svc->Reap();
1206 ...
1213 return true;
1214 }
这儿思路十分简略,便是调用一个体系调用函数waitid来获取中止的子进程id,这儿体系调用函数咱们不必深究,这儿有个有意思的函数是这个TEMP_FAILURE_RETRY:
/bionic/libc/include/unistd.h
#define TEMP_FAILURE_RETRY(exp) ({ \
238 __typeof__(exp) _rc; \
239 do { \
240 _rc = (exp); \
241 } while (_rc == -1 && errno == EINTR); \
242 _rc; })
这儿便是当函数履行成果为-1且errno为EINTR时一向循环,这儿扯远了,当waitid获取到值时就阐明有子进程现已中止了,这时依据pid找到对应的服务,调用服务的Reap()函数来做处理:
/system/core/init/service.cpp
//中心逻辑办法,用于处理子进程中止时该怎么办
void Service::Reap() {
//当flag为RESTART,表明ONESHOT时,先kill进程组内一切子进程
296 if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
297 KillProcessGroup(SIGKILL);
298 }
//移除一切该进程所创立的描述符
301 std::for_each(descriptors_.begin(), descriptors_.end(),
302 std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
303 //flag为TEMPORARY时,直接不处理
304 if (flags_ & SVC_TEMPORARY) {
305 return;
306 }
307
308 pid_ = 0;
309 flags_ &= (~SVC_RUNNING);
//关于ONESHOT且非RESTART的进程,设置为DISABLED状况
313 if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
314 flags_ |= SVC_DISABLED;
315 }
//关于DISABLED和RESET状况的进程,设置为stop,而且告诉状况
318 if (flags_ & (SVC_DISABLED | SVC_RESET)) {
319 NotifyStateChange("stopped");
320 return;
321 }
322 //服务在4分钟内重启超过4次,则手机进入revovery形式
324 boot_clock::time_point now = boot_clock::now();
325 if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
326 if (now < time_crashed_ + 4min) {
327 if (++crash_count_ > 4) {
328 LOG(ERROR) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
329 panic();
330 }
331 } else {
332 time_crashed_ = now;
333 crash_count_ = 1;
334 }
335 }
336 //进行重启服务
337 flags_ &= (~SVC_RESTART);
338 flags_ |= SVC_RESTARTING;
339 //履行一切onrestart指令
341 onrestart_.ExecuteAllCommands();
342 //告诉状况
343 NotifyStateChange("restarting");
344 return;
345 }
上面大致逻辑咱们能够了解,便是关于不同的进程当它中止时,履行不同的战略。首要便是会告诉体系状况发生了改动,那怎么检查这些运转进程的状况呢,经过下面指令:
这儿进程的状况分为running、stoped和restarting。
然后便是已然这些进程的处理逻辑不同,也便是各个进程的装备不同,那进程的装备信息是怎么设置以及履行呢?这儿咱们直接在下面说init进程怎么发动Zygote进程时来具体阐明,包括怎么读取该进程的装备参数、以及当该进程中止时所履行的操作。
发动Zygote进程
Zygote进程作为敞开Java世界的第一个进程,它的含义十分严重,而创立Zygote进程便是init进程。在上面init进程进口函数中,会有这么一行代码:
/system/core/init/init.cpp
if (bootscript.empty()) {
1137 parser.ParseConfig("/init.rc");
这儿当从体系特点中获取bootscript为空时,会解析init.rc这个文件,其实这儿rc文件便是装备文件,或许叫做脚本文件,在其间装备了该进程所需求做的事,可是想读懂rc文件,必需求了解一种叫做Android初始化言语(Android Int Language)的语法。
Android Init Language
这种言语比较简略,首要包含5种类型句子:Action、Command、Service、Option和Import。其间rc文件语法以行为单位,以空格间隔的语法,以#开端代表注释行。
Action
经过触发器trigger触发,以on最初的句子便是一种Action,具体有如下以on最初的句子:
- on early-init:在初始化早期阶段触发。
- on init:在初始化阶段触发。
- on late-init:在初始化晚期阶段触发。
- on boot/charger:当体系发动/充电时触发,还有其他情况,就不罗列了。
- on property:key=value:当特点值满意条件时触发。
这儿能够看成简略的if句子而已。
Service
该句子以service最初,表明敞开一个服务,一般该服务运转在该rc文件的一个子进程中。比方init.rc中定义的service,在发动时都会经过fork办法生成子进程。
而且这儿在发动service前需求判别对应的可履行文件是否存在,比方在init.rc的结尾:
service console /system/bin/sh
723 class core
724 console
725 disabled
726 user shell
727 group shell log readproc
728 seclabel u:r:shell:s0
这儿的服务名便是console,服务的履行途径为/system/bin/sh。
Command
Command便是履行指令的句子,比方上面在Action和Service后边都会跟一系列的履行句子,其实许多直接看名字就能知道是什么意思,下面罗列一些常用的指令:
- start <service_name>:发动指定的服务,假如现已发动则越过。
- stop <service_name>:中止正在运转的服务。
- setprop:设置特点值。
- mkdir:创立指定目录。
- exec:fork并履行,会堵塞init进程知道程序完毕。
Options
Options是Service的可选项,与service配合运用,下面罗列一些:
- oneshot:service退出后不再重启。
- onrestart:当服务重启时,履行相应的指令。
- critical:在规则时刻内该service不管重启,则体系会重启进入康复形式。
从这儿咱们就能了解前面所说的中止的服务为什么有的需求重启,有的而不需求的原因。
解析rc文件
学习了rc文件的脚本言语,咱们就来看看init.rc脚本到底做了些什么事:
/system/core/rootdir/init.rc
//导入一些rc文件
import /init.environ.rc
8 import /init.usb.rc
9 import /init.${ro.hardware}.rc
10 import /vendor/etc/init/hw/init.${ro.hardware}.rc
11 import /init.usb.configfs.rc
//Zygote的rc文件,区别64位和32位的
12 import /init.${ro.zygote}.rc
13
//当是early-init的Action动作
14 on early-init
16 write /proc/1/oom_score_adj -1000
19 write /proc/sys/kernel/sysrq 0
22 restorecon /adb_keys
25 mkdir /mnt 0775 root system
31 mount cgroup none /acct cpuacct
32 mkdir /acct/uid
35 mkdir /dev/memcg 0700 root system
36 mount cgroup none /dev/memcg memory
38 mkdir /dev/memcg/apps/ 0755 system system
40 mkdir /dev/memcg/system 0550 system system
41 //敞开ueventd服务
42 start ueventd
43 //当是init的Action动作
44 on init
...
//这儿省掉了几百个操作
256 on property:sys.boot_from_charger_mode=1
257 class_stop charger
//触发late-init
258 trigger late-init
259
260 on load_persist_props_action
261 load_persist_props
262 start logd
263 start logd-reinit
264
//late-init阶段
270 on late-init
//触发fs
271 trigger early-fs
277 trigger fs
278 trigger post-fs
285 trigger late-fs
289 trigger post-fs-data
290 //触发zygote
292 trigger zygote-start
295 trigger load_persist_props_action
298 trigger firmware_mounts_complete
300 trigger early-boot
301 trigger boot
302
303 on post-fs
309 load_system_props
311 start logd
//敞开servicemanager服务,重要服务
312 start servicemanager
313 start hwservicemanager
314 start vndservicemanager
315
348 //省掉
370
371 on post-fs-data
373 //省掉几百个操作
//zygote-start敞开
533 on zygote-start && property:ro.crypto.state=unencrypted
536 start netd
537 start zygote
538 start zygote_secondary
539
540 on zygote-start && property:ro.crypto.state=unsupported
542 exec_start update_verifier_nonencrypted
543 start netd
544 start zygote
545 start zygote_secondary
546
547 on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
549 exec_start update_verifier_nonencrypted
550 start netd
551 start zygote
552 start zygote_secondary
553
711 service ueventd /sbin/ueventd
712 class core
713 critical
714 seclabel u:r:ueventd:s0
715 shutdown critical
716
717 service healthd /system/bin/healthd
718 class core
719 critical
720 group root system wakelock
721
722 service console /system/bin/sh
723 class core
724 console
725 disabled
726 user shell
727 group shell log readproc
728 seclabel u:r:shell:s0
729
730 on property:ro.debuggable=1
731 # Give writes to anyone for the trace folder on debug builds.
732 # The folder is used to store method traces.
733 chmod 0773 /data/misc/trace
734 start console
735
736 service flash_recovery /system/bin/install-recovery.sh
737 class main
738 oneshot
上面脚本文件有700多行,这就阐明init发动干了十分多的作业,可是脚本言语都比较好了解,都是一些指令。
咱们挑要点说一点,首要便是import参加进来的zygote的rc文件,这儿会依据CPU的位数来加载不同的rc文件;然后便是加载各种咱们后边会说的服务,比方servicemanager等。
咱们以64位处理器为例,会加载zygote64.rc文件:
/system/core/rootdir/init.zygote64.rc
1 service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
2 class main //注释1
3 priority -20
4 user root
5 group root readproc
6 socket zygote stream 660 root system
7 onrestart write /sys/android_power/request_state wake
8 onrestart write /sys/power/state on
9 onrestart restart audioserver
10 onrestart restart cameraserver
11 onrestart restart media
12 onrestart restart netd
13 onrestart restart wificond
14 writepid /dev/cpuset/foreground/tasks
了解了大概后,咱们来具体剖析init进程是怎么发动Zygote进程的。
class_start指令
在init.rc中有如下指令:
on nonencrypted
651 class_start main
652 class_start late_start
这儿的class_start是一个COMMAND,它对应的解析函数为do_class_start,前面咱们介绍了Android Init Language言语,然后它有一套C++的解析代码,这儿就不具体剖析了。
这儿的class_start会发动那些classname为main的Service,从上面int.Zygote64.rc咱们知道,Zygote的classname便是main,所以这行句子便是来发动Zygote的。
解析指令
咱们来看看解析函数:
/system/core/init/builtins.cpp
static int do_class_start(const std::vector<std::string>& args) {
132 ServiceManager::GetInstance().
133 ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
134 return 0;
135}
ForEachServiceInClass函数会遍历Service链表,找到classname为main的Zygote,并履行StartIfNotDisabled函数:
/system/core/init/service.cpp
bool Service::StartIfNotDisabled() {
867 if (!(flags_ & SVC_DISABLED)) {
868 return Start();
869 } else {
870 flags_ |= SVC_DISABLED_START;
871 }
872 return true;
873}
这儿的意思便是假如Service在rc文件中没有设置disabled选项,则会调用Start()办法发动Service,在前面咱们知道init.zygote64.rc中没有设置disabled选项,所以咱们来看一下Start函数:
/system/core/init/service.cpp
bool Service::Start() {
691 flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
//假如service现已在运转,则不发动
696 if (flags_ & SVC_RUNNING) {
697 return false;
698 }
699
700 bool needs_console = (flags_ & SVC_CONSOLE);
701 if (needs_console) {
702 if (console_.empty()) {
703 console_ = default_console;
704 }
708 int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
709 if (console_fd < 0) {
710 PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
711 flags_ |= SVC_DISABLED;
712 return false;
713 }
714 close(console_fd);
715 }
716 //判别需求发动的Service对应的履行文件是否存在,不存在则不发动
717 struct stat sb;
718 if (stat(args_[0].c_str(), &sb) == -1) {
719 PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
720 flags_ |= SVC_DISABLED;
721 return false;
722 }
723
724 std::string scon;
725 if (!seclabel_.empty()) {
726 scon = seclabel_;
727 } else {
728 scon = ComputeContextFromExecutable(name_, args_[0]);
729 if (scon == "") {
730 return false;
731 }
732 }
733
734 LOG(INFO) << "starting service '" << name_ << "'...";
735
736 pid_t pid = -1;
737 if (namespace_flags_) {
738 pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
739 } else {
//调用fork()函数创立子进程
740 pid = fork();
741 }
...
805 //调用execve办法,Service子进程就会被发动
806 if (!ExpandArgsAndExecve(args_)) {
807 PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
808 }
809
810 _exit(127);
811 }
812
813 ...
864}
上述代码便是发动服务的中心代码,首要会判别Service是否在运转,在运转则不需求发动。然后再判别Service的履行文件是否存在,假如不存在则不发动。然后调用fork函数创立子进程,而且回来pid值。
然后再经过调用execve函数,这个创立的子进程就会被发动,而且进入该Service的main函数中。假如该Service是Zygote,从前面Zygote的rc文件咱们能够知道其履行途径为/system/bin/app_process64,对应的文件为app_main.cpp,而且履行其main函数,然后开端Zygote进程的作业流程。
从这儿咱们能够发现,init进程孵化出Zygote进程是经过fork函数完成的。关于fork函数很关键,它是linux的一个体系调用函数,从字面意思上来说是分支的意思,其实便是把原来进程仿制一遍,为什么这样做呢?是由于创立进程需求做许多操作,这样更方便。然后便是execve函数,它是用来履行进程的。关于这部分linux常识,后边在linux文章中,再仔细剖析。
总结
本篇文章首要剖析了init进程的发动流程,首要作业如下:
- 特点服务部分。创立了同享内存,运用socket作为服务端,不断监听特点改动。其间特点也分为好几种,有的能够修正,有的需求耐久化保存。
- 子进程中止监听。经过监听子进程中止的信号,当子进程中止时,依据进程装备收回资源或许重启进程等操作。
- 解析rc文件,运转脚本,经过在rc文件中定义的脚本,能够发动其他进程的脚本,比方servicemanager、zygote进程等,然后敞开其他进程。
能够见到,init进程的中心作业便是呼应property作业以及收回处理中止的子进程,然后履行rc脚本敞开其他进程脚本。
注:在linux中,父进程创立子进程,在子进程中止后,假如父进程并不知道子进程中止了,这时进程尽管现已退出了,可是在体系进程表中还为它保留必定的信息,比方进程号、退出状况、运转时刻等,这个子进程便是僵尸进程。体系进程表是一项有限的资源,假如体系进程表被僵尸进程耗尽的话,就无法创立新进程了,所以清理僵尸进程很关键。
本篇文章触及较多linux和C++常识,笔者也在努力加油学习,文中有问题,欢迎纠正。最后记载一下Flag。# 一个Android开发的学习Flag记载贴