Part 4
// ==================================================
// Part 4
union selinux_callback cb;
cb.func_log = klog_write;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
selinux_initialize();
/* These directories were necessarily created before initial policy load
* and therefore need their security context restored to the proper value.
* This must happen before /dev is populated by ueventd.
*/
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
// ==================================================
这部分与selinux有关,会进行一些设置,并将值写入相关设备节点。
Part 5
// ==================================================
// Part 5
is_charger = !strcmp(bootmode, "charger");
// ==================================================
这部分只有一句话,就是检查bootmode,根据是不是charger模式给is_charger赋值。bootmode是在export_kernel_boot_props()函数里从属性连读取的。我猜测,这个变量的作用是判断是不是充电开机的。因为Android在关机的时候插入充电器,屏幕会点亮,并显示当前点亮,这种开机与正常开机相比,所需要支持的功能要简单得多,所以,init在后面的进程中,会判断是否是充电开机,如果是充电开机,某些过程会直接省略掉。
// ==================================================
// Part 6
INFO("property init\n");
if (!is_charger)
property_load_boot_defaults();
INFO("reading config file\n");
init_parse_config_file("/init.rc");
action_for_each_trigger("early-init", action_add_queue_tail);
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
/* execute all the boot actions to get us started */
action_for_each_trigger("init", action_add_queue_tail);
/* skip mounting filesystems in charger mode */
if (!is_charger) {
action_for_each_trigger("early-fs", action_add_queue_tail);
action_for_each_trigger("fs", action_add_queue_tail);
action_for_each_trigger("post-fs", action_add_queue_tail);
action_for_each_trigger("post-fs-data", action_add_queue_tail);
}
/* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
* wasn't ready immediately after wait_for_coldboot_done
*/
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(signal_init_action, "signal_init");
queue_builtin_action(check_startup_action, "check_startup");
if (is_charger) {
action_for_each_trigger("charger", action_add_queue_tail);
} else {
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);
}
/* run all property triggers based on current state of the properties */
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
#if BOOTCHART
queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
// ==================================================
这一部分是init进程的重头戏,我们将重点分析。
INFO("property init\n");
if (!is_charger)
property_load_boot_defaults();
void property_load_boot_defaults(void)
{
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
static void load_properties_from_file(const char *fn)
{
char *data;
unsigned sz;
data = read_file(fn, &sz);
if(data != 0) {
load_properties(data);
free(data);
}
}
static void load_properties(char *data)
{
char *key, *value, *eol, *sol, *tmp;
sol = data;
while((eol = strchr(sol, '\n'))) {
key = sol;
*eol++ = 0;
sol = eol;
value = strchr(key, '=');
if(value == 0) continue;
*value++ = 0;
while(isspace(*key)) key++;
if(*key == '#') continue;
tmp = value - 2;
while((tmp > key) && isspace(*tmp)) *tmp-- = 0;
while(isspace(*value)) value++;
tmp = eol - 2;
while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
property_set(key, value);
}
}
首先是从PROP_PATH_RAMDISK_DEFAULT指定的设备节点中读取数据,然后解析这些数据,再调用property_set()函数将数据写入property系统。
接下来解析init.rc文件。
INFO("reading config file\n");
init_parse_config_file("/init.rc");
int init_parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0);
if (!data) return -1;
parse_config(fn, data);
DUMP();
return 0;
}
static void parse_config(const char *fn, char *s)
{
struct parse_state state;
struct listnode import_list;
struct listnode *node;
char *args[INIT_PARSER_MAXARGS];
int nargs;
nargs = 0;
state.filename = fn;
state.line = 0;
state.ptr = s;
state.nexttoken = 0;
state.parse_line = parse_line_no_op;
list_init(&import_list);
state.priv = &import_list;
for (;;) {
switch (next_token(&state)) {
case T_EOF:
state.parse_line(&state, 0, 0);
goto parser_done;
case T_NEWLINE:
state.line++;
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;
}
}
parser_done:
list_for_each(node, &import_list) {
struct import *import = node_to_item(node, struct import, list);
int ret;
INFO("importing '%s'", import->filename);
ret = init_parse_config_file(import->filename);
if (ret)
ERROR("could not import file '%s' from '%s'\n",
import->filename, fn);
}
}
首先读取/init.rc这个文件,然后在parse_config()函数里面解析读到的数据。解析的过程在for循环里。
注意parse_config()里面的parser_done这个标签。list_for_each是一个宏,作用是遍历解析到的import节点。然后递归的调用init_parse_config_file()这个函数来遍历import进来的.rc文件。
之后就是一堆action_for_each_trigger()和queue_builtin_action()。
void action_for_each_trigger(const char *trigger,
void (*func)(struct action *act))
{
struct listnode *node;
struct action *act;
list_for_each(node, &action_list) {
act = node_to_item(node, struct action, alist);
if (!strcmp(act->name, trigger)) {
func(act);
}
}
}
action_for_each_trigge的作用是遍历action_list,将act的名称与指定的trigger比较,如果一致,就将act传递给func函数并执行。
在这里,就是在init.rc解析出来的action_list中查找指定的trigger,然后将这些trigger对应的act通过函数action_add_queue_tail添加到队列中:
void action_add_queue_tail(struct action *act)
{
if (list_empty(&act->qlist)) {
list_add_tail(&action_queue, &act->qlist);
}
}
void list_add_tail(struct listnode *head, struct listnode *item)
{
item->next = head;
item->prev = head->prev;
head->prev->next = item;
head->prev = item;
}
void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
{
struct action *act;
struct command *cmd;
act = calloc(1, sizeof(*act));
act->name = name;
list_init(&act->commands);
list_init(&act->qlist);
cmd = calloc(1, sizeof(*cmd));
cmd->func = func;
cmd->args[0] = name;
list_add_tail(&act->commands, &cmd->clist);
list_add_tail(&action_list, &act->alist);
action_add_queue_tail(act);
}
可以看到,这个函数的最后也是调用action_add_queue_tail函数。所以,这两个函数的作用是一样的,区别在于一个是从init.rc文件得到act,一个是从函数得到的。
在《Android框架揭秘》中,没有看到queue_builtin_action()的介绍,所以,我猜测,这是Android4.4与Android4.2有区别的地方。
至于为什么要这样做,我猜测,Google是将可以配置的act放在了init.rc中,而不可配置,必须要有的act就放在了代码中写死。
下面来分析一下queue_builtin_action中的各种act来源。
static const char *coldboot_done = "/dev/.coldboot_done";
static int wait_for_coldboot_done_action(int nargs, char **args)
{
int ret;
INFO("wait for %s\n", coldboot_done);
ret = wait_for_file(coldboot_done, COMMAND_RETRY_TIMEOUT);
if (ret)
ERROR("Timed out waiting for %s\n", coldboot_done);
return ret;
}
当系统冷启动完成之后,会创建/dev/.coldboot_done文件。这里就是等待冷启动结束,再执行下面的动作。
/*
* Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
* by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
* Does nothing if Hardware RNG is not present.
*
* Since we don't yet trust the quality of Hardware RNG, these bytes are not
* mixed into the primary pool of Linux RNG and the entropy estimate is left
* unmodified.
*
* If the HW RNG device /dev/hw_random is present, we require that at least
* 512 bytes read from it are written into Linux RNG. QA is expected to catch
* devices/configurations where these I/O operations are blocking for a long
* time. We do not reboot or halt on failures, as this is a best-effort
* attempt.
*/
static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
{
int result = -1;
int hwrandom_fd = -1;
int urandom_fd = -1;
char buf[512];
ssize_t chunk_size;
size_t total_bytes_written = 0;
hwrandom_fd = TEMP_FAILURE_RETRY(
open("/dev/hw_random", O_RDONLY | O_NOFOLLOW));
if (hwrandom_fd == -1) {
if (errno == ENOENT) {
ERROR("/dev/hw_random not found\n");
/* It's not an error to not have a Hardware RNG. */
result = 0;
} else {
ERROR("Failed to open /dev/hw_random: %s\n", strerror(errno));
}
goto ret;
}
urandom_fd = TEMP_FAILURE_RETRY(
open("/dev/urandom", O_WRONLY | O_NOFOLLOW));
if (urandom_fd == -1) {
ERROR("Failed to open /dev/urandom: %s\n", strerror(errno));
goto ret;
}
while (total_bytes_written < sizeof(buf)) {
chunk_size = TEMP_FAILURE_RETRY(
read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
if (chunk_size == -1) {
ERROR("Failed to read from /dev/hw_random: %s\n", strerror(errno));
goto ret;
} else if (chunk_size == 0) {
ERROR("Failed to read from /dev/hw_random: EOF\n");
goto ret;
}
chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
if (chunk_size == -1) {
ERROR("Failed to write to /dev/urandom: %s\n", strerror(errno));
goto ret;
}
total_bytes_written += chunk_size;
}
INFO("Mixed %d bytes from /dev/hw_random into /dev/urandom",
total_bytes_written);
result = 0;
ret:
if (hwrandom_fd != -1) {
close(hwrandom_fd);
}
if (urandom_fd != -1) {
close(urandom_fd);
}
memset(buf, 0, sizeof(buf));
return result;
}
这个函数比较复杂,好像是和随机数的生成机制有关。从硬件随机数读512个字节写入软件随机数。
static int keychord_init_action(int nargs, char **args)
{
keychord_init();
return 0;
}
void keychord_init()
{
int fd, ret;
service_for_each(add_service_keycodes);
/* nothing to do if no services require keychords */
if (!keychords)
return;
fd = open("/dev/keychord", O_RDWR);
if (fd < 0) {
ERROR("could not open /dev/keychord\n");
return;
}
fcntl(fd, F_SETFD, FD_CLOEXEC);
ret = write(fd, keychords, keychords_length);
if (ret != keychords_length) {
ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno);
close(fd);
fd = -1;
}
free(keychords);
keychords = 0;
keychord_fd = fd;
}
这个就是打开多点触控的设备节点。
static char console_name[PROP_VALUE_MAX] = "/dev/console";
static int console_init_action(int nargs, char **args)
{
int fd;
if (console[0]) {
snprintf(console_name, sizeof(console_name), "/dev/%s", console);
}
fd = open(console_name, O_RDWR);
if (fd >= 0)
have_console = 1;
close(fd);
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);
}
}
return 0;
}
这里是打开一个设备节点,然后显示INIT_IMAGE_FILE指定的开机画面(这里的开机画面是565rle格式的)。
static int property_service_init_action(int nargs, char **args)
{
/* read any property files on system or data and
* fire up the property service. This must happen
* after the ro.foo properties are set above so
* that /data/local.prop cannot interfere with them.
*/
start_property_service();
return 0;
}
void start_property_service(void)
{
int fd;
load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
load_override_properties();
/* 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系统。一次载入PROP_PATH_SYSTEM_BUILD、PROP_PATH_SYSTEM_BUILD指定的文件中的属性,覆盖的属性、persist属性。
属性值的更改仅能在init进程中进行,即一个进程若想修改属性值,必须向init进程提交申请,为此init进程生成“/dev/socket/property_service”套接字,以接受其他进程提交的申请。
static int signal_init_action(int nargs, char **args)
{
signal_init();
return 0;
}
void signal_init(void)
{
int s[2];
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = sigchld_handler;
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, 0);
/* create a signalling mechanism for the sigchld handler */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
signal_fd = s[0];
signal_recv_fd = s[1];
fcntl(s[0], F_SETFD, FD_CLOEXEC);
fcntl(s[0], F_SETFL, O_NONBLOCK);
fcntl(s[1], F_SETFD, FD_CLOEXEC);
fcntl(s[1], F_SETFL, O_NONBLOCK);
}
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))
;
}
首先,将sigchld_handler()函数注册为SIGCHLD信号处理函数。然后,创建socket,来接收信号。然后,开始处理SIGCHLD信号。
static int check_startup_action(int nargs, char **args)
{
/* make sure we actually have all the pieces we need */
if ((get_property_set_fd() < 0) ||
(get_signal_fd() < 0)) {
ERROR("init startup failure\n");
exit(1);
}
/* signal that we hit this point */
unlink("/dev/.booting");
return 0;
}
这里仅仅是做个检查。
static int queue_property_triggers_action(int nargs, char **args)
{
queue_all_property_triggers();
/* enable property triggers */
property_triggers_enabled = 1;
return 0;
}
void queue_all_property_triggers()
{
struct listnode *node;
struct action *act;
list_for_each(node, &action_list) {
act = node_to_item(node, struct action, alist);
if (!strncmp(act->name, "property:", strlen("property:"))) {
/* parse property name and value
syntax is property:<name>=<value> */
const char* name = act->name + strlen("property:");
const char* equals = strchr(name, '=');
if (equals) {
char prop_name[PROP_NAME_MAX + 1];
char value[PROP_VALUE_MAX];
int length = equals - name;
if (length > PROP_NAME_MAX) {
ERROR("property name too long in trigger %s", act->name);
} else {
memcpy(prop_name, name, length);
prop_name[length] = 0;
/* does the property exist, and match the trigger value? */
property_get(prop_name, value);
if (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*")) {
action_add_queue_tail(act);
}
}
}
}
}
}
在init.rc中定义了很多on propertychange的act,这里就是根据当前属性的状态,来触发这些act。
#if BOOTCHART
static int bootchart_init_action(int nargs, char **args)
{
bootchart_count = bootchart_init();
if (bootchart_count < 0) {
ERROR("bootcharting init failure\n");
} else if (bootchart_count > 0) {
NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS);
} else {
NOTICE("bootcharting ignored\n");
}
return 0;
}
#endif
bootchart是一个用于分析开机启动的实用程序,可以借助于它来分析开机过程中cpu的使用情况和各个进程的启动耗时。
Part 7
这一部分是init进程的事件处理循环。
// ==================================================
// Part 7
for(;;) {
int nr, i, timeout = -1;
// 在确认时间发生前,先要在action_list的命令中确认是否有尚未执行的命令,并执行之。
execute_one_command();
// 当紫禁城终止退出时,此命令用于重启或终止子进程。
restart_processes();
// 注册init进程监视的文件描述符。
if (!property_set_fd_init && get_property_set_fd() > 0) {
ufds[fd_count].fd = get_property_set_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
property_set_fd_init = 1;
}
if (!signal_fd_init && get_signal_fd() > 0) {
ufds[fd_count].fd = get_signal_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
signal_fd_init = 1;
}
if (!keychord_fd_init && get_keychord_fd() > 0) {
ufds[fd_count].fd = get_keychord_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
keychord_fd_init = 1;
}
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
if (!action_queue_empty() || cur_action)
timeout = 0;
#if BOOTCHART
if (bootchart_count > 0) {
if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
timeout = BOOTCHART_POLLING_MS;
if (bootchart_step() < 0 || --bootchart_count == 0) {
bootchart_finish();
bootchart_count = 0;
}
}
#endif
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
continue;
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()) // 处理SIGCHLD信号
handle_signal();
}
}
}
// ==================================================