property

作者: gbmaotai | 来源:发表于2018-10-23 09:25 被阅读0次

    1) 系统一启动就会从若干属性脚本文件中加载属性内容;

    2) 系统中的所有属性(key/value)会存入同一块共享内存中;

    3) 系统中的各个进程会将这块共享内存映射到自己的内存空间,这样就可以直接读取属性内容了;

    4) 系统中只有一个实体可以设置、修改属性值,它就是属性服务(Property Service);

    5) 不同进程只可以通过socket方式,向属性服务Service发出修改属性值的请求,而不能直接修改属性值;

    6) 共享内存中的键值内容会以一种字典树的形式进行组织。

    2018082118102975.png

    init进程里的Property Service

    是在init 进程里面poll 检测来自其他进程的设置消息

    初始化属性共享内存

    在init进程的main()函数里,辗转打开了一个内存文件“/dev/properties”,并把它设定为128KB大小,接着调用mmap()将这块内存映射到init进程空间了。这个内存的首地址被记录在system_property_area全局变量里,以后每添加或修改一个属性,都会基于这个system_property_area变量来计算位置。
    为什么要两次open那个/dev/properties文件呢?我想原因是这样的:第一次open的句柄,最终是给属性服务自己用的,所以需要有读写权限;而第二次open的句柄,会被记入pa_workspace.fd,并在合适时机添加进环境变量,供其他进程使用,因此只能具有读取权限。
    第二次open动作发生在接下来的init_workspace()函数里。此时会再一次打开properties文件,这次却是以只读模式打开的:

    int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW);
    

    打开的句柄记录在pa_workspace.fd处,以后每当init进程执行socket命令,并调用service_start()时,会执行类似下面的句子:

    get_property_workspace(&fd, &sz);   // 读取pa_workspace.fd  
    sprintf(tmp, "%d,%d", dup(fd), sz);  
    add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
    

    说白了就是把 pa_workspace.fd 的句柄记入一个名叫“ ANDROID_PROPERTY_WORKSPACE ”的环境变量去

    初始化属性服务

    property_service_init_action()函数只是在简单调用start_property_service()而已
    加载若干属性文件,然后创建并监听一个socket接口。
    加载的文件有:

    /system/build.prop
    /system/default.prop(该文件不一定存在)
    /data/local.prop
    /data/property目录里的若干脚本
    
    初始化属性后的触发动作

    现在就可以遍历刚生成的action_list,看看哪个刚加载好的属性可以进一步触发连锁动作。
    当获取的属性名和属性值,与当初init.rc里记录的某action的激发条件匹配时,就把该action插入执行队列的尾部(action_add_queue_tail(act))

    init进程循环监听socket

    后创建了一个socket,

    其名为ANDROID_SOCKET_DIR"/PROP_SERVICE_NAME",也即 "/dev/socket/property_service

    当从socket收到“设置属性”的命令后,会调用上面的handle_property_set_fd()函数
    对于普通属性而言,主要是调用property_set()来设置属性值,但是有一类特殊属性是以“ctl.”开头的,它们本质上是一些控制命令,比如启动某个系统服务。这种控制命令需调用handle_control_message()来处理。
    不是谁都可以成功设置以“ctl.”开头的特殊属性。handle_property_set_fd()会先调用check_control_perms()来检查发起方是否具有相应的权限。

    如果设置方的uid是AID_SYSTEM或者AID_ROOT,那么一般都是具有权限的。而如果uid是其他值,那么就得查control_perms表了,这个表的定义如下
    core/init/Property_service.c

    /* White list of permissions for setting property services. */
    struct {
        const char *prefix;
        unsigned int uid;
        unsigned int gid;
    } property_perms[] = {
        { "net.rmnet0.",      AID_RADIO,    0 },
        { "net.gprs.",        AID_RADIO,    0 },
        { "net.ppp",          AID_RADIO,    0 },
        { "net.qmi",          AID_RADIO,    0 },
        { "net.lte",          AID_RADIO,    0 },
        { "net.cdma",         AID_RADIO,    0 },
        { "ril.",             AID_RADIO,    0 },
        { "gsm.",             AID_RADIO,    0 },
        { "persist.radio",    AID_RADIO,    0 },
        { "net.dns",          AID_RADIO,    0 },
        { "sys.usb.config",   AID_RADIO,    0 },
        { "net.",             AID_SYSTEM,   0 },
        { "dev.",             AID_SYSTEM,   0 },
        { "runtime.",         AID_SYSTEM,   0 },
        { "hw.",              AID_SYSTEM,   0 },
        { "sys.",             AID_SYSTEM,   0 },
        { "sys.powerctl",     AID_SHELL,    0 },
        { "service.",         AID_SYSTEM,   0 },
        { "wlan.",            AID_SYSTEM,   0 },
        { "bluetooth.",       AID_BLUETOOTH,   0 },
        { "dhcp.",            AID_SYSTEM,   0 },
        { "dhcp.",            AID_DHCP,     0 },
        { "debug.",           AID_SYSTEM,   0 },
        { "debug.",           AID_SHELL,    0 },
        { "log.",             AID_SHELL,    0 },
        { "service.adb.root", AID_SHELL,    0 },
        { "service.adb.tcp.port", AID_SHELL,    0 },
        { "persist.sys.",     AID_SYSTEM,   0 },
        { "persist.service.", AID_SYSTEM,   0 },
        { "persist.security.", AID_SYSTEM,   0 },
        { "persist.service.bdroid.", AID_BLUETOOTH,   0 },
        { "selinux."         , AID_SYSTEM,   0 },
        /* psw0523 add for samsung_slsi slsiap hwc */
        { "hwc.scenario",     AID_SYSTEM,   0 },
        { "hwc.scale",        AID_SYSTEM,   0 },
        { "hwc.resolution",   AID_SYSTEM,   0 },
        { "hwc.hdmimode",     AID_SYSTEM,   0 },
        { "hwc.screendownsizing",    AID_SYSTEM,   0 },
        /* end psw0523 */
        { NULL, 0, 0 }
    };
    
    property_set

    property_set(
    一开始当然要先找到“希望设置的目标属性”在共享内存里对应的prop_info节点啦
    如果可以找到prop_info节点,就尽量将这个属性的值更新一下,除非是遇到“ro.”属性,这种属性是只读的,当然不能set。
    。如果找不到prop_info节点,此时会为这个新属性创建若干字典树节点,包括最终的prop_info叶子。
    当某个属性修改之后, Property Service 会遍历一遍 action_list 列表,找到其中匹配的 action 节点,并将之添加进 action_queue 队列。

    客户进程访问属性的机制

    int __system_properties_init()
    它调用的map_prop_area()会把属性共享内存,以只读模式映射到用户进程空间:

    static int map_prop_area()  
    {  
        fd = open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);  
        . . . . . .  
        if ((fd < 0) && (errno == ENOENT)) {  
            fd = get_fd_from_env();    
            fromFile = false;  
        }  
      
        . . . . . .  
        pa_size = fd_stat.st_size;  
        pa_data_size = pa_size - sizeof(prop_area);  
        prop_area *pa = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0);  
        . . . . . .  
        result = 0;  
        __system_property_area__ = pa;  
        . . . . . .  
      
        return result;  
    }  
    

    bionic/libc/bionic/Libc_init_common.cpp

    void __libc_init_common(KernelArgumentBlock& args) {  
      . . . . . .  
      . . . . . .  
      _pthread_internal_add(main_thread);  
      __system_properties_init(); // Requires 'environ'.  
    }  
    

    当一个用户进程被调用起来时,内核会先调用到C运行期库(crtbegin)层次来初始化运行期环境,在这个阶段就会调用到__libc_init(),而后才会间接调用到C程序员熟悉的main()函数。可见属性共享内存在执行main()函数之前就已经映射好了。

    读取属性值

    属性共享内存中的内容,其实被组织成一棵字典树。内存块的第一个节点是个特殊的总述节点,类型为prop_area。紧随其后的就是字典树的“树枝”和“树叶”了,树枝以prop_bt表达,树叶以prop_info表达。我们读取或设置属性值时,最终都只是在操作“叶子”节点而已。

    相关文章

      网友评论

          本文标题:property

          本文链接:https://www.haomeiwen.com/subject/sadozftx.html