framework | init进程流程分析

2,038 阅读15分钟

前言

在上一篇文章 Android系统架构 中,我们说可以从线程和进程的角度来动态理解Android的系统分层,还是给出经典图:

image.png

在这张图中,我们知道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的意思,在手机系统中可以发现该进程:

image.png

该进程的父进程号是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的属性:

image.png

持久化的属性:

image.png

不可修改的属性:

image.png

同时可以使用setprop来设置系统属性:

image.png

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

上面大致逻辑我们可以明白,就是对于不同的进程当它终止时,执行不同的策略。首先就是会通知系统状态发生了改变,那如何查看这些运行进程的状态呢,通过下面命令:

image.png

这里进程的状态分为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记录贴