每当我们学习一门新的语言时,我们总是以HelloWorld来开始我们的学习之旅,每当我们分析一个应用程序时,我们也总会找到main函数入口,学习一个系统,最好的办法也是先把它的启动流程弄清楚,这样,后面分析起来才能更好的把握。
android系统比较复杂,它的启动涉及的东西也比较多,但是只要我们把一点点都弄明白 ,最后整个流程起来再看的话也就会明了很多,我们先看下init启动过程中主要做的事。
Android从Linux系统启动有4个步骤:
(1)、init进程启动
(2)、Native服务启动
(3)、System Server、Android服务启动
(4)、Home启动
总体启动框架图如:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLyEDOz8VM4gDN2QzMzMTMvw1Mw8CX0AjMxAjMvw1ckF2bsBXdvwFdl5mLuR2cj5Set1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
首先我们看下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();//需要在后面的程序中看到打印的话需要屏蔽这个函数
我们再看下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);
}
这里调用dup函数把标准输入,输出,错误输出都重定位到/dev/__null__
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);
}
}
这里创建并打开了一个设备节点,用于后面的log信息处理,如写日志等就往这个节点里面写.
.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;
}
这里也是对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
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
对的,就是在这里监听的。这样有属性设置或启动相关service时就可以通过socket通信来 实现了。
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;
}
wait_for_one_process首先会根据关闭进程的pid查找service,进行一些相关处理,关闭一些相关资源,如果是SVC_CRITICAL类型的service,则判断重启次数和时间,超过一定限制的话进入recovery模式。
然后把service标记为SVC_RESTARTING,这样在restart_processes里面会将它重启,再执行这个service相关命令。
到这里init的任务就基本完成了。