Android init进程解析

来源:互联网 发布:js代码注解 编辑:程序博客网 时间:2024/04/29 22:15

使用的版本:Android 2.3

Linux中的所有进程都是由init进程创建并运行的,Android中也存在init进程,首先Android内核启动,然后在用户空间中启动init进程,再依次启动系统运行所需的其他进程。
init进程是守护进程,用来监视其他进程,当某个进程终结后,变成僵尸进程(Zombie process),僵尸进程会占据进程列表中的位置,这时init进程就负责回收僵尸进程,Android平台中,Init进程除了负责回收僵尸进程外,还提供几种额外的功能。

·init进程运行过程

start_kernet() => init_post() =>run_initprocess() =>运行init进程

·init进程四大功能

1.分析及运行init.rc文件
2.生成设备驱动节点
3.处理子进程终止
4.属性服务

1.分析及运行init.rc文件

init.rc文件在system/core/rootdir目录下。
init.rc启动脚本是Android启动时,用来设置系统环境的脚本文件。action list与service list相关的内容是由init进程根据init.rc文件生成的。下面是init.rc文件的大致结构。

on early-init    start ueventdon initsysclktz 0loglevel 3# setup the global environment    export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin    export LD_LIBRARY_PATH /vendor/lib:/system/lib    export ANDROID_BOOTLOGO 1    export ANDROID_ROOT /system    export ANDROID_ASSETS /system/app    export ANDROID_DATA /data    export EXTERNAL_STORAGE /mnt/sdcard    export ASEC_MOUNTPOINT /mnt/asec    export LOOP_MOUNTPOINT /mnt/obb
......
on boot# basic network init    ifup lo    hostname localhost    domainname localdomain
......
<pre name="code" class="plain">on property:ro.secure=0    start console# adbd is controlled by the persist.service.adb.enable system propertyservice adbd /sbin/adbd    disabled# adbd on at boot in emulatoron property:ro.kernel.qemu=1    start adbdon property:persist.service.adb.enable=1    start adbdon property:persist.service.adb.enable=0    stop adbd

......
service servicemanager /system/bin/servicemanager    user system    critical    onrestart restart zygote    onrestart restart mediaservice vold /system/bin/vold    socket vold stream 0660 root mount    ioprio be 2service netd /system/bin/netd    socket netd stream 0660 root systemservice debuggerd /system/bin/debuggerdservice ril-daemon /system/bin/rild    socket rild stream 660 root radio    socket rild-debug stream 660 radio system    user root    group radio cache inet misc audio sdcard_rw
......
init.rc文件分为两部分,一部分是以"on"关键字开头的动作列表(Action list),一部分是以"service"关键字开头的服务列表(Service list)。

1.1 动作列表

动作列表有三个段落,on init ,on boot ,on property段落。on init段落主要设置环境变量,生成系统运行所需的文件或目录,修改相应的权限,并挂载与系统运行相关的目录。环境变量设置主要设置运行根文件系统命令的目录以及程序编译时需要的库目录,根文件的挂载主要是/system和/data两个目录,挂载完毕后Android的根文件系统就准备好了。on boot段落主要用于设置应用程序终止条件,应用程序驱动目录及文件权限等。on property段落中,记录属性值改变时执行的命令,在默认的init.rc初始化脚本中,还记录adbd服务启动、终止的条件。Android平台中的共享属性存储区域中的值,只能通过通知init进程,由init进程负责改变,增强安全性。

1.2服务列表

service列表用来记录init进程启动的进程。由init进程启动的子进程或者是一次性程序,或者是运行在后台的与应用程序、系统管理相关的Daemon进程。

1.3 init.rc文件分析函数

