深入理解init
2015-05-08 11:13
78 查看
深入理解init(基于Android5.0代码分析)
1.概述
init进程是Linux系统中用户空间的第一个进程,由于Android也是基于Linux内核的,因此init也是Android系统中用户空间的第一个进程,进程号为1。init进程是被kernel启动的。
2.init.c(system/core/init/init.c)主函数分析
3.init.rc文件
init.rc是一个文本文件,可认为它是Android系统启动脚本。init.rc文件中定义了环境变量配置、系统进程启动、分区挂载、属性配置等诸多内容。init.rc有特殊的语法,可参考init目录下的readme.txt。
init.rc(system/core/rootdir/init.rc)部分代码片段
init.rc可以定义两类结构:Actions与Services。
Actions是一组命令的集合,定义一个Actions如下,每个Actions都可定义一个触发器(trigger),Actions格式如下:
on <trigger>
<command>
<command>
<command>
其中<command>类似于shell命令,command对应一个函数,通常执行一个动作,比如创建文件夹。
Command的格式如下
command-name <parameter1>[parameter2... ]
init.rc中所有的command都在keywords.h中定义过。
触发器<trigger>其实就是一段字符串
根据trigger的不同可以将Actions分为两类:
(1)普通型
这类trigger的的作用仅仅是用于给一个Actions命名,方便查找和引用。如 early-init、init、late-init、early-fs、fs、post-fs、post-fs-data、early-boot、boot、charger等。
一般来说,这类Actions将在Android启动时执行,其trigger暗示了执行对应Actions执行的时机。具体的执行流程将在本文最后介绍。
此外,根据readme.txt描述,还有其他几种trigger,但在init.rc以及init源代码中却没有找到相关代码,如下所示,
device-added-<path>
device-removed-<path>
设备节点被添加或移除时调用,。
service-exited-<name>
这类trigger将在某service退出时执行。关于什么是service稍后介绍。
(2)属性型
其trigger为property:<name>=<value>。其trigger不仅唯一标示了此类action的,同时也设定了此类action执行的条件,即name=value。
Service
在init.rc中一个Service对应一个可执行程序,并且可以设定该程序的一些执行性质,如仅仅执行一次、或退出时自动重启。当Service所代表的可执行程序在退出时自动重启时,该Service通常意味着这是一个守护进程,比如zygote。
Service格式如下:
Service <name> <pathname> [<parameter>]*
<option>
<option>
<option>
...
<name>是这个Service代表的可执行程序的名字
<pathname>可执行程序对应的路径
<paramter>参数列表
<option>service的属性,这些属性也在keywords.h中定义了
class属性,具有同样class的Service构成一个组,可以在Action中通过class_start、class_stop、class_reset等命令启动、停止、重新启动。
比如zygote这个可执行程序有自动重启这个属性,因此zygote就是一个守护进程
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
4.解析init.rc文件
-->init_paser.c
上面代码就是parse_config函数,代码虽短但是却比较复杂。从整体上说,parse_config首先会找到配置文件的的一个section,然后针对不同的section进行不同的解析。
4.1 解析service
下面就是一个zygote service
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
解析service的入口函数如下:
其中,解析service主要用到了parse_service和parse_line_service两个函数。
4.1.1 service结构体
init中用到了一个叫service的结构体来保存service section相关信息,这个结构体如下:
我们现在了解的service结构体,相对来说还算是清晰易懂的。而zygote中的那三个onrestart该怎么描述呢。请看service中使用的这个action结构体:
现在再来看上面的两个解析service时需要的两个函数:parse_service和parse_line_service。
parse_service只是搭建了一个service的架子,具体内容还得由后面函数来填充。
5.属性服务
我们知道windows下可以使用注册表来存储一些键值对,因此系统或应用程序可以把自己的一些属性存储到注册表中去,这样即使系统重启或者应用程序重启还能根据注册表中的内容进行相应初始化。
Android平台也提供了一个类似的机制,称之为属性服务(property service)。应用程序可以通过这个属性服务机制查询或者设置属性。
那么这个属性服务是怎么实现的呢?其中init.c和属性服务相关的代码有下面几行:
property_init()
property_load_boot_defaults();
start_property_service()
上面分析了客户端可以通过共享内存空间获取属性值,那么客户端怎么设置属性值呢?只能通过与属性服务器进行交互来设置属性。
属性服务器已经了解完了,那么怎么是怎么请求设置属性的呢?
客户端通过propert_set发送请求,property_set由libcutils库提供。
总结:init进程主要做了两件事,一是创建系统中几个关键进程,比如zygote。二是创建系统的属性服务以及启动属性服务。
1.概述
init进程是Linux系统中用户空间的第一个进程,由于Android也是基于Linux内核的,因此init也是Android系统中用户空间的第一个进程,进程号为1。init进程是被kernel启动的。
2.init.c(system/core/init/init.c)主函数分析
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; bool is_charger = false; #ifdef MTK_INIT int mt_boot_mode = 0; klog_set_level(6); #endif //通过命令行判断argv[0]的字符串内容,来区分当前程序是init,ueventd或是watchdogd。 if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); if (!strcmp(basename(argv[0]), "watchdogd")) return watchdogd_main(argc, argv); //设定当前进程(即/init)的文件模型创建掩码(file mode creation mask) /* 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); //虚拟内存文件系统,该文件系统被挂载到/dev目录下,主要存放设备节点文件,用户进程可以通过访问/dev下的设备节点文件与硬件驱动程序交互 mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"); mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); //devpts一种虚拟终端文件系统 mount("devpts", "/dev/pts", "devpts", 0, NULL); //proc,虚拟文件系统,被挂载到/proc目录下,通过该文件系统可与内核数据结构交互,查看以及设定内核参数 mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); #ifdef INIT_ENG_BUILD mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL); #endif /* indicate that booting is in progress to background fw loaders, etc */ close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000)); /* 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. */ //将init进程的stdio重定向到dev/null设备 open_devnull_stdio(); klog_init(); //初始化Android属性系统,这是init进程需要完成的功能之一 property_init(); //通过读取/proc/cupinfo获取硬件信息 get_hardware_name(hardware, &revision); //解析内核启动参数 process_kernel_cmdline(); //主要是SELinux的初始化 union selinux_callback cb; cb.func_log = log_callback; 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"); //判断是不是以充电模式启动 is_charger = !strcmp(bootmode, "charger"); //打印一条log语句,此宏定义在init/log.h中 INFO("property init\n"); property_load_boot_defaults(); #ifndef INIT_ENG_BUILD property_set("ro.mtprof.disable", "1"); #endif INFO("reading config file\n"); //下面一部分主要是解析init.rc文件,init.rc详细信息请看下面 #ifdef MTK_INIT /* NEW FEATURE: multi-boot mode */ mt_boot_mode = get_boot_mode(); if ( (mt_boot_mode == MT_FACTORY_BOOT) || (mt_boot_mode == MT_ATE_FACTORY_BOOT) ) { printf("Factory Mode Booting.....\n"); property_set("sys.mtk.no.factoryimage","1"); init_parse_config_file("/factory_init.rc"); init_parse_config_file("/factory_init.project.rc"); } else if ( mt_boot_mode == MT_META_BOOT ) { printf("META Mode Booting.....\n"); init_parse_config_file("/meta_init.rc"); init_parse_config_file("/meta_init.project.rc"); } else { #endif // MTK_INIT init_parse_config_file("/init.rc"); #ifdef MTK_INIT } #endif #ifdef MTK_INIT if ( (mt_boot_mode == MT_FACTORY_BOOT) || (mt_boot_mode == MT_ATE_FACTORY_BOOT) ) { NOTICE("No need modem.rc for factory mode\n"); } else if ( mt_boot_mode == MT_META_BOOT ) { init_parse_config_file("/meta_init.modem.rc"); }else { init_parse_config_file("/init.modem.rc"); } #endif // MTK_INIT /**** End of Parsing .rc files ****/ //解析完配置文件,会得到一系列的action,下面代码将会执行init.rc中的early-init阶段的动作 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"); //执行init.rc中的init阶段动作 /* execute all the boot actions to get us started */ action_for_each_trigger("init", 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"); /* Don't mount filesystems or start core system services if in charger mode. */ if (is_charger) { action_for_each_trigger("charger", action_add_queue_tail); } else { action_for_each_trigger("late-init", 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 for(;;) { int nr, i, timeout = -1; execute_one_command(); restart_processes(); 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 //调用poll等待一些事情的发生 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()) handle_signal(); } } } return 0; }
3.init.rc文件
init.rc是一个文本文件,可认为它是Android系统启动脚本。init.rc文件中定义了环境变量配置、系统进程启动、分区挂载、属性配置等诸多内容。init.rc有特殊的语法,可参考init目录下的readme.txt。
init.rc(system/core/rootdir/init.rc)部分代码片段
import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc //5.0在这里开始区分32位和64位 import /init.${ro.zygote}.rc import /init.trace.rc on early-init start ueventd on init sysclktz 0 loglevel 3 mkdir /system mkdir /data 0771 system system write /proc/sys/kernel/panic_on_oops 1 # Load properties from /system/ + /factory after fs mount. on load_all_props_action load_all_props on post-fs # once everything is setup, no need to modify / mount rootfs rootfs / ro remount on boot # basic network init ifup lo class_start core class_start main on property:vold.decrypt=trigger_reset_main class_reset main service ueventd /sbin/ueventd class core critical on property:ro.debuggable=1 start console service debuggerd /system/bin/debuggerd class main service bootanim /system/bin/bootanimation class main user graphics group graphics disabled oneshot
init.rc可以定义两类结构:Actions与Services。
Actions是一组命令的集合,定义一个Actions如下,每个Actions都可定义一个触发器(trigger),Actions格式如下:
on <trigger>
<command>
<command>
<command>
其中<command>类似于shell命令,command对应一个函数,通常执行一个动作,比如创建文件夹。
Command的格式如下
command-name <parameter1>[parameter2... ]
init.rc中所有的command都在keywords.h中定义过。
触发器<trigger>其实就是一段字符串
根据trigger的不同可以将Actions分为两类:
(1)普通型
这类trigger的的作用仅仅是用于给一个Actions命名,方便查找和引用。如 early-init、init、late-init、early-fs、fs、post-fs、post-fs-data、early-boot、boot、charger等。
一般来说,这类Actions将在Android启动时执行,其trigger暗示了执行对应Actions执行的时机。具体的执行流程将在本文最后介绍。
此外,根据readme.txt描述,还有其他几种trigger,但在init.rc以及init源代码中却没有找到相关代码,如下所示,
device-added-<path>
device-removed-<path>
设备节点被添加或移除时调用,。
service-exited-<name>
这类trigger将在某service退出时执行。关于什么是service稍后介绍。
(2)属性型
其trigger为property:<name>=<value>。其trigger不仅唯一标示了此类action的,同时也设定了此类action执行的条件,即name=value。
Service
在init.rc中一个Service对应一个可执行程序,并且可以设定该程序的一些执行性质,如仅仅执行一次、或退出时自动重启。当Service所代表的可执行程序在退出时自动重启时,该Service通常意味着这是一个守护进程,比如zygote。
Service格式如下:
Service <name> <pathname> [<parameter>]*
<option>
<option>
<option>
...
<name>是这个Service代表的可执行程序的名字
<pathname>可执行程序对应的路径
<paramter>参数列表
<option>service的属性,这些属性也在keywords.h中定义了
class属性,具有同样class的Service构成一个组,可以在Action中通过class_start、class_stop、class_reset等命令启动、停止、重新启动。
比如zygote这个可执行程序有自动重启这个属性,因此zygote就是一个守护进程
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
4.解析init.rc文件
-->init_paser.c
int init_parse_config_file(const char *fn) { char *data; data = read_file(fn, 0);//读取配置文件内容 if (!data) return -1; parse_config(fn, data);//调用paser_config做真正的解析 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; #ifdef MTK_INIT NOTICE("<<Parsing %s>>\n", fn); #endif 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]); //判断关键字类型是不是SECTION if (kw_is(kw, SECTION)) { state.parse_line(&state, 0, 0); //解析这个SECTION 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); } }
上面代码就是parse_config函数,代码虽短但是却比较复杂。从整体上说,parse_config首先会找到配置文件的的一个section,然后针对不同的section进行不同的解析。
4.1 解析service
下面就是一个zygote service
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
解析service的入口函数如下:
//针对不同的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) { //解析service case K_service: state->context = parse_service(state, nargs, args); if (state->context) { state->parse_line = parse_line_service; return; } break; //解析on case K_on: state->context = parse_action(state, nargs, args); if (state->context) { state->parse_line = parse_line_action; return; } break; //解析import case K_import: parse_import(state, nargs, args); break; } state->parse_line = parse_line_no_op; }
其中,解析service主要用到了parse_service和parse_line_service两个函数。
4.1.1 service结构体
init中用到了一个叫service的结构体来保存service section相关信息,这个结构体如下:
struct service { //listnode 是一个特殊的结构体,主要用来将结构体链接成一个双向链表。init中有一个全局service_list,专门用来保存解析init.rc后得到的service /* list of all services */ struct listnode slist; const char *name; //service的名称 const char *classname; //service所属的class名称 unsigned flags; //service的属性 pid_t pid; //进程号 //上一次启动的时间 time_t time_started; /* time of last start */ //上一次死亡的时间 time_t time_crashed; /* first crash within inspection window */ //死亡的次数 int nr_crashed; /* number of times crashed within window */ uid_t uid; gid_t gid; gid_t supp_gids[NR_SVC_SUPP_GIDS]; size_t nr_supp_gids; char *seclabel; //有些service需要使用socket,下面这个socketinfo用来描述socket的相关信息, struct socketinfo *sockets; //service一般创建在一个单独的进程中,envvars用来描述创建这个进程所需要的环境变量信息 struct svcenvinfo *envvars; struct action onrestart; /* Actions to execute on restart. */ /* keycodes for triggering this service via /dev/keychord */ int *keycodes; int nkeycodes; int keychord_id; int ioprio_class; int ioprio_pri; int nargs; /* "MUST BE AT THE END OF THE STRUCT" */ char *args[1]; };
我们现在了解的service结构体,相对来说还算是清晰易懂的。而zygote中的那三个onrestart该怎么描述呢。请看service中使用的这个action结构体:
struct action { /* node in list of all actions */ struct listnode alist; //用于存储所有的action /* node in the queue of pending actions */ struct listnode qlist; //链接那些等待执行的action /* node in list of actions for a trigger */ struct listnode tlist; //链接那些某些条件满足后需要执行的action unsigned hash; const char *name; //这个option对应的是command链表,以zygote为例,它有三个onrestart option,所以他会对应创建三个command结构体 struct listnode commands; struct command *current; };
现在再来看上面的两个解析service时需要的两个函数:parse_service和parse_line_service。
-->parse_service static void *parse_service(struct parse_state *state, int nargs, char **args) { //声明一个service结构体 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; } //init维护一个全局的service链表,先判断是否有重名链表 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]; //设置classname为default 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); //把service加到全局链表service_list中 list_add_tail(&service_list, &svc->slist); return svc; }
parse_service只是搭建了一个service的架子,具体内容还得由后面函数来填充。
-->parse_line_service static void parse_line_service(struct parse_state *state, int nargs, char **args) { struct service *svc = state->context; struct command *cmd; int i, kw, kw_nargs; if (nargs == 0) { return; } svc->ioprio_class = IoSchedClass_NONE; kw = lookup_keyword(args[0]); //还是根据关键字进行不同的处理 switch (kw) { case K_capability: break; case K_class: if (nargs != 2) { parse_error(state, "class option requires a classname\n"); } else { svc->classname = args[1]; } break; case K_oneshot: /* 这是service的属性,它一共有五个属性,分别为: SVC_DISABLED:不随着class自动启动 SVC_ONESHOT:退出后不需要重启,也就是说service只启动一次就可以了 SVC_RUNNING:正在运行 SVC_RESTARTING:等待重启 SVC_CONSOLE:该service需要使用控制台 SVC_CRITICAL:如果在规定的时间内,该service不断重启,则系统会重启并进入恢复模式 */ svc->flags |= SVC_ONESHOT; break; //根据onrestart的内容,填充action结构体 case K_onrestart: nargs--; args++; kw = lookup_keyword(args[0]); if (!kw_is(kw, COMMAND)) { parse_error(state, "invalid command '%s'\n", args[0]); break; } kw_nargs = kw_nargs(kw); if (nargs < kw_nargs) { parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, kw_nargs > 2 ? "arguments" : "argument"); break; } cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); cmd->func = kw_func(kw); cmd->nargs = nargs; memcpy(cmd->args, args, sizeof(char*) * nargs); list_add_tail(&svc->onrestart.commands, &cmd->clist); break; } //创建socket相关信息 case K_socket: {/* name type perm [ uid gid ] */ struct socketinfo *si; if (nargs < 4) { parse_error(state, "socket option requires name, type, perm arguments\n"); break; } if (strcmp(args[2],"dgram") && strcmp(args[2],"stream") && strcmp(args[2],"seqpacket")) { parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n"); break; } si = calloc(1, sizeof(*si)); if (!si) { parse_error(state, "out of memory\n"); break; } si->name = args[1]; si->type = args[2]; si->perm = strtoul(args[3], 0, 8); if (nargs > 4) si->uid = decode_uid(args[4]); if (nargs > 5) si->gid = decode_uid(args[5]); si->next = svc->sockets; svc->sockets = si; break; } case K_user: if (nargs != 2) { parse_error(state, "user option requires a user id\n"); } else { svc->uid = decode_uid(args[1]); } break; case K_seclabel: if (nargs != 2) { parse_error(state, "seclabel option requires a label string\n"); } else { svc->seclabel = args[1]; } break; default: parse_error(state, "invalid option '%s'\n", args[0]); } }
5.属性服务
我们知道windows下可以使用注册表来存储一些键值对,因此系统或应用程序可以把自己的一些属性存储到注册表中去,这样即使系统重启或者应用程序重启还能根据注册表中的内容进行相应初始化。
Android平台也提供了一个类似的机制,称之为属性服务(property service)。应用程序可以通过这个属性服务机制查询或者设置属性。
那么这个属性服务是怎么实现的呢?其中init.c和属性服务相关的代码有下面几行:
property_init()
property_load_boot_defaults();
start_property_service()
-->property_init() void property_init(void) { //初始化属性存储区域 init_property_area(); } ->init_property_area() static int init_property_area(void) { if (property_area_inited) return -1; if(__system_property_area_init()) return -1; //创建一片共享内存,这样虽然这片区域是由init进程启动的,但是其他进程也能读取这块内存里的东西 if(init_workspace(&pa_workspace, 0)) return -1; fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); property_area_inited = 1; return 0; } -->property_load_boot_defaults() void property_load_boot_defaults(void) { //加载default.prop文件 load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL); }
上面分析了客户端可以通过共享内存空间获取属性值,那么客户端怎么设置属性值呢?只能通过与属性服务器进行交互来设置属性。
-->start_property_service() void start_property_service(void) { int fd; /* 属性服务创建了一个用来接收请求的socket,可这个请求在哪里被处理呢?其实在init的for循环中已经进行相应处理了。 */ fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL); if(fd < 0) return; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK); listen(fd, 8); property_set_fd = fd; } 当属性服务器接收到客户端请求设置属性时,init会调用handle_property_set_fd()进行处理。 -->handle_property_set_fd() void handle_property_set_fd() { prop_msg msg; int s; int r; int res; struct ucred cr; struct sockaddr_un addr; socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); char * source_ctx = NULL; struct pollfd ufds[1]; const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */ int nr; //先接收tcp连接 if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; } //取出客户端的权限等属性 /* Check socket options here */ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); ERROR("Unable to receive socket options\n"); return; } ufds[0].fd = s; ufds[0].events = POLLIN; ufds[0].revents = 0; nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms)); if (nr == 0) { ERROR("sys_prop: timeout waiting for pid=%d uid=%d gid=%d to send property message.\n", cr.pid, cr.uid, cr.gid); close(s); return; } else if (nr < 0) { ERROR("sys_prop: error waiting for pid=%d uid=%d gid=%d to send property message. err=%d %s\n", cr.pid, cr.uid, cr.gid, errno, strerror(errno)); close(s); return; } //接收请求数据 r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT)); if(r != sizeof(prop_msg)) { ERROR("sys_prop: mis-match msg size received: %d from pid=%d uid=%d gid=%d expected: %zu errno: %d\n", r, cr.pid, cr.uid, cr.gid, sizeof(prop_msg), errno); close(s); return; } switch(msg.cmd) { case PROP_MSG_SETPROP: msg.name[PROP_NAME_MAX-1] = 0; msg.value[PROP_VALUE_MAX-1] = 0; if (!is_legal_property_name(msg.name, strlen(msg.name))) { ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name); close(s); return; } getpeercon(s, &source_ctx); if(memcmp(msg.name,"ctl.",4) == 0) { // Keep the old close-socket-early behavior when handling // ctl.* properties. close(s); if (check_control_mac_perms(msg.value, source_ctx)) { handle_control_message((char*) msg.name + 4, (char*) msg.value); } else { ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n", msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid); } } else { //检查客户端是否有足够权限,如果有则调用property_set设置属性 if (check_perms(msg.name, source_ctx)) { property_set((char*) msg.name, (char*) msg.value); } else { ERROR("sys_prop: permission denied uid:%d name:%s\n", cr.uid, msg.name); } // Note: bionic's property client code assumes that the // property server will not close the socket until *AFTER* // the property is written to memory. close(s); } freecon(source_ctx); break; default: close(s); break; } } 接下来就是调用property_set进行属性设置 -->property_set() int property_set(const char *name, const char *value) { prop_info *pi; int ret; size_t namelen = strlen(name); size_t valuelen = strlen(value); if (!is_legal_property_name(name, namelen)) { ERROR("PropSet Error:[%s:%s] property name is illegal\n", name, value); return -1; } if (valuelen >= PROP_VALUE_MAX) { ERROR("PropSet Error:[%s:%s] valuelen %d >= %d\n", name, value, valuelen, PROP_VALUE_MAX); return -1; } //从属性存储空间中寻找是否已经存在该属性了 pi = (prop_info*) __system_property_find(name); if(pi != 0) { //如果属性名是以ro.开头,则表示不能被设置,直接返回 /* ro.* properties may NEVER be modified once set */ if(!strncmp(name, "ro.", 3)) { return -1; } //更新改属性值 __system_property_update(pi, value, valuelen); } else { //如果没有找到对应的属性,则认为需要增加该属性,所以需要创建一项。注意Android最多支持247项属性。 #ifdef INIT_ENG_BUILD prop_area *pa = __system_property_area__; if ((pa->bytes_used > PA_SIZE_ERR) && (0 == alarmed )) { alarmed = 1; ERROR("[Property Error]: Unable to set [%s:%s] property limit has arrived: %d\n", name, value, pa->bytes_used); #ifdef MTK_INIT property_show(); #endif } else if ((pa->bytes_used > PA_SIZE_WARN) && (0 == warned )) { warned = 1; NOTICE("[Property Warning]: limit would be arrived:%d (Max:%d). " "Use getprop to review your properties!\n", pa->bytes_used, PA_SIZE); #ifdef MTK_INIT property_show(); #endif } #endif ret = __system_property_add(name, namelen, value, valuelen); if (ret < 0) { return ret; } } //下面就是一些特殊属性需要设置 /* If name starts with "net." treat as a DNS property. */ if (strncmp("net.", name, strlen("net.")) == 0) { if (strcmp("net.change", name) == 0) { return 0; } /* * The 'net.change' property is a special property used track when any * 'net.*' property name is updated. It is _ONLY_ updated here. Its value * contains the last updated 'net.*' property. */ property_set("net.change", name); } else if (persistent_properties_loaded && strncmp("persist.", name, strlen("persist.")) == 0) { /* * Don't write properties to disk until after we have read all default properties * to prevent them from being overwritten by default values. */ write_persistent_property(name, value); } else if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) { selinux_reload_policy(); } property_changed(name, value); return 0; }
属性服务器已经了解完了,那么怎么是怎么请求设置属性的呢?
客户端通过propert_set发送请求,property_set由libcutils库提供。
总结:init进程主要做了两件事,一是创建系统中几个关键进程,比如zygote。二是创建系统的属性服务以及启动属性服务。
相关文章推荐
- 深入理解jvm--Java中init和clinit区别完全解析
- csdn 深入理解Android》导读之init
- 第3章 深入理解init
- [深入理解Android卷一全文-第三章]深入理解init
- 深入理解Python中的 __new__ 和 __init__及区别介绍
- 深入理解Android(03)——深入理解init初始化函数
- 《深入理解Android 卷1》读书笔记 (一)—— Android Init之zygote restart
- [深入理解Android卷一全文-第三章]深入理解init
- 《深入理解Android 卷1》读书笔记 (一)—— Android Init之属性服务 (property_service)
- [深入理解Android卷一全文-第三章]深入理解init
- [深入理解Android卷一全文-第三章]深入理解init
- 深入理解init_5-----属性服务(基于Android 2.2,代码源自Google)
- 深入理解 Android 卷I - 第3章 深入理解init
- 《深入理解Android(卷1)》笔记 3.第三章 深入理解init
- 深入理解 Android 卷I - 第3章 深入理解init
- 深入理解init_4``````````init控制service(基于Android2.2,代码源自Google)
- [深入理解Android卷一全文-第三章]深入理解init
- 深入理解Android之init与zygote
- 深入理解init_3 --------- 解析Zygote 的service(基于源码2.2,代码源自Google)
- 深入理解init_1----init分析(基于Android 2.2,源码来自Google)