前言

在上一篇文章 Android体系架构 中,咱们说能够从线程和进程的角度来动态了解Android的体系分层,仍是给出经典图:

framework | init进程流程分析

在这张图中,咱们知道Loader层和Kernel层暂时不是咱们要研究的要点,由于它们是Linux内核层,而在Android体系用户态中,会先发动native层即C++代码部分,而这部分的作业就由init进程所负责。

作为Android体系用户态的第一个进程,它是Android体系发动流程中的一个关键步骤,被赋予了许多及其重要的作业责任,比方创立Zygote进程、创立特点服务、监听子进程中止等等。

本文源码:aospxref.com/android-8.1…

正文

话不多说,咱们就来剖析剖析该进程的作业流程。

开端init进程

init进程的进程号固定为1,经过如下今个步骤引进init进程:

  1. 发动电源以及体系发动:当电源键按下时从固化在ROM中的芯片代码开端履行,加载引导程序BootLoader到RAM中,然后开端履行。
  2. 引导程序BootLoader:BootLoader是Android体系开端运转前的一个程序,用来拉起体系OS。
  3. Linux内核发动:当内核发动时,会设置缓存、加载驱动等,当内核加载好后,会发动init进程。
  4. 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的意思,在手机体系中能够发现该进程:

framework | init进程流程分析

该进程的父进程号是1,也便是本章所说的init进程。

watchdogd

watchdogd是独立的看门狗进程,访问的目录是/dev/watchdog,是一个独立的硬件,在嵌入式设备中一般用来检查设备的存活状况,在一段时刻超时后,看门狗硬件会以为体系以及跑飞,然后重启设备。

first_stage(第一阶段)

这个阶段首要创立和挂载发动所需求的文件目录,其间挂载了tmpfs、devpts、proc、sysfs和selinuxfs共5种文件体系,这些都是体系运转时目录,即只在体系运转时才存在,体系中止时就会消失

second_stage(第二阶段)

第二阶段的init函数代码触及内容十分多,而且是在Android用户态中见到的真实程序。

在该阶段做的作业比较多,能够分为下面几个方面:

  1. 初始化特点服务,而且监听特点服务的改动请求。
  2. 监听子进程中止信号,处理僵尸进程,对有必需求的子进程进行重启。
  3. 经过解析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的特点:

framework | init进程流程分析

耐久化的特点:

framework | init进程流程分析

不行修正的特点:

framework | init进程流程分析

一起能够运用setprop来设置体系特点:

framework | init进程流程分析

好了,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 }

上面大致逻辑咱们能够了解,便是关于不同的进程当它中止时,履行不同的战略。首要便是会告诉体系状况发生了改动,那怎么检查这些运转进程的状况呢,经过下面指令:

framework | init进程流程分析

这儿进程的状况分为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进程的发动流程,首要作业如下:

  1. 特点服务部分。创立了同享内存,运用socket作为服务端,不断监听特点改动。其间特点也分为好几种,有的能够修正,有的需求耐久化保存。
  2. 子进程中止监听。经过监听子进程中止的信号,当子进程中止时,依据进程装备收回资源或许重启进程等操作。
  3. 解析rc文件,运转脚本,经过在rc文件中定义的脚本,能够发动其他进程的脚本,比方servicemanager、zygote进程等,然后敞开其他进程。

能够见到,init进程的中心作业便是呼应property作业以及收回处理中止的子进程,然后履行rc脚本敞开其他进程脚本。

注:在linux中,父进程创立子进程,在子进程中止后,假如父进程并不知道子进程中止了,这时进程尽管现已退出了,可是在体系进程表中还为它保留必定的信息,比方进程号、退出状况、运转时刻等,这个子进程便是僵尸进程。体系进程表是一项有限的资源,假如体系进程表被僵尸进程耗尽的话,就无法创立新进程了,所以清理僵尸进程很关键。

本篇文章触及较多linux和C++常识,笔者也在努力加油学习,文中有问题,欢迎纠正。最后记载一下Flag。# 一个Android开发的学习Flag记载贴