您的位置:首页 > 移动开发 > Android开发

Android5.0开关机模块——init进程(init.rc、property_service)

2015-03-10 14:59 423 查看
上一篇最后提到init进程的轮询机制,init进程主要轮询property、signal、组合键等,这里探究一下这几个内容是如何实现的。

要说明这一部分内容,必须先来分析一下init.rc的语法问题

import /init.trace.rc 前文说过,就是在解析完init.rc后把其他XX.rc的内容也解析出来

on early-init

# Set init and its forked children's oom_adj.

write /proc/1/oom_adj -16

# Set the security context for the init process.

# This should occur before anything else (e.g. ueventd) is started.

setcon u:r:init:s0

start ueventd

on关键字对应的就是一个action,一个action里会对应一些command,这个action会被放到action_queue中,里面的command最终会由execute_one_command()执行

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

class main

socket zygote stream 660 root system

onrestart write /sys/android_power/request_state wake

onrestart write /sys/power/state on

onrestart restart media

onrestart restart netd

onrestart setprop ro.yulong.ylcrash syscrash

onrestart start crash_and_reset

service会被放进service_list队列中,每个service下面都对应很多的option,class main表示这个service属于main这一组,当执行命令class_start main时,所有main组里的 service都会被执行;onrestart是指当这个service死掉后,执行onrestart后面的命令

service的执行是调用init.c中的service_start来执行的,在service_start中,用fork() + execve()来创建子进程并执行service指定的可执行文件来终于启动了service

没启动一个service就会创建一个子进程。

on property:vold.decrypt=trigger_reset_main

class_reset main

当系统属性的判断成立时,执行下面的命令

上面是init.rc的一些简单语法,下面结合init.rc这些语法特点来分析前面的轮询问题

在init进程里面,init进程会通过捕获SIGCHLD得知其子进程的死亡,并据情况决定是否重启之。记住,在Linux的世界里,在子进程死亡的时候,父进程会收到一个SIGCHLD信号,而后父进程通过wait()系统调用,清理子进程僵尸。大家都知道,一个进程死亡的时候,如果它还有子进程,典型地,子进程会被托孤给init进程,这种情况非常普遍,所以任何一个Linux系统,它的init程序,少不了要做一件事情,就是反复通过wait()清理僵尸,否则Linux系统就会尸横遍野,整个一部生化危机啊有木有?Linux是个怎样残酷的世界啊,我艰于呼吸视听啊,哪里还能有什么言语?永远都是白发人送黑发人,父进程清理子进程。——网络牛人

get_signal_fd是前面提到的一种轮询,它的主要作用就是接收死亡进程的消息。比如,当zygote死亡后,会给init发信号,会触发init进程执行handle_signal()

void handle_signal(void)
{<pre name="code" class="cpp">static int wait_for_one_process(int block)
{
…

while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); // 干掉僵尸

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);

…

/* 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) {
…
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) { //执行onrestart里面的命令
cmd = node_to_item(node, struct command, clist);
cmd->func(cmd->nargs, cmd->args);
}
notify_service_state(svc->name, "restarting");
return 0;
}


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)) ;}


1、无主僵尸

挂的时候有“untracked pid %d exited”打印的进程属于init的子进程,但是并没有在 init.rc里面注册service。典型地,那些先父仙游后被托孤给init的进程!

2、不投胎的僵尸

有oneshot标志的服务,死亡以后,只能手动重启

servicebootsound /system/bin/playmp3
user media
group audio
oneshot
3、可以重启的僵尸

这类进程就爽了,因为被“svc->flags |= SVC_RESTARTING”盖章了,死了会重新投胎, 下次init执行到restart_processes()的时候就可以重启之

4、重要进程

如果一个service是critical的,而它又在短时间内反复挂,restart后又总是夭折,我们很可能不能再让它这么痛苦下去了,唯一的方法就是让它解脱,于是整个系统重启。这样的service包括:

service ueventd /sbin/ueventd
critical

service servicemanager /system/bin/servicemanager
user system
critical
onrestart restart zygote
onrestart restart media


下面说下系统属性修改的过程

设置property,有C/S架构组成。客户端的程序位于properties.c,服务端的程序位于property_service.c。

前文提到过,

queue_builtin_action(property_service_init_action, "property_service_init"); 和 queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");

这两个函数只能算是初始化服务器端的作用

建了一个socket,其名为ANDROID_SOCKET_DIR"/PROP_SERVICE_NAME",也即 "/dev/socket/property_service“

然后调用listen监听这个socket,并将创建的socket描述符赋值给全局变量property_set_fd

当用户执行setprop时,调用的其实是properties.c中的property_set函数,该函数会发送socket消息,最终调用到property_service.c中的handle_property_set_fd(),然后调用是property_set,然后是property_changed,在property_changed中会调用queue_property_triggers()来把对应的action放入queue中,这样,就可以执行到这个action中的命令了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: