安卓property_service代码移植供app开发使用(上)

安卓property_service代码移植供app开发使用(上)

源码基于aosp_7.1()

位置/bionic/libc/bionic/system_properties.cpp

1: 先来看官方的注释:

/*
 * Properties are stored in a hybrid trie/binary tree structure.
 * Each property's name is delimited at '.' characters, and the tokens are put
 * into a trie structure.  Siblings at each level of the trie are stored in a
 * binary tree.  For instance, "ro.secure"="1" could be stored as follows:
 *
 * +-----+   children    +----+   children    +--------+
 * |     |-------------->| ro |-------------->| secure |
 * +-----+               +----+               +--------+
 *                       /    \                /   |
 *                 left /      \ right   left /    |  prop   +===========+
 *                     v        v            v     +-------->| ro.secure |
 *                  +-----+   +-----+     +-----+            +-----------+
 *                  | net |   | sys |     | com |            |     1     |
 *                  +-----+   +-----+     +-----+            +===========+
 */

属性存储在一个混合的二叉树结构体中。每个属性名都会以 . 作为分隔符, 分割的子串会被放在一个trie结构体。

同级别的兄弟节点以二叉树的形式存放。比如ro.secure = 1会被如上图中存储

2: 几个重要的结构体

2.1 prop_bt

Property-trie结构体仅仅会被init进程更新,因为init进程初始化属性服务并拥有绝对主权。 结构体可以同时被多个线程读取。但是结构体并没有线程保护锁,所以trie节点中的成员属性left、right、children等指针均使用atomic_uint_leaset32_t类型。

为了确保访问prop_bt的线程同时也能注意到left、right、children等具有亲密关系节点的变化,通常访问这些节点的时候使用release-consume序列

如果当前的trie节点有一个属性关联,那么prop指针就指向了一个prop_info节点。left、right、children指针也是如此。

这几个指针都使用了atomic_uint_least32_t,并且使用release-consume访问序列类型保护

struct prop_bt {
    uint8_t namelen;
    uint8_t reserved[3];

    // The property trie is updated only by the init process (single threaded) which provides
    // property service. And it can be read by multiple threads at the same time.
    // As the property trie is not protected by locks, we use atomic_uint_least32_t types for the
    // left, right, children "pointers" in the trie node. To make sure readers who see the
    // change of "pointers" can also notice the change of prop_bt structure contents pointed by
    // the "pointers", we always use release-consume ordering pair when accessing these "pointers".

    // prop "points" to prop_info structure if there is a propery associated with the trie node.
    // Its situation is similar to the left, right, children "pointers". So we use
    // atomic_uint_least32_t and release-consume ordering to protect it as well.

    // We should also avoid rereading these fields redundantly, since not
    // all processor implementations ensure that multiple loads from the
    // same field are carried out in the right order.
    atomic_uint_least32_t prop;

    atomic_uint_least32_t left;
    atomic_uint_least32_t right;

    atomic_uint_least32_t children;

    char name[0];

    prop_bt(const char *name, const uint8_t name_length) {
        this->namelen = name_length;
        memcpy(this->name, name, name_length);
        this->name[name_length] = '\0';
    }

private:
    DISALLOW_COPY_AND_ASSIGN(prop_bt);
};

2.2 prop_info

该结构体被prop_bt使用,如果trie节点关联一个属性,那么该属性的详细信息就会以prop_info存储,包括value, name字符串。

struct prop_info {
    atomic_uint_least32_t serial;
    char value[PROP_VALUE_MAX];
    char name[0];

    prop_info(const char *name, const uint8_t namelen, const char *value,
              const uint8_t valuelen) {
        memcpy(this->name, name, namelen);
        this->name[namelen] = '\0';
        atomic_init(&this->serial, valuelen << 24);
        memcpy(this->value, value, valuelen);
        this->value[valuelen] = '\0';
    }
private:
    DISALLOW_COPY_AND_ASSIGN(prop_info);
};

2.3 prop_area

该结构体我省略了一些代码。

以下代码都是需要说明一下的,所以没删除

class prop_area {
    prop_area(const uint32_t magic, const uint32_t version) :
        magic_(magic), version_(version) {
        atomic_init(&serial_, 0);
        memset(reserved_, 0, sizeof(reserved_));
        // Allocate enough space for the root node.
        bytes_used_ = sizeof(prop_bt);
    }
    const prop_info *find(const char *name);
private:
    void *allocate_obj(const size_t size, uint_least32_t *const off);
    prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off);
    prop_info *new_prop_info(const char *name, uint8_t namelen,
                             const char *value, uint8_t valuelen,
                             uint_least32_t *const off);
    void *to_prop_obj(uint_least32_t off);
    prop_bt *to_prop_bt(atomic_uint_least32_t *off_p);
    prop_info *to_prop_info(atomic_uint_least32_t *off_p);

    prop_bt *root_node();

    prop_bt *find_prop_bt(prop_bt *const bt, const char *name,
                          uint8_t namelen, bool alloc_if_needed);

    const prop_info *find_property(prop_bt *const trie, const char *name,
                                   uint8_t namelen, const char *value,
                                   uint8_t valuelen, bool alloc_if_needed);
  
    uint32_t bytes_used_;
    atomic_uint_least32_t serial_;
    uint32_t magic_;
    uint32_t version_;
    uint32_t reserved_[28];
    char data_[0];

    DISALLOW_COPY_AND_ASSIGN(prop_area);
}

Prop_area构造函数: serial_与同步有关系, bytes_used代表prop_area已使用空间,总空间是通过map属性文件的时候定义的

​ bytes_used_初始化为size_of(prop_bt),这段空间为root节点。

allocate_obj:在prop_area区域空间申请一段内存

new_prop_bt以及new_prop_info:内部都会调用allocate_obj

to_prop_obj: 将atomic_uint_least32_t转换为指针,其计算方法是通过data_ + offset偏移

void *prop_area::to_prop_obj(uint_least32_t off)
{
    if (off > pa_data_size)
        return NULL;

    return (data_ + off);
}

root_node函数:data_ + offset,其中offset=0

find_prop_bt: 第一个参数为trie节点,第二个参数为name,表示需要查找的属性,比如ro.secure

Find_property: 类似find_prop_bt,其内部会调用find_prop_bt

3.prop_area初始化

3.1 init进程初始化property_service流程

3.1.1 /system/core/init/init.cpp中调用property_init()
int main(int argc, char** argv) {
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }
    ...............
    ...............

    NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");

    if (!is_first_stage) {
        // Indicate that booting is in progress to background fw loaders, etc.
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

        property_init();

        // If arguments are passed both on the command line and in DT,
        // properties set in DT always have priority over the command-line ones.
        process_kernel_dt();
        process_kernel_cmdline();

        // Propagate the kernel variables to internal variables
        // used by init as well as the current required properties.
        export_kernel_boot_props();
    }
3.1.2 /system/core/init/property_service.cpp中调用了system_property_area_init()
void property_init() {
    if (__system_property_area_init()) {
        ERROR("Failed to initialize property area\n");
        exit(1);
    }
}
3.1.3 /bionic/libc/bionic/system_properties.cpp中调用了context_node->open函数
int __system_property_area_init()
{
    free_and_unmap_contexts();
    mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
    if (!initialize_properties()) {
        return -1;
    }
    bool open_failed = false;
    bool fsetxattr_failed = false;
    list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) {
        if (!l->open(true, &fsetxattr_failed)) {
            open_failed = true;
        }
    });
    if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
        free_and_unmap_contexts();
        return -1;
    }
    initialized = true;
    return fsetxattr_failed ? -2 : 0;
}
3.1.4 /bionic/libc/bionic/system_properties.cpp中调用了map_prop_area_rw
bool context_node::open(bool access_rw, bool* fsetxattr_failed) {
    lock_.lock();
    if (pa_) {
        lock_.unlock();
        return true;
    }

    char filename[PROP_FILENAME_MAX];
    int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s",
                                   property_filename, context_);
    if (len < 0 || len > PROP_FILENAME_MAX) {
        lock_.unlock();
        return false;
    }

    if (access_rw) {
        pa_ = map_prop_area_rw(filename, context_, fsetxattr_failed);
    } else {
        pa_ = map_prop_area(filename, false);
    }
    lock_.unlock();
    return pa_;
}
3.1.5 解读一下map_prop_area_rw
  1. 以只读的方式open属性文件
  2. 通过fsetxattr设置文件的上下文
  3. 将属性文件的大小设置为PA_SIZE, PA_SIZE为宏(#define PA_SIZE (128 * 1024)), 128KB
  4. 将属性文件以可读写的模式共享到内存
  5. new (address) prop_area, 表示以address为内存地址,初始化prop_area结构体
static prop_area* map_prop_area_rw(const char* filename, const char* context,
                                   bool* fsetxattr_failed) {
    /* dev is a tmpfs that we can use to carve a shared workspace
     * out of, so let's do that...
     */
    const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);

    if (fd < 0) {
        if (errno == EACCES) {
            /* for consistency with the case where the process has already
             * mapped the page in and segfaults when trying to write to it
             */
            abort();
        }
        return nullptr;
    }

    if (context) {
        if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) {
            __libc_format_log(ANDROID_LOG_ERROR, "libc",
                              "fsetxattr failed to set context (%s) for \"%s\"", context, filename);
            /* 
             * fsetxattr() will fail during system properties tests due to selinux policy.
             * We do not want to create a custom policy for the tester, so we will continue in
             * this function but set a flag that an error has occurred.
             * Init, which is the only daemon that should ever call this function will abort
             * when this error occurs.
             * Otherwise, the tester will ignore it and continue, albeit without any selinux
             * property separation.
             */
            if (fsetxattr_failed) { 
                *fsetxattr_failed = true;
            }
        }
    }
    if (ftruncate(fd, PA_SIZE) < 0) {
        close(fd);
        return nullptr;
    }

    pa_size = PA_SIZE;
    pa_data_size = pa_size - sizeof(prop_area);
    compat_mode = false;

    void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (memory_area == MAP_FAILED) {
        close(fd);
        return nullptr;
    }

    prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);

    close(fd);
    return pa;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值