編輯:關於Android編程
在前面的文章分析完init.rc的解析以後,我們知道現在action按照init.c的main函數中的秩序,維護了一條qlist的鏈表,listnode為action_qlist service也維護了一條鏈表,listnode為service_list。 那麼,在android的啟動過程中,action和service是如何被啟動的呢? 我們接下來再回到我們一直分析的init.c的main函數中來看一下。 在main函數中,完成前面的解析工作之後,我們分析到了一個死循環,來看一下:
for(;;) {
int nr, i, timeout = -1;
execute_one_command();
restart_processes();
.....
}
這個for循環的處理有很多,但是我們現在暫時關心的只有這短短的兩個函數。
execute_one_command();
restart_processes();
首先我們來分析execute_one_command函數,看著個函數的名字,我們就能明白這個函數的功能。 這個函數就是去執行一個command嘛。。 來看一下這個函數的實現:
void execute_one_command(void)
{
int ret, i;
char cmd_str[256] = ;
if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) { // 在第一次的啟動中,因為都是NULL,所以肯定可以進入這個判斷,如果不是第一次的話,因為得到cur_action或者cur_command都是null,並且如果這個command是當前action的最後一個command的話,會進入到下面的這個判斷。
cur_action = action_remove_queue_head(); // 依次獲取action從action_queue中
cur_command = NULL; // 在獲取新的action之後,將cur_command設置為null
if (!cur_action) // 如果沒有action了,則返回
return;
INFO(processing action %p (%s)
, cur_action, cur_action->name);
cur_command = get_first_command(cur_action); // 如果是一個新的action的話,會執行到這一步去獲得first command
} else {
cur_command = get_next_command(cur_action, cur_command); // 仍然在action的內部鏈表中,如果仍然存在沒有被獲取到的command的話,則會去獲得下一個command。
}
if (!cur_command) // 如果獲取到的command為空的話,會返回,反之,繼續
return;
ret = cur_command->func(cur_command->nargs, cur_command->args); // 會調用這個command的func區執行,執行的參數個數為nargs,命令為args
if (klog_get_level() >= KLOG_INFO_LEVEL) { // Log的打印
for (i = 0; i < cur_command->nargs; i++) {
strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));
if (i < cur_command->nargs - 1) {
strlcat(cmd_str, , sizeof(cmd_str));
}
}
INFO(command '%s' action=%s status=%d (%s:%d)
,
cmd_str, cur_action ? cur_action->name : , ret, cur_command->filename,
cur_command->line);
}
}
其實這個邏輯是比較好理解的,我們要著重分析的僅僅是如何獲取action以及command. 來看一下action_remove_queue_head這個函數:
struct action *action_remove_queue_head(void)
{
if (list_empty(&action_queue)) { // 首先我們去判斷當前待執行的action是否已經為null,即是否還有action沒有被執行
return 0;
} else {
struct listnode *node = list_head(&action_queue); // 如果仍然有未被執行的隊列的話,就將node指向現在action_queue的頭指針
struct action *act = node_to_item(node, struct action, qlist); // 取出action
list_remove(node); // 將這個節點從整個action _queue的列表中刪除
list_init(node); // 刪除這個節點後,為了安全起見,將node自己指向自己,以避免出現野指針。
return act; // 返回已經查找到的action
}
}
我們可以看到,其實是從action_queue中拿每一個結構體的。 在拿到action之後呢?就要從action裡面去拿command了。 來看一下下面的這兩個函數:
static struct command *get_first_command(struct action *act) // 從一個actoin裡面尋找其第一個command,所以只用傳遞action即可
{
struct listnode *node;
node = list_head(&act->commands); // 將node指向action的commands的結構體
if (!node || list_empty(&act->commands)) // 如果這個節點不存在,或者這個action的commands結構體為空,則返回null
return NULL;
return node_to_item(node, struct command, clist); // 返回第一個節點
}
static struct command *get_next_command(struct action *act, struct command *cmd) // 返回當前commands的下一個command
{
struct listnode *node;
node = cmd->clist.next; 指針向後移動next
if (!node) // 如果不存在,則返回null
return NULL;
if (node == &act->commands) // 如果這個節點已經是頭節點了,則返回null
return NULL;
return node_to_item(node, struct command, clist); // 返回next節點
}
在獲取到了command之後,我們會去調用command的方法:
ret = cur_command->func(cur_command->nargs, cur_command->args);去執行command裡面的每一個func。 但是,非常奇怪的是,執行完commands之後,service是怎麼啟動的呢? 我們再去init.rc裡面一探究竟。
on boot .... class_start core
on nonencrypted
class_start main
class_start late_start
我們看到在action裡面,會有一些commands是class_start, 而後面跟的參數,好像與我們service的class name 是一致的。
service adbd /sbin/adbd --root_seclabel=u:r:su:s0
class core
socket adbd stream 660 system system
disabled
seclabel u:r:adbd:s0
# adbd on at boot in emulator
on property:ro.kernel.qemu=1
start adbd
service lmkd /system/bin/lmkd
class core
critical
socket lmkd seqpacket 0660 system system
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
從keywords裡面,我們找到了對應的function:
./init/keywords.h:53: KEYWORD(class_start, COMMAND, 1, do_class_start)來看一下do_class_start的實現:
int do_class_start(int nargs, char **args)
{
/* Starting a class does not start services
* which are explicitly disabled. They must
* be started individually.
*/
service_for_each_class(args[1], service_start_if_not_disabled);
return 0;
}
這個函數的實現很簡單,僅僅是傳遞調用了service_for_each_class, 並且在傳遞service name的時候,多傳遞了一個參數為service_start_if_not_disable.
void service_for_each_class(const char *classname,
void (*func)(struct service *svc))
{
struct listnode *node;
struct service *svc;
list_for_each(node, &service_list) { // 遍歷service的結構體,這裡是不會重復的,因為service的name如果有重復的時候,在解析過程中就已經處理了
svc = node_to_item(node, struct service, slist); // 從slist裡取出每一個結構體
if (!strcmp(svc->classname, classname)) { // 如果名字是匹配的話,就會進入這個判斷
func(svc); // 執行service_start_if_not_disable, 並且將當前的service結構體給傳遞進去
}
}
}
接下來要執行的就是service_start_if_not_disable了,我們來看一下具體的實現:
static void service_start_if_not_disabled(struct service *svc)
{
if (!(svc->flags & SVC_DISABLED)) {
service_start(svc, NULL);
} else {
svc->flags |= SVC_DISABLED_START;
}
}
如果這個service被設置為disabled的話,就不會被啟動,如果沒有設置的話,我們會去啟動這個service。 這裡需要注意的是,在我們調用service_start的時候,我們會去將第二個形參置為NULL。 在service_start的時候,這個函數很長很場,但是可以根據注釋,將其分為三個階段。
void service_start(struct service *svc, const char *dynamic_args)
{ /// ****************************** start service 的第一個階段
struct stat s;
pid_t pid;
int needs_console;
int n;
char *scon = NULL;
int rc;
/* starting a service removes it from the disabled or reset
* state and immediately takes it out of the restarting
* state if it was in there
*/
svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); // 這個service即將被啟動,將其從disable或reset的狀態給移除掉,置其為重新運行的狀態
svc->time_started = 0;
/* running processes require no additional work -- if
* they're in the process of exiting, we've ensured
* that they will immediately restart on exit, unless
* they are ONESHOT
*/
if (svc->flags & SVC_RUNNING) { // 如果這個service仍然是運行態的話,即return
return;
}
needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
if (needs_console && (!have_console)) {
ERROR(service '%s' requires console
, svc->name);
svc->flags |= SVC_DISABLED;
return;
} // 如果這個service的flags是初始console,但是這個已經啟動了的話,就會設置當前的flags為disabled
if (stat(svc->args[0], &s) != 0) { // 如果要執行的這個service的start的command不存在的話,返回error
ERROR(cannot find '%s', disabling '%s'
, svc->args[0], svc->name);
svc->flags |= SVC_DISABLED;
return;
}
if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) { // 因為dynamic_args為null,所以這邊不會進入這個判斷
ERROR(service '%s' must be one-shot to use dynamic args, disabling
,
svc->args[0]);
svc->flags |= SVC_DISABLED;
return;
}
// ***********************************************************這裡我們可以認為是第二個階段,selinux是信息安全相關的操作,這邊我們忽略掉
if (is_selinux_enabled() > 0) {
if (svc->seclabel) {
scon = strdup(svc->seclabel);
if (!scon) {
ERROR(Out of memory while starting '%s'
, svc->name);
return;
}
} else {
char *mycon = NULL, *fcon = NULL;
INFO(computing context for service '%s'
, svc->args[0]);
rc = getcon(&mycon);
if (rc < 0) {
ERROR(could not get context while starting '%s'
, svc->name);
return;
}
rc = getfilecon(svc->args[0], &fcon);
if (rc < 0) {
ERROR(could not get context while starting '%s'
, svc->name);
freecon(mycon);
return;
}
rc = security_compute_create(mycon, fcon, string_to_security_class(process), &scon);
if (rc == 0 && !strcmp(scon, mycon)) {
ERROR(Warning! Service %s needs a SELinux domain defined; please fix!
, svc->name);
}
freecon(mycon);
freecon(fcon);
if (rc < 0) {
ERROR(could not get context while starting '%s'
, svc->name);
return;
}
}
}
// ***************************************** selinux的操作結束,進入到第三個階段
NOTICE(starting '%s'
, svc->name);
pid = fork(); // fork一個自進程,即所有從init.rc啟動的service,都是一個子進程
if (pid == 0) { // pid = 0, 進入到子進程中
struct socketinfo *si;
struct svcenvinfo *ei;
char tmp[32];
int fd, sz;
umask(077);
if (properties_inited()) {
get_property_workspace(&fd, &sz); // 得到屬性存儲空間的信息並加入到環境變量中
sprintf(tmp, %d,%d, dup(fd), sz);
add_environment(ANDROID_PROPERTY_WORKSPACE, tmp);
}
for (ei = svc->envvars; ei; ei = ei->next) // 將service自己聲明的env加入到環境變量中
add_environment(ei->name, ei->value);
for (si = svc->sockets; si; si = si->next) { // 根據socket info設置socket
int socket_type = (
!strcmp(si->type, stream) ? SOCK_STREAM :
(!strcmp(si->type, dgram) ? SOCK_DGRAM : SOCK_SEQPACKET));
int s = create_socket(si->name, socket_type,
si->perm, si->uid, si->gid, si->socketcon ?: scon);
if (s >= 0) {
publish_socket(si->name, s);
}
}
freecon(scon);
scon = NULL;
if (svc->ioprio_class != IoSchedClass_NONE) {
if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
ERROR(Failed to set pid %d ioprio = %d,%d: %s
,
getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
}
}
if (needs_console) {
setsid();
open_console();
} else {
zap_stdio();
}
#if 0
for (n = 0; svc->args[n]; n++) {
INFO(args[%d] = '%s'
, n, svc->args[n]);
}
for (n = 0; ENV[n]; n++) {
INFO(env[%d] = '%s'
, n, ENV[n]);
}
#endif
setpgid(0, getpid());
/* as requested, set our gid, supplemental gids, and uid */
if (svc->gid) { // 設置gid
if (setgid(svc->gid) != 0) {
ERROR(setgid failed: %s
, strerror(errno));
_exit(127);
}
}
if (svc->nr_supp_gids) {
if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
ERROR(setgroups failed: %s
, strerror(errno));
_exit(127);
}
}
if (svc->uid) { // 設置uid
if (setuid(svc->uid) != 0) {
ERROR(setuid failed: %s
, strerror(errno));
_exit(127);
}
}
if (svc->seclabel) {
if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
ERROR(cannot setexeccon('%s'): %s
, svc->seclabel, strerror(errno));
_exit(127);
}
}
if (!dynamic_args) { // 因為dynamic_args設置的為null,我們在第一次從init.rc啟動的時候,一定會進入到這個判斷。
if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { // !!! 執行當前的service的啟動的命令,也就是說從這邊開始,我們就可以理解為已經從init進程中,去像kernel執行init一樣,就去執行各個service所對應的啟動函數了!
ERROR(cannot execve('%s'): %s
, svc->args[0], strerror(errno));
}
} else {
char *arg_ptrs[INIT_PARSER_MAXARGS+1];
int arg_idx = svc->nargs;
char *tmp = strdup(dynamic_args);
char *next = tmp;
char *bword;
/* Copy the static arguments */
memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
while((bword = strsep(&next, ))) {
arg_ptrs[arg_idx++] = bword;
if (arg_idx == INIT_PARSER_MAXARGS)
break;
}
arg_ptrs[arg_idx] = '';
execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
}
_exit(127);
}
freecon(scon);
if (pid < 0) {
ERROR(failed to start '%s'
, svc->name);
svc->pid = 0;
return;
}
svc->time_started = gettime();
svc->pid = pid;
svc->flags |= SVC_RUNNING;
if (properties_inited())
notify_service_state(svc->name, running);
}
終於結束了漫長的init進程的分析,估計這十篇文章可以基本概括了init進程啟動過程中的每一個細節。 但是,這樣是不夠的,我們需要接下來繼續看一下,android系統啟動流程所繼續的其他部分。 接下來,也就是我們啟動過程中常見的zygote了!
微信公眾號 微信公眾號怎麼申請 微信公眾號申請 微信公眾賬號申請
微信公眾號是微信公眾平台下的一個小分支,有信息推廣推送的功能,公眾號每天定時的推送相關文章或者相關活動內容,讓用戶及時了解最新動態!那麼微信公眾號怎麼申請?
AndroidStudio 如何使用JNI
一這是本人第一次寫博客 請多多關照第一次接觸到Android NDK 真是蒙圈 因為需要和Bluetooth下位機接觸 所以要在項目中鑲嵌Cpp 算法 然後總結了andr
Android App開發的自動化測試框架UI Automator使用教程
Android的自動化測試有很多框架,其中ui automator是google官方提供的黑盒UI相關的自動化測試工具,(GitHub主頁:case使用java寫,今天實
布局與控件(七)-ListView知多少(上)
第9節 ListView在應用界面當中,經常需要使用列表來展示內容。Android SDK提供了ListView控件,來實現這種效果。ListView需要和Adapter