android启动过程分析--启动init进程

本文详细介绍了Android系统的启动过程,从init进程启动到Home启动的四个阶段,并深入剖析了init进程的具体工作流程,包括文件目录准备、log系统初始化、init.rc解析等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载:http://blog.youkuaiyun.com/new_abc/article/details/7424587


每当我们学习一门新的语言时,我们总是以HelloWorld来开始我们的学习之旅,每当我们分析一个应用程序时,我们也总会找到main函数入口,学习一个系统,最好的办法也是先把它的启动流程弄清楚,这样,后面分析起来才能更好的把握。

android系统比较复杂,它的启动涉及的东西也比较多,但是只要我们把一点点都弄明白 ,最后整个流程起来再看的话也就会明了很多,我们先看下init启动过程中主要做的事。

Android从Linux系统启动有4个步骤:

(1)、init进程启动

(2)、Native服务启动

(3)、System Server、Android服务启动

(4)、Home启动

总体启动框架图如:


首先我们看下init进程的启动过程中都做了哪些事情:

代码路径:system/init

下面我们一步步看它都 做了哪些事情:

1、准备系统启动所需要的最小文件目录:

创建必要的文件夹,挂载所需文件系统,重定位标准输入输出错误到/dev/__null__,以下是相关代码:

  1. int main(int argc, char **argv)  
  2. {  
  3.     int fd_count = 0;  
  4.     struct pollfd ufds[4];  
  5.     char *tmpdev;  
  6.     char* debuggable;  
  7.     char tmp[32];  
  8.     int property_set_fd_init = 0;  
  9.     int signal_fd_init = 0;  
  10.     int keychord_fd_init = 0;  
  11.   
  12.     if (!strcmp(basename(argv[0]), "ueventd"))  
  13.         return ueventd_main(argc, argv);  
  14.   
  15.     /* clear the umask */  
  16.     umask(0);  
  17.   
  18.         /* Get the basic filesystem setup we need put 
  19.          * together in the initramdisk on / and then we'll 
  20.          * let the rc file figure out the rest. 
  21.          */  
  22.     mkdir("/dev", 0755);  
  23.     mkdir("/proc", 0755);  
  24.     mkdir("/sys", 0755);  
  25.   
  26.     mount("tmpfs""/dev""tmpfs", 0, "mode=0755");  
  27.     mkdir("/dev/pts", 0755);  
  28.     mkdir("/dev/socket", 0755);  
  29.     mount("devpts""/dev/pts""devpts", 0, NULL);  
  30.     mount("proc""/proc""proc", 0, NULL);  
  31.     mount("sysfs""/sys""sysfs", 0, NULL);  
  32.   
  33.         /* We must have some place other than / to create the 
  34.          * device nodes for kmsg and null, otherwise we won't 
  35.          * be able to remount / read-only later on. 
  36.          * Now that tmpfs is mounted on /dev, we can actually 
  37.          * talk to the outside world. 
  38.          */  
  39.     open_devnull_stdio();//需要在后面的程序中看到打印的话需要屏蔽这个函数  
我们再看下open_devnull_stdio 函数

  1. void open_devnull_stdio(void)  
  2. {  
  3.     int fd;  
  4.     static const char *name = "/dev/__null__";  
  5.     if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {  
  6.         fd = open(name, O_RDWR);  
  7.         unlink(name);  
  8.         if (fd >= 0) {  
  9.             dup2(fd, 0);  
  10.             dup2(fd, 1);  
  11.             dup2(fd, 2);  
  12.             if (fd > 2) {  
  13.                 close(fd);  
  14.             }  
  15.             return;  
  16.         }  
  17.     }  
  18.   
  19.     exit(1);  
  20. }  
这里调用dup函数把标准输入,输出,错误输出都重定位到/dev/__null__

2、初始化log系统

调用log_init对log系统进行初始化,我们看下这个函数:

  1. void log_init(void)  
  2. {  
  3.     static const char *name = "/dev/__kmsg__";  
  4.     if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {  
  5.         log_fd = open(name, O_WRONLY);  
  6.         fcntl(log_fd, F_SETFD, FD_CLOEXEC);  
  7.         unlink(name);  
  8.     }  
  9. }  
这里创建并打开了一个设备节点,用于后面的log信息处理,如写日志等就往这个节点里面写.

.3、解析init.rc

这里调用了parse_config去解析init.rc。

  1. static void parse_config(const char *fn, char *s)  
  2. {  
  3.     struct parse_state state;  
  4.     char *args[INIT_PARSER_MAXARGS];  
  5.     int nargs;  
  6.   
  7.     nargs = 0;  
  8.     state.filename = fn;  
  9.     state.line = 1;  
  10.     state.ptr = s;  
  11.     state.nexttoken = 0;  
  12.     state.parse_line = parse_line_no_op;  
  13.     for (;;) {  
  14.         switch (next_token(&state)) {  
  15.         case T_EOF:  
  16.             state.parse_line(&state, 0, 0);  
  17.             return;  
  18.         case T_NEWLINE:  
  19.             if (nargs) {  
  20.                 int kw = lookup_keyword(args[0]);  
  21.                 if (kw_is(kw, SECTION)) {  
  22.                     state.parse_line(&state, 0, 0);  
  23.                     parse_new_section(&state, kw, nargs, args);  
  24.                 } else {  
  25.                     state.parse_line(&state, nargs, args);  
  26.                 }  
  27.                 nargs = 0;  
  28.             }  
  29.             break;  
  30.         case T_TEXT:  
  31.             if (nargs < INIT_PARSER_MAXARGS) {  
  32.                 args[nargs++] = state.text;  
  33.             }  
  34.             break;  
  35.         }  
  36.     }  
  37. }  

调用next_token对下一个字符进行解析,这里分成了三类,文件结束,换行,还有就是字符  ,它们的处理也不一样。

1、如果是文件结束符,直接调用parse_line进行处理

2、如果是字符,则保存到args变量中

3、如果是换行,首先查关键字,如果是SECTION,说明是一个新的段的开始,先调用parse_line处理(因为第二个参数为0,其实什么也没有做),然后调用parse_new_section对这新的一个段的开始一行进行解析;否则直接调用parse_line解析。

再看一下parse_new_section

  1. void parse_new_section(struct parse_state *state, int kw,  
  2.                        int nargs, char **args)  
  3. {  
  4.     printf("[ %s %s ]\n", args[0],  
  5.            nargs > 1 ? args[1] : "");  
  6.     switch(kw) {  
  7.     case K_service:  
  8.         state->context = parse_service(state, nargs, args);  
  9.         if (state->context) {  
  10.             state->parse_line = parse_line_service;  
  11.             return;  
  12.         }  
  13.         break;  
  14.     case K_on:  
  15.         state->context = parse_action(state, nargs, args);  
  16.         if (state->context) {  
  17.             state->parse_line = parse_line_action;  
  18.             return;  
  19.         }  
  20.         break;  
  21.     }  
  22.     state->parse_line = parse_line_no_op;  
  23. }  
这里也是对keyword分两种情况进行了处理,一个是service,另一个是on

1、如果是service,则调用parse_service进行处理,并将state的parse_line设置为parse_line_service;

2、如果是On,则调用parse_action进行解析,并将state的parse_line设置为parse_line_action;

再来看一下parse_service

  1. static void *parse_service(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     struct service *svc;  
  4.     if (nargs < 3) {  
  5.         parse_error(state, "services must have a name and a program\n");  
  6.         return 0;  
  7.     }  
  8.     if (!valid_name(args[1])) {  
  9.         parse_error(state, "invalid service name '%s'\n", args[1]);  
  10.         return 0;  
  11.     }  
  12.   
  13.     svc = service_find_by_name(args[1]);  
  14.     if (svc) {  
  15.         parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);  
  16.         return 0;  
  17.     }  
  18.   
  19.     nargs -= 2;  
  20.     svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);  
  21.     if (!svc) {  
  22.         parse_error(state, "out of memory\n");  
  23.         return 0;  
  24.     }  
  25.     svc->name = args[1];  
  26.     svc->classname = "default";  
  27.     memcpy(svc->args, args + 2, sizeof(char*) * nargs);  
  28.     svc->args[nargs] = 0;  
  29.     svc->nargs = nargs;  
  30.     svc->onrestart.name = "onrestart";  
  31.     list_init(&svc->onrestart.commands);  
  32.     list_add_tail(&service_list, &svc->slist);  
  33.     return svc;  
  34. }  