system/core/init目录下的init_parser.c文件中有一个名为init_parse_config_file()函数
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;}
fn是指定待分析文件的路径,read_file函数读取文件,并作为字符串保存在data中,再调用parse_config函数分析读入的字符串。
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()函数以行为单位分割传递过来的字符串,然后调用lookup_keyword()函数。
lookup_keyword()函数用于返回init.rc脚本中每行首个单词在keyword_list结构体数组中的数组编号。Keyword_list结构体数组由KEYWORD列表构成
#define __MAKE_KEYWORD_ENUM__#define KEYWORD(symbol, flags, nargs, func) K_##symbol,enum {    K_UNKNOWN,#endif    KEYWORD(capability,  OPTION,  0, 0)    KEYWORD(chdir,       COMMAND, 1, do_chdir)    KEYWORD(chroot,      COMMAND, 1, do_chroot)    KEYWORD(class,       OPTION,  0, 0)    KEYWORD(class_start, COMMAND, 1, do_class_start)    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)    KEYWORD(console,     OPTION,  0, 0)    KEYWORD(critical,    OPTION,  0, 0)    KEYWORD(disabled,    OPTION,  0, 0)    KEYWORD(domainname,  COMMAND, 1, do_domainname)    KEYWORD(exec,        COMMAND, 1, do_exec)    KEYWORD(export,      COMMAND, 2, do_export)    KEYWORD(group,       OPTION,  0, 0)    KEYWORD(hostname,    COMMAND, 1, do_hostname)    KEYWORD(ifup,        COMMAND, 1, do_ifup)    KEYWORD(insmod,      COMMAND, 1, do_insmod)    KEYWORD(import,      COMMAND, 1, do_import)    KEYWORD(keycodes,    OPTION,  0, 0)    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)    KEYWORD(mount,       COMMAND, 3, do_mount)    KEYWORD(on,          SECTION, 0, 0)    KEYWORD(oneshot,     OPTION,  0, 0)    KEYWORD(onrestart,   OPTION,  0, 0)    KEYWORD(restart,     COMMAND, 1, do_restart)    KEYWORD(service,     SECTION, 0, 0)    KEYWORD(setenv,      OPTION,  2, 0)    KEYWORD(setkey,      COMMAND, 0, do_setkey)    KEYWORD(setprop,     COMMAND, 2, do_setprop)    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)    KEYWORD(socket,      OPTION,  0, 0)    KEYWORD(start,       COMMAND, 1, do_start)    KEYWORD(stop,        COMMAND, 1, do_stop)    KEYWORD(trigger,     COMMAND, 1, do_trigger)    KEYWORD(symlink,     COMMAND, 1, do_symlink)    KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)    KEYWORD(user,        OPTION,  0, 0)    KEYWORD(wait,        COMMAND, 1, do_wait)    KEYWORD(write,       COMMAND, 2, do_write)    KEYWORD(copy,        COMMAND, 2, do_copy)    KEYWORD(chown,       COMMAND, 2, do_chown)    KEYWORD(chmod,       COMMAND, 2, do_chmod)    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)    KEYWORD(ioprio,      OPTION,  0, 0)
KEYWORD宏定义在platform/system/core/init目录下的keyword.h文件中,用来把关键字和相关函数对应起来,如stop关键字与do_stop()函数相对应。用户如果想向init.rc中添加新命令,必须在KEYWORD列表中定义新命令,并定义与之对应的函数。

1.4 动作列表与服务列表的运行

关键的函数:queue_buildin_action();
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);    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);}

该函数用来运行动作列表,位于system/core/init目录下的init_parser.c文件中。

do_class_start()和service_start_if_not_disabled()函数
static void service_start_if_not_disabled(struct service *svc){    if (!(svc->flags & SVC_DISABLED)) {        service_start(svc, NULL);    }}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;}

这两个函数用来运行服务列表,位于system/core/init目录下的builtins.c文件中。

2.创建设备节点文件

与linux相同,Android中的应用程序通过设备驱动来访问硬件设备。在Linux中,提供mknod实用程序来创建设备节点文件,但出于安全考虑,Android未提供类似mknod的实用程序。
在linux中,运行所需要的设备节点文件都被事先定义在"/dev"目录下,应用程序通过事先定义好的设备节点文件就能访问设备驱动程序。在Android根文件系统的映像中不存在"/dev"目录,需要有一个进程(init进程)创建设备节点文件。
init进程通过两种方式创建设备节点文件,第一种是连接已定义的设备的方法,成为“冷插拔”,第二种方法是在系统运行状态下连接设备,称为“热插拔”。
udev实用程序作为守护进程运行,当设备驱动被加载时,它会掌握主设备号、次设备号,以及设备类型,然后在"/dev"目录下自动创建设备节点文件。首先由驱动程序初始化函数,注册驱动信息到"/sys"目录下,发生uevent,被udev守护进程监听到。udev守护进程会比较uevent信息与注册在"/sys"中的设备信息,然后在"/dev"中创建设备节点文件。
uevent是内核向用户空间进程传递信息的信号系统,在添加或删除设备时,内核使用uevent将设备信息传递到用户空间。
Linux系统中,在udev守护进程运行前,通过提供与加载的设备驱动程序冷插拔机制,来解决设备节点文件没被创建的问题。当插入设备时,热插拔机制会立即进行处理。

