init,是linux系统中用户空间的第一个进程,也是Android系统中用户空间的第一个进程。
位于/system/core/init目录下。
- int main(int argc, char **argv)
- {
- //设置子进程退出的信号处理函数 sigchld_handler
- act.sa_handler = sigchld_handler;
-
- //解析init.rc文件
- parse_config_file("/init.rc");
- //通过这个函数读取/proc/cpuinfo得到机器的HardWare名,
- get_hardware_name();
- snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
- //解析这个和机器相关的配置文件
- parse_config_file(tmp);
-
-
- //执行各个阶段的工作 共分为4个阶段
- /**
- * ① early-init阶段完成的动作
- */
- action_for_each_trigger("early-init", action_add_queue_tail);
- drain_action_queue();
-
- //初始化和属性相关的资源
- property_init();
-
- /**INIT_IMAGE_FILE定义为“initlogo.rle”
- * 这个函数将加载这个文件作为系统开机的画面
- * 如果加载文件initlogo.rle失败就会打开/dev/tty0设备,将输出“ANDROID”的字样作为开机画面
- */
-
- if( load_565rle_image(INIT_IMAGE_FILE) ) {
- fd = open("/dev/tty0", O_WRONLY);
- if (fd >= 0) {
- const char *msg;
- msg = "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n" // console is 40 cols x 30 lines
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- " A N D R O I D ";
- write(fd, msg, strlen(msg));
- close(fd);
- }
- }
-
- /**
- * ②init
- */
- action_for_each_trigger("init", action_add_queue_tail);
- drain_action_queue();
-
- //启动属性服务
- property_set_fd = start_property_service();
-
- /**
- * ③early-boot ④boot
- */
- action_for_each_trigger("early-boot", action_add_queue_tail);
- action_for_each_trigger("boot", action_add_queue_tail);
- drain_action_queue();
-
- //boot char是一个小工具 对系统性能进行分析 帮助提升系统的启动速度
- #if BOOTCHART
- bootchart_count = bootchart_init();
- if (bootchart_count < 0) {
- ……
- #endif
-
-
- /**
- * 进入init循环
- */
-
- for(;;) {
- int nr, i, timeout = -1;
-
- for (i = 0; i < fd_count; i++)
- ufds[i].revents = 0;
-
- drain_action_queue();
- //重启死亡的进程
- restart_processes();
- //调用poll等待一些事情的发生
- nr = poll(ufds, fd_count, timeout);
-
总结:init工作主要先初始化两个配置文件,然后执行四个阶段的动作,接下来调用property_init相关的属性资源,并通过property_start_service启动属性服务,然后init将进入一个无限循环,并等待一些事情的发生。
解析配置文件
init会解析两个配置文件一个是系统配置文件init_rc,另一个是与硬件平台相关的配置文件,解析调用的是parse_config_file函数,然后看看这个函数里做了什么?
/system/core/init下的parser.c文件
- int parse_config_file(const char *fn)
- {
- parse_config(fn, data);
- return 0;
- }
- static void parse_config(const char *fn, char *s)
- {
- struct parse_state state;
- char *args[SVC_MAXARGS];
- int nargs;
-
- nargs = 0;
- state.filename = fn;
- state.line = 1;
- state.ptr = s;
- state.nexttoken = 0;
- //设置解析函数 不同的内容用不同的解析函数
- state.parse_line = parse_line_no_op;
- for (;;) {
- switch (next_token(&state)) {
- case T_EOF:
- state.parse_line(&state, 0, 0);
- return;
- case T_NEWLINE:
- if (nargs) {
- //得到关键字类型
- int kw = lookup_keyword(args[0]);
- //判断关键字是不是SECTION
- if (kw_is(kw, SECTION)) {
- state.parse_line(&state, 0, 0);
- //解析这个SECTION
- parse_new_section(&state, kw, nargs, args);
- } else {
- state.parse_line(&state, nargs, args);
- }
- nargs = 0;
- }
- break;
- case T_TEXT:
- if (nargs < SVC_MAXARGS) {
- args[nargs++] = state.text;
- }
- break;
- }
- }
- }
实际上,parse_config首先会找到配置文件中的section,然后根据不同的section使用不同的解析函数去解析。section是什么?在parse.h里包含了keywords.h脚本文件

第一次包含keywords.h时声明了一些函数,定义了一个枚举值是K_class等关键字
第二次包含keywords.h后,得到了一个keyword_info的结构体数组,以前面对应的枚举值为索引,存储对应的关键字信息,这些信息包括关键字名称、处理函数、函数参数个数以及属性。根据keywords.h中的定义,on和service就可以表示成section。

init.rc的解析:
1 一个section的内容的从这个标识section的关键字开始,到下一个标识section的关键字结束。
2 init.rc中出现的名字为boot和init的section就是前面介绍的四个动作执行阶段中的boot和init,也就是说,在boot这个阶段执行的动作都是由boot这个section定义的。
然后再来解析init.rc文件

总结:
1. 一个section的内容是从这个标识section的关键字开始,直到下一个标识section的地方结束。on和service都是section开始的标识。
2. 这里出现了名为boot和init的section,这就是前面四个动作中执行的init和boot,init和boot阶段执行的动作都是在这里定义的。
Zygote对应的是service,zygote的section是这样的:
- service zygote /system/bin/app_process -Xzygote /system/bin =zygote \
- --start system server
- socket zygote stream 666
- onrestart write
- onrestart write
- onrestart restart media
解析section的入口函数是parse_new_section,在这里,解析service时用到了parse_service和parse_line_service这两个函数,
先来看Service结构体,是什么样子的?
- struct service {
- struct listnode slist;//将结构体连接成一个双向链表
- const char *name; //service的名字 这里就是zygote
- const char *classname; //service所属的class的名字
- unsigned flags;//service属性
- pid_t pid;//进程号
- time_t time_started; //上一次启动时间
- time_t time_crashed; //上一次死亡的时间
- int nr_crashed; //死亡的次数
- uid_t uid;
- gid_t gid;
- gid_t supp_gids[NR_SVC_SUPP_GIDS];
- size_t nr_supp_gids;
- struct socketinfo *sockets; //用来描述socket信息的结构体
- //service一般运行在一个单独的线程中,envvars表示创建这个进程需要的环境变量
- struct svcenvinfo *envvars;
- struct action onrestart; /* Actions to execute on restart. */
- /* 与keywords相关的信息 */
- int *keycodes;
- int nkeycodes;
- int keychord_id;
- //IO优先级
- int ioprio_class;
- int ioprio_pri;
- int nargs; //参数个数
- char *args[1];
- }
现在已经了解了service中的结构体,再来看zygote中的三个onrestart是什么,先看action结构体:
- struct action {
- /*一个action结构体可以存放在三个双向链表中 */
- struct listnode alist; //存放所有的action
- struct listnode qlist;//链接等待执行的action
- struct listnode tlist; //链接那些待某些条件满足后需要执行的action
-
- unsigned hash;
- const char *name;
- /*对应的是command列表 zygote有三个onrestart就会创建三个command结构体*/
- struct listnode commands;
- struct command *current;
- };
接下来,在parse_service和parse_line_service里,parse_service搭建了一个service的架子,parse_line_service里:
- kw = lookup_keyword(args[0]);//根据关键字进行处理
- switch (kw) {
- case K_capability:
- break;
- case K_class:
- if (nargs != 2) {
- parse_error(state, "class option requires a classname\n");
- } else {
- svc->classname = args[1];
- }
- break;
- case K_oneshot: //service的属性
- svc->flags |= SVC_ONESHOT;/*有五个属性 SVC_ONESHOT代表退出后不需要重启*/
- //SVC_DISABLED 不随class自动启动;SVC_RUNNING 正在运行;SVC_RESTARING:等待重启;
- //SVC_CONSOLE 该service需要使用控制台;SVC_CRITICAL 如果在规定时间内,该service不断重启则系统会重启进入恢复模式。
- ......
- List_add_tail(&svc->onrestart.commands, &cmd->clist);// 把新建的command加入到双向链表中
- ......
由此可见,parse_line_service是根据配置文件的内容填充service结构体。
总结:
service_list解析后将service连接到一起,是一个双向链表的形式。
socket_info也是一个双向链表。
onrestart通过command指向一个command链表,zygote有三个command。
Init.rc目录是system/core/rootdir/init,rc
在init.rc的解析中,包含了这样的内容:

class_start标识一个COMMAND,对应的处理函数是do_class_start,位于boot section里,在init的main函数里,boot阶段是这样的,
- //将boot的section节的的COMMAND加入到执行队列
- action_for_each_trigger("boot", action_add_queue_tail);
- //执行队列里的命令,class是一个COMMAND,所以他对应的do_class_start会被执行
- drain_action_queue();
do_class_start函数位于system/core/init/builtins.c中,
- int do_class_start(int nargs, char **args)
- {
- /*args是do_class_start的参数,init.rc中只有一个参数,就是default,下面这个函数将从service_list中找到classname为default的service,然后调用service_start_if_not_disabled*/
- //zygote的classname 就是default
- service_for_each_class(args[1], service_start_if_not_disabled);
- return 0;
- }
service_start_if_not_disabled里调用了service_start函数,代码分析如下:
1 启动zygote,Zygote是由fork和execve共同创建的
- void service_start(struct service *svc, const char *dynamic_args)
- {
- ...
- if (svc->flags & SVC_RUNNING) {
- return; //service已经在运行,不用处理
- }
- /*service一般运行在另外一个进程中,这个进程也是init的子进程,*/
- if (stat(svc->args[0], &s) != 0) {
- ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
- svc->flags |= SVC_DISABLED;
- return;
- }
- //调用fork创建子进程
- pid = fork();
- //Pid为0的时候,表明运行在子线程中
- if (pid == 0) {//pid为0 表明运行在子进程中
- ......
- //添加环境变量信息 socket等
-
- if (!dynamic_args) {
- if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
- ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
- }
- }
- }
- //父进程的init的处理
- svc->time_started = gettime();
- svc->pid = pid;
- svc->flags |= SVC_RUNNING;
- ......
- }
2 重启zygote
onrestart方法顾名思义是在zygote重启的时候调用,zygote死亡后,父进程init:
- static void sigchld_handler(int s)
- { //子进程退出后 init这个信号函数会被调用
- write(signal_fd, &s, 1); // 往signal_fd写数据
- }
signal_fd就是在init中通过socketpair创建的两个当中的一个,会往这个socket里发送数据另一个socket就一定能收到,这样就会导致init从poll函数中返回,在init.c中,
- for(;;) {
- int nr, i, timeout = -1;
- for (i = 0; i < fd_count; i++)
- ufds[i].revents = 0;
- drain_action_queue();//poll函数返回后 会有下一轮的循环
- restart_processes(); // 重启所有flag标识为SVC_RESTARTING的service
- }
那么zygote在哪里重启呢?答案是init.c的main函数里.
- static int wait_for_one_process(int block)
- {
- ......
- svc = service_find_by_pid(pid);//找到死掉的service
- //杀掉zygote创建的所有子进程,这就是zygote死后Java世界崩溃的原因
- if (!(svc->flags & SVC_ONESHOT)) {
- kill(-pid, SIGKILL);
- NOTICE("process '%s' killing any children in process group\n", svc->name);
- }
- //如果设置了SVC_CRITICAL模式,则四分钟内重启的次数不能超过四次否则机器
- //会在重启后进入recovery模式
- if (svc->flags & SVC_CRITICAL) {
- if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
- if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
- ERROR("critical process '%s' exited %d times in %d minutes; "
- "rebooting into recovery mode\n", svc->name,
- CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
- sync();
- __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
- LINUX_REBOOT_CMD_RESTART2, "recovery");
- return 0;
- }
- } else {
- svc->time_crashed = now;
- svc->nr_crashed = 1;
- }
- //设置标识为SVC_RESTARTING,然后执行service onrestart中的COMMAND
- svc->flags |= SVC_RESTARTING;
- }
Windows平台有个叫注册表的东西,可以存储一些类似key/value的键值对,包含了系统的属性,即使系统在重启后也能根据注册表中的属性进行初始化工作,Android平台这种类似的机制叫做属性服务---property service 如下是该手机的部分属性

init.c中有个函数初始化属性服务函数 property_init(),这里先调用init_property_area()函数进行初始化属性存储区域,
- static int init_property_area(void)
- {
- prop_area *pa;
- if(pa_info_array)
- return -1;
-
- /*initworkspace是一块内存 PA_SIZE是存储空间的总大小 a_workspace是workspace的结构体*/
- if(init_workspace(&pa_workspace, PA_SIZE))
- return -1;
-
- fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
- pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);
-
- pa = pa_workspace.data;
- memset(pa, 0, PA_SIZE);
- pa->magic = PROP_AREA_MAGIC;
- pa->version = PROP_AREA_VERSION;
-
- /*这个变量由bionic libc库输出 */
- __system_property_area__ = pa;
-
- return 0;
- }
为什么在最后要给变量赋值呢?属性虽然由init创建,但是Android希望其他进程也能读取到这块内存里的东西所以做了以下工作:
客户端进程获取存储空间
在bionic/libc/bionic/libc_init_common.c的libc_init_dynamic.c函数中,constructor属性指示加载器加载该库后,首先调用_libc_prenit函数,然后调用libc_init_common函数,最后一行就是初始化客户端的属性存储区域。

- int __system_properties_init(void)
- {
- ...
- //zygote中添加的环境变量 在这里取出
- env = getenv("ANDROID_PROPERTY_WORKSPACE");
- /*映射init创建的那块内存到本地进程空间,本地进程就可以使用这个共享内存了 指定了PROT_READ属性是只读不允许设置*/
- pa = mmap(0, sz, PROT_READ, MAP_SHARED, fd, 0);
- }
属性服务器的分析
init进程会启动一个属性服务器来与与客户端交互设置客户端属性,属性服务器由start_property_service函数启动
- int start_property_service(void)
- {
- int fd;
- //加载属性文件,解析出来设置到属性空间中
- load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
- load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
- load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);
- //创建socket用于IPC通信
- fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
- if(fd < 0) return -1;
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- fcntl(fd, F_SETFL, O_NONBLOCK);
-
- listen(fd, 8);
- return fd;
- }
属性服务器接受到客户端请求后会在init进程中,调用handle_property_set_fd进行处理。检查客户端是否有足够的权限,有则调用property_set(位于properties_service.c下)函数进行相关处理。
那么客户端是如何设置属性的?
客户端调用property_set(位于properties.c下)进行处理,在libcutils库下,
- static int send_prop_msg(prop_msg *msg)
- { //建立和属性服务器的socket链接
- s = socket_local_client(PROP_SERVICE_NAME,
- ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM);
- //通过socket发出去
- while((r = send(s, msg, sizeof(prop_msg), 0)) < 0) {
- if((errno == EINTR) || (errno == EAGAIN)) continue;
- break;
- }
- }
- int property_set(const char *key, const char *value)
- { ......
- msg.cmd = PROP_MSG_SETPROP;//设置消息码为PROP_MSG_SETPROP
- return send_prop_msg(&msg);......
- }
至此,讲解了init如何解析配置文件,解析zygote,以及属性服务。