这里首先看这个service是否已经存在,存在说明重复了,直接返回错误,

不存在的话根据这个service的参数等分配空间,并利用传进来的参数进行初始化,最后把它加到service_list列表。

再看看parse_action

  1. static void *parse_action(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     struct action *act;  
  4.     if (nargs < 2) {  
  5.         parse_error(state, "actions must have a trigger\n");  
  6.         return 0;  
  7.     }  
  8.     if (nargs > 2) {  
  9.         parse_error(state, "actions may not have extra parameters\n");  
  10.         return 0;  
  11.     }  
  12.     act = calloc(1, sizeof(*act));  
  13.     act->name = args[1];  
  14.     list_init(&act->commands);  
  15.     list_add_tail(&action_list, &act->alist);  
  16.         /* XXX add to hash */  
  17.     return act;  
  18. }  

这里主要把命令加入到action_list中。

4、添加一些其它的命令到action_queue和action_queue

主要调用queue_builtin_action把action_list中的一些命令添加到action_queue,

调用queue_builtin_action一些命令到action_list

5、进入循环处理

1)、调用execute_one_command执行一个命令

  1. void execute_one_command(void)  
  2. {  
  3.     int ret;  
  4.   
  5.     if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {  
  6.         cur_action = action_remove_queue_head();  
  7.         cur_command = NULL;  
  8.         if (!cur_action)  
  9.             return;  
  10.         INFO("processing action %p (%s)\n", cur_action, cur_action->name);  
  11.         cur_command = get_first_command(cur_action);  
  12.     } else {  
  13.         cur_command = get_next_command(cur_action, cur_command);  
  14.     }  
  15.   
  16.     if (!cur_command)  
  17.         return;  
  18.   
  19.     ret = cur_command->func(cur_command->nargs, cur_command->args);  
  20.     INFO("command '%s' r=%d\n", cur_command->args[0], ret);  
  21. }  