3.进程的终止与再启动

init进程读取并分析init.rc文件,获得服务列表,而后从列表中依次启动服务子进程,Init进程启动的主要进程如下:
1.sh:搭载android的机器终端,连接串口或adbd时,提供控制台输入输出的shell程序
2.adbd:Android Debug Bridge,用来管理QEWU模拟器或实际机器的状态,该工具运行在目标机器上,充当服务器。PC连接服务器的客户端。
3.serviceManager:Android中比较重要的一个进程,在init进程启动后启动,用来管理系统中的服务。
4.vold:指Volume Demeaon,用来挂载/管理USB存储或SD卡设备
5.playmp3:在Android启动时,输出启动声音。

init进程中有一个事件处理循环
static void sigchld_handler(int s){     write(signal_fd, &d, 1);}
发生SIGCHLD信号时,该函数被调用,参数S是用来接收SIGCHLD信号的编号。
sigchld_handler写入SIGCHLD编号到signal_fd中,通过套接字传递数据到signal_recv_fd,发生事件poll()到wait_for_one_process(0)中。

下面是wait_for_one_process源代码,在system/core/init/signal_handler.c中
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;}


4.属性服务

Andorid平台中,为了让运行中的所有进程共享系统运行时所需要的各种属性值,系统开辟了属性存储区域。属性由Key-Value构成,在访问属性值时,添加了访问权限控制,增加了访问的安全性。当其他进程修改属性值时,必须向init进程提出请求,最终由init进程负责修改属性值,init进程会先检查各属性的访问权限,而后再修改属性值。
在init目录下的property_service.c文件中包含了属性的初始化和访问修改函数
如property_set()函数
int property_set(const char *name, const char *value){    prop_area *pa;    prop_info *pi;    int namelen = strlen(name);    int valuelen = strlen(value);    if(namelen >= PROP_NAME_MAX) return -1;    if(valuelen >= PROP_VALUE_MAX) return -1;    if(namelen < 1) return -1;    pi = (prop_info*) __system_property_find(name);    if(pi != 0) {        /* ro.* properties may NEVER be modified once set */        if(!strncmp(name, "ro.", 3)) return -1;        pa = __system_property_area__;        update_prop_info(pi, value, valuelen);        pa->serial++;        __futex_wake(&pa->serial, INT32_MAX);    } else {        pa = __system_property_area__;        if(pa->count == PA_COUNT_MAX) return -1;        pi = pa_info_array + pa->count;        pi->serial = (valuelen << 24);        memcpy(pi->name, name, namelen + 1);        memcpy(pi->value, value, valuelen + 1);        pa->toc[pa->count] =            (namelen << 24) | (((unsigned) pi) - ((unsigned) pa));        pa->count++;        pa->serial++;        __futex_wake(&pa->serial, INT32_MAX);    }    /* 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);    }    property_changed(name, value);    return 0;}
property_get()函数
const char* property_get(const char *name){    prop_info *pi;    if(strlen(name) >= PROP_NAME_MAX) return 0;    pi = (prop_info*) __system_property_find(name);    if(pi != 0) {        return pi->value;    } else {        return 0;    }}
init_property_area()函数
static int init_property_area(void){    prop_area *pa;    if(pa_info_array)        return -1;    if(init_workspace(&pa_workspace, PA_SIZE))        return -1;    fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);    pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);    pa = pa_workspace.data;    memset(pa, 0, PA_SIZE);    pa->magic = PROP_AREA_MAGIC;    pa->version = PROP_AREA_VERSION;        /* plug into the lib property services */    __system_property_area__ = pa;    property_area_inited = 1;    return 0;}


0 0