Android init进程一些容易忽视的技术细节

Android init进程作为用户空间第一个进程,做了很多系统初始化工作,其核心分为两块:一是属性,二是init.rc。此外,在具体的项目过程中,常和init进程打交道,里面有不少技术细节值得我们研究清楚。

  • 属性key-value大小限制
bionic/libc/include/sys/System_properties.h
#define PROP_NAME_MAX   32
#define PROP_VALUE_MAX  92

key最大为32个字节,末位\0还要占一个字节,所以真正用于key的最大字串为31。即key = persist.system.donotallow.secsim(32Bytes) 时,会报错。
同理value最大为91Bytes。

  • 属性前缀的作用
    通常,属性都带有前缀,如ro.config、persist.xxx等。
    对于ro前缀,只读属性,即第一次设置之后,以后都不能再改变。
    if(pi != 0) {
        /* ro.* properties may NEVER be modified once set */
        if(!strncmp(name, "ro.", 3)) return -1;

persist前缀属性在初始加载属性时须注意,default属性未加载完成时,不能将其写入文件中,以免覆盖文件中的属性值。
对于persist前缀,在设置属性的同时,还会将其写到/data/property/persist.xxx文件中,在系统重启时,将该属性值恢复到上机关机时的状态。那么什么时候加载的这些persist属性?

on property:vold.decrypt=trigger_load_persist_props
    load_persist_props
void load_persist_props(void)
{
    load_override_properties();
    /* Read persistent properties after all default values have been loaded. */
    load_persistent_properties();
}

加载时机要控制好,一是在默认属性都加载完成后;二是等data分区挂载好之后,毕竟persist属性存储在data分区中。

  • tcp的buffer size和接收窗口大小在init中设置,在tcp调优过程中,可以在init.rc中进行修改。

  • 系统中的可执行路径(PATH)在init.environ.rc中设置

on init
    export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
  • usb的各种functions通过属性sys.usb.config触发,在init.usb.rc中实现,所以控制usb function可以在framework层设置属性即可,没有必须在通过JNI到native层控制。
# Used to disable USB when switching states
on property:sys.usb.config=none
    stop adbd
    write /sys/class/android_usb/android0/enable 0
    write /sys/class/android_usb/android0/bDeviceClass 0
    setprop sys.usb.state ${sys.usb.config}

# adb only USB configuration
# This should only be used during device bringup
# and as a fallback if the USB manager fails to set a standard configuration
on property:sys.usb.config=adb
    write /sys/class/android_usb/android0/enable 0
    write /sys/class/android_usb/android0/idVendor 18d1
    write /sys/class/android_usb/android0/idProduct D002
    write /sys/class/android_usb/android0/functions ${sys.usb.config}
    write /sys/class/android_usb/android0/enable 1
    start adbd
    setprop sys.usb.state ${sys.usb.config}
  • ueventd进程被init进程唤起之后,去解析ueventd.rc,可以看到一些常见的设备,同时Android系统特有的设备ashmem、binder也在其中。这里会记录设备节点信息,而这些设备不一定会创建节点。
on early-init
    start ueventd
ueventd.rc
/dev/null                 0666   root       root
/dev/zero                 0666   root       root
/dev/full                 0666   root       root
/dev/ptmx                 0666   root       root
/dev/tty                  0666   root       root
/dev/random               0666   root       root
/dev/urandom              0666   root       root
# Make HW RNG readable by group system to let EntropyMixer read it.
/dev/hw_random            0440   root       system
/dev/ashmem               0666   root       root
/dev/binder               0666   root       root
  • 各native deamon基本都在init进程启动。对于服务可以配置不同的参数,决定服务的启动情况:
    disable: 不自动启动
    oneshot: 只启动一次
service vold /system/bin/vold
    class core
    socket vold stream 0660 root mount
    ioprio be 2

service bootanim /system/bin/bootanimation
    class main
    user graphics
    group graphics
    disabled
    oneshot

service media /system/bin/mediaserver
    class main
    user media
    group audio camera inet net_bt net_bt_admin net_bw_acct  mediadrm
    ioprio rt 4

以media服务为例,init进程调用service_start:

pid = fork();
    if (pid == 0) { // son
        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)
            add_environment(ei->name, ei->value);

        for (si = svc->sockets; si; si = si->next) {
            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);
            }
        }

        setsid();
        setpgid(0, getpid());

        execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
        }
        _exit(127);
    }

init进程启动服务主要包括:
1)把属性内存区域的文件描述符加入到环境变量ANDROID_PROPERTY_WORKSPACE中,因此启动的服务可以与属性进行通信。
2)创建服务配置的unix socket套接字。
3)设置会话id和进程组id
4)执行指定的服务程序

这样一个deamon就正常启动了。

你可能感兴趣的:(android基础)