这里主要是从action_queue的队列头弹出一个命令并调用它的func函数执行,这里的func函数 一般是do_***,在keywords.h中定义的。

2)、restart_processes

查看是否有服务需要重启,如果有重启它。

3)、把基于property、signal、keychord的句柄加入poll检测,调用poll等待事件到来。

  1. for (i = 0; i < fd_count; i++) {  
  2.            if (ufds[i].revents == POLLIN) {  
  3.                if (ufds[i].fd == get_property_set_fd())  
  4.                    handle_property_set_fd();  
  5.                else if (ufds[i].fd == get_keychord_fd())  
  6.                    handle_keychord();  
  7.                else if (ufds[i].fd == get_signal_fd())  
  8.                    handle_signal();  
  9.            }  
  10.        }  

分别调用相应的函数进行处理,这里稍微分析下这三个事件。

1、handle_property_set_fd

这个函数主要用来处理属性事件,包括属性设置和启动一些service,部分代码

  1. if(memcmp(msg.name,"ctl.",4) == 0) {  
  2.     if (check_control_perms(msg.value, cr.uid, cr.gid)) {  
  3.         handle_control_message((char*) msg.name + 4, (char*) msg.value);  
  4.     } else {  
  5.         ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n",  
  6.                 msg.name + 4, msg.value, cr.uid, cr.pid);  
  7.     }  
  8. else {  
  9.     if (check_perms(msg.name, cr.uid, cr.gid)) {  
  10.         property_set((char*) msg.name, (char*) msg.value);  
  11.     } else {  
  12.         ERROR("sys_prop: permission denied uid:%d  name:%s\n",  
  13.               cr.uid, msg.name);  
  14.     }  
  15. }  

如果是ctl开头的,说明是控制信息,启动或停止某个service

我们看在开机界面的启动,这个也是一个services,在init.rc中加进去了,但是没有启动

[plain]  view plain copy print ?
  1. service bootanim /system/bin/bootanimation  
  2.     user graphics  
  3.     group graphics  
  4.     disabled  
  5.     oneshot  

然后在status_t SurfaceFlinger::readyToRun()中有如下代码:

 property_set("ctl.start", "bootanim");

bootanim 就是上面 /system/bin/bootanimation 这个service的名字,

我们看到property_set最终调用了send_prop_msg,看下这个函数:

  1. static int send_prop_msg(prop_msg *msg)  
  2. {  
  3.     int s;  
  4.     int r;  
  5.       
  6.     s = socket_local_client(PROP_SERVICE_NAME,   
  7.                             ANDROID_SOCKET_NAMESPACE_RESERVED,  
  8.                             SOCK_STREAM);  
  9.     if(s < 0) return -1;  
  10.       
  11.     while((r = send(s, msg, sizeof(prop_msg), 0)) < 0) {  
  12.         if((errno == EINTR) || (errno == EAGAIN)) continue;  
  13.         break;  
  14.     }  
  15.   
  16.     if(r == sizeof(prop_msg)) {  
  17.         r = 0;  
  18.     } else {  
  19.         r = -1;  
  20.     }  
  21.   
  22.     close(s);  
  23.     return r;  
  24. }  

这里把消息发给服务端,那么服务端是什么时候监听的呢,我们看下init.rc中有如下代码。

在mina函数 中有

  1. queue_builtin_action(property_service_init_action, "property_service_init");  

