转载: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__,以下是相关代码:
- int main(int argc, char **argv)
- {
- int fd_count = 0;
- struct pollfd ufds[4];
- char *tmpdev;
- char* debuggable;
- char tmp[32];
- int property_set_fd_init = 0;
- int signal_fd_init = 0;
- int keychord_fd_init = 0;
- if (!strcmp(basename(argv[0]), "ueventd"))
- return ueventd_main(argc, argv);
- /* clear the umask */
- umask(0);
- /* Get the basic filesystem setup we need put
- * together in the initramdisk on / and then we'll
- * let the rc file figure out the rest.
- */
- mkdir("/dev", 0755);
- mkdir("/proc", 0755);
- mkdir("/sys", 0755);
- mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
- mkdir("/dev/pts", 0755);
- mkdir("/dev/socket", 0755);
- mount("devpts", "/dev/pts", "devpts", 0, NULL);
- mount("proc", "/proc", "proc", 0, NULL);
- mount("sysfs", "/sys", "sysfs", 0, NULL);
- /* We must have some place other than / to create the
- * device nodes for kmsg and null, otherwise we won't
- * be able to remount / read-only later on.
- * Now that tmpfs is mounted on /dev, we can actually
- * talk to the outside world.
- */
- open_devnull_stdio();//需要在后面的程序中看到打印的话需要屏蔽这个函数
- void open_devnull_stdio(void)
- {
- int fd;
- static const char *name = "/dev/__null__";
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
- fd = open(name, O_RDWR);
- unlink(name);
- if (fd >= 0) {
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) {
- close(fd);
- }
- return;
- }
- }
- exit(1);
- }
2、初始化log系统
调用log_init对log系统进行初始化,我们看下这个函数:
- void log_init(void)
- {
- static const char *name = "/dev/__kmsg__";
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
- log_fd = open(name, O_WRONLY);
- fcntl(log_fd, F_SETFD, FD_CLOEXEC);
- unlink(name);
- }
- }
.3、解析init.rc
这里调用了parse_config去解析init.rc。
- static void parse_config(const char *fn, char *s)
- {
- struct parse_state state;
- char *args[INIT_PARSER_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]);
- if (kw_is(kw, SECTION)) {
- state.parse_line(&state, 0, 0);
- parse_new_section(&state, kw, nargs, args);
- } else {
- state.parse_line(&state, nargs, args);
- }
- nargs = 0;
- }
- break;
- case T_TEXT:
- if (nargs < INIT_PARSER_MAXARGS) {
- args[nargs++] = state.text;
- }
- break;
- }
- }
- }
调用next_token对下一个字符进行解析,这里分成了三类,文件结束,换行,还有就是字符 ,它们的处理也不一样。
1、如果是文件结束符,直接调用parse_line进行处理
2、如果是字符,则保存到args变量中
3、如果是换行,首先查关键字,如果是SECTION,说明是一个新的段的开始,先调用parse_line处理(因为第二个参数为0,其实什么也没有做),然后调用parse_new_section对这新的一个段的开始一行进行解析;否则直接调用parse_line解析。
再看一下parse_new_section
- void parse_new_section(struct parse_state *state, int kw,
- int nargs, char **args)
- {
- printf("[ %s %s ]\n", args[0],
- nargs > 1 ? args[1] : "");
- switch(kw) {
- case K_service:
- state->context = parse_service(state, nargs, args);
- if (state->context) {
- state->parse_line = parse_line_service;
- return;
- }
- break;
- case K_on:
- state->context = parse_action(state, nargs, args);
- if (state->context) {
- state->parse_line = parse_line_action;
- return;
- }
- break;
- }
- state->parse_line = parse_line_no_op;
- }
1、如果是service,则调用parse_service进行处理,并将state的parse_line设置为parse_line_service;
2、如果是On,则调用parse_action进行解析,并将state的parse_line设置为parse_line_action;
再来看一下parse_service
- static void *parse_service(struct parse_state *state, int nargs, char **args)
- {
- struct service *svc;
- if (nargs < 3) {
- parse_error(state, "services must have a name and a program\n");
- return 0;
- }
- if (!valid_name(args[1])) {
- parse_error(state, "invalid service name '%s'\n", args[1]);
- return 0;
- }
- svc = service_find_by_name(args[1]);
- if (svc) {
- parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
- return 0;
- }
- nargs -= 2;
- svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
- if (!svc) {
- parse_error(state, "out of memory\n");
- return 0;
- }
- svc->name = args[1];
- svc->classname = "default";
- memcpy(svc->args, args + 2, sizeof(char*) * nargs);
- svc->args[nargs] = 0;
- svc->nargs = nargs;
- svc->onrestart.name = "onrestart";
- list_init(&svc->onrestart.commands);
- list_add_tail(&service_list, &svc->slist);
- return svc;
- }
这里首先看这个service是否已经存在,存在说明重复了,直接返回错误,
不存在的话根据这个service的参数等分配空间,并利用传进来的参数进行初始化,最后把它加到service_list列表。
再看看parse_action
- static void *parse_action(struct parse_state *state, int nargs, char **args)
- {
- struct action *act;
- if (nargs < 2) {
- parse_error(state, "actions must have a trigger\n");
- return 0;
- }
- if (nargs > 2) {
- parse_error(state, "actions may not have extra parameters\n");
- return 0;
- }
- act = calloc(1, sizeof(*act));
- act->name = args[1];
- list_init(&act->commands);
- list_add_tail(&action_list, &act->alist);
- /* XXX add to hash */
- return act;
- }
这里主要把命令加入到action_list中。
4、添加一些其它的命令到action_queue和action_queue
主要调用queue_builtin_action把action_list中的一些命令添加到action_queue,
调用queue_builtin_action一些命令到action_list
5、进入循环处理
1)、调用execute_one_command执行一个命令
- void execute_one_command(void)
- {
- int ret;
- if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
- cur_action = action_remove_queue_head();
- cur_command = NULL;
- if (!cur_action)
- return;
- INFO("processing action %p (%s)\n", cur_action, cur_action->name);
- cur_command = get_first_command(cur_action);
- } else {
- cur_command = get_next_command(cur_action, cur_command);
- }
- if (!cur_command)
- return;
- ret = cur_command->func(cur_command->nargs, cur_command->args);
- INFO("command '%s' r=%d\n", cur_command->args[0], ret);
- }
这里主要是从action_queue的队列头弹出一个命令并调用它的func函数执行,这里的func函数 一般是do_***,在keywords.h中定义的。
2)、restart_processes
查看是否有服务需要重启,如果有重启它。
3)、把基于property、signal、keychord的句柄加入poll检测,调用poll等待事件到来。
- for (i = 0; i < fd_count; i++) {
- if (ufds[i].revents == POLLIN) {
- if (ufds[i].fd == get_property_set_fd())
- handle_property_set_fd();
- else if (ufds[i].fd == get_keychord_fd())
- handle_keychord();
- else if (ufds[i].fd == get_signal_fd())
- handle_signal();
- }
- }
分别调用相应的函数进行处理,这里稍微分析下这三个事件。
1、handle_property_set_fd
这个函数主要用来处理属性事件,包括属性设置和启动一些service,部分代码
- if(memcmp(msg.name,"ctl.",4) == 0) {
- if (check_control_perms(msg.value, cr.uid, cr.gid)) {
- handle_control_message((char*) msg.name + 4, (char*) msg.value);
- } else {
- ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n",
- msg.name + 4, msg.value, cr.uid, cr.pid);
- }
- } else {
- if (check_perms(msg.name, cr.uid, cr.gid)) {
- property_set((char*) msg.name, (char*) msg.value);
- } else {
- ERROR("sys_prop: permission denied uid:%d name:%s\n",
- cr.uid, msg.name);
- }
- }
如果是ctl开头的,说明是控制信息,启动或停止某个service
我们看在开机界面的启动,这个也是一个services,在init.rc中加进去了,但是没有启动
- service bootanim /system/bin/bootanimation
- user graphics
- group graphics
- disabled
- oneshot
然后在status_t SurfaceFlinger::readyToRun()中有如下代码:
property_set("ctl.start", "bootanim");
bootanim 就是上面 /system/bin/bootanimation 这个service的名字,
我们看到property_set最终调用了send_prop_msg,看下这个函数:
- static int send_prop_msg(prop_msg *msg)
- {
- int s;
- int r;
- s = socket_local_client(PROP_SERVICE_NAME,
- ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM);
- if(s < 0) return -1;
- while((r = send(s, msg, sizeof(prop_msg), 0)) < 0) {
- if((errno == EINTR) || (errno == EAGAIN)) continue;
- break;
- }
- if(r == sizeof(prop_msg)) {
- r = 0;
- } else {
- r = -1;
- }
- close(s);
- return r;
- }
这里把消息发给服务端,那么服务端是什么时候监听的呢,我们看下init.rc中有如下代码。
在mina函数 中有
- queue_builtin_action(property_service_init_action, "property_service_init");
这里面调用了start_property_service();我们看下它;
- void 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);
- /* Read persistent properties after all default values have been loaded. */
- load_persistent_properties();
- fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
- if(fd < 0) return;
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- fcntl(fd, F_SETFL, O_NONBLOCK);
- listen(fd, 8);
- property_set_fd = fd;
- }
- property
2、handle_keychord处理调试模式下的组合按键
3、handle_signal
- void handle_signal(void)
- {
- char tmp[32];
- /* we got a SIGCHLD - reap and restart as needed */
- read(signal_recv_fd, tmp, sizeof(tmp));
- while (!wait_for_one_process(0))
- ;
- }
handle_signal阻塞在read上,但当有子进程退出时,sigchld_handler会write(signal_fd, &s, 1);这里signal_fd与signal_recv_fd是本了已经建立好的通信套接字,导致上面的read返回,进入 wait_for_one_process
- static int wait_for_one_process(int block)
- {
- pid_t pid;
- int status;
- struct service *svc;
- struct socketinfo *si;
- time_t now;
- struct listnode *node;
- struct command *cmd;
- while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );
- if (pid <= 0) return -1;
- INFO("waitpid returned pid %d, status = %08x\n", pid, status);
- svc = service_find_by_pid(pid);
- if (!svc) {
- ERROR("untracked pid %d exited\n", pid);
- return 0;
- }
- NOTICE("process '%s', pid %d exited\n", svc->name, pid);
- if (!(svc->flags & SVC_ONESHOT)) {
- kill(-pid, SIGKILL);
- NOTICE("process '%s' killing any children in process group\n", svc->name);
- }
- /* remove any sockets we may have created */
- for (si = svc->sockets; si; si = si->next) {
- char tmp[128];
- snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
- unlink(tmp);
- }
- svc->pid = 0;
- svc->flags &= (~SVC_RUNNING);
- /* oneshot processes go into the disabled state on exit */
- if (svc->flags & SVC_ONESHOT) {
- svc->flags |= SVC_DISABLED;
- }
- /* disabled processes do not get restarted automatically */
- if (svc->flags & SVC_DISABLED) {
- notify_service_state(svc->name, "stopped");
- return 0;
- }
- now = gettime();
- 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->flags |= SVC_RESTARTING;
- /* Execute all onrestart commands for this service. */
- list_for_each(node, &svc->onrestart.commands) {
- cmd = node_to_item(node, struct command, clist);
- cmd->func(cmd->nargs, cmd->args);
- }
- notify_service_state(svc->name, "restarting");
- return 0;
- }
然后把service标记为SVC_RESTARTING,这样在restart_processes里面会将它重启,再执行这个service相关命令。
到这里init的任务就基本完成了。