这里面调用了start_property_service();我们看下它;

  1. void start_property_service(void)  
  2. {  
  3.     int fd;  
  4.   
  5.     load_properties_from_file(PROP_PATH_SYSTEM_BUILD);  
  6.     load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);  
  7.     load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);  
  8.     /* Read persistent properties after all default values have been loaded. */  
  9.     load_persistent_properties();  
  10.   
  11.     fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);  
  12.     if(fd < 0) return;  
  13.     fcntl(fd, F_SETFD, FD_CLOEXEC);  
  14.     fcntl(fd, F_SETFL, O_NONBLOCK);  
  15.   
  16.     listen(fd, 8);  
  17.     property_set_fd = fd;  
  18. }  

  1. property  
对的,就是在这里监听的。这样有属性设置或启动相关service时就可以通过socket通信来 实现了。

2、handle_keychord处理调试模式下的组合按键

3、handle_signal

  1. void handle_signal(void)  
  2. {  
  3.     char tmp[32];  
  4.   
  5.     /* we got a SIGCHLD - reap and restart as needed */  
  6.     read(signal_recv_fd, tmp, sizeof(tmp));  
  7.     while (!wait_for_one_process(0))  
  8.         ;  
  9. }  

handle_signal阻塞在read上,但当有子进程退出时,sigchld_handler会write(signal_fd, &s, 1);这里signal_fd与signal_recv_fd是本了已经建立好的通信套接字,导致上面的read返回,进入 wait_for_one_process
  1. static int wait_for_one_process(int block)  
  2. {  
  3.     pid_t pid;  
  4.     int status;  
  5.     struct service *svc;  
  6.     struct socketinfo *si;  
  7.     time_t now;  
  8.     struct listnode *node;  
  9.     struct command *cmd;  
  10.   
  11.     while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );  
  12.     if (pid <= 0) return -1;  
  13.     INFO("waitpid returned pid %d, status = %08x\n", pid, status);  
  14.   
  15.     svc = service_find_by_pid(pid);  
  16.     if (!svc) {  
  17.         ERROR("untracked pid %d exited\n", pid);  
  18.         return 0;  
  19.     }  
  20.   
  21.     NOTICE("process '%s', pid %d exited\n", svc->name, pid);  
  22.   
  23.     if (!(svc->flags & SVC_ONESHOT)) {  
  24.         kill(-pid, SIGKILL);  
  25.         NOTICE("process '%s' killing any children in process group\n", svc->name);  
  26.     }  
  27.   
  28.     /* remove any sockets we may have created */  
  29.     for (si = svc->sockets; si; si = si->next) {  
  30.         char tmp[128];  
  31.         snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);  
  32.         unlink(tmp);  
  33.     }  
  34.   
  35.     svc->pid = 0;  
  36.     svc->flags &= (~SVC_RUNNING);  
  37.   
  38.         /* oneshot processes go into the disabled state on exit */  
  39.     if (svc->flags & SVC_ONESHOT) {  
  40.         svc->flags |= SVC_DISABLED;  
  41.     }  
  42.   
  43.         /* disabled processes do not get restarted automatically */  
  44.     if (svc->flags & SVC_DISABLED) {  
  45.         notify_service_state(svc->name, "stopped");  
  46.         return 0;  
  47.     }  
  48.   
  49.     now = gettime();  
  50.     if (svc->flags & SVC_CRITICAL) {  
  51.         if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {  
  52.             if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {  
  53.                 ERROR("critical process '%s' exited %d times in %d minutes; "  
  54.                       "rebooting into recovery mode\n", svc->name,  
  55.                       CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);  
  56.                 sync();  
  57.                 __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,  
  58.                          LINUX_REBOOT_CMD_RESTART2, "recovery");  
  59.                 return 0;  
  60.             }  
  61.         } else {  
  62.             svc->time_crashed = now;  
  63.             svc->nr_crashed = 1;  
  64.         }  
  65.     }  
  66.   
  67.     svc->flags |= SVC_RESTARTING;  
  68.   
  69.     /* Execute all onrestart commands for this service. */  
  70.     list_for_each(node, &svc->onrestart.commands) {  
  71.         cmd = node_to_item(node, struct command, clist);  
  72.         cmd->func(cmd->nargs, cmd->args);  
  73.     }  
  74.     notify_service_state(svc->name, "restarting");  
  75.     return 0;  
  76. }  
wait_for_one_process首先会根据关闭进程的pid查找service,进行一些相关处理,关闭一些相关资源,如果是SVC_CRITICAL类型的service,则判断重启次数和时间,超过一定限制的话进入recovery模式。

然后把service标记为SVC_RESTARTING,这样在restart_processes里面会将它重启,再执行这个service相关命令。

到这里init的任务就基本完成了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值