Android4.4属性系统-初始化(下)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 笔记

5.2 property_service.c解析

system/core/init/property_service.c

#include "property_service.h"
#include <sys/socket.h>
#include <sys/_system_properties.h>
#define PERSISTENT_PROPERTY_DIR  "/data/property"
//标识persistent proerties是否加载完成
static int persistent_properties_loaded = 0;
//标识property初始化是否完成
static int property_area_inited = 0;
typedef struct {
    size_t size;  //共享内存空间大小
    int fd;  //句柄
} workspace;
//property初始化
void property_init(void)
{
    init_property_area();
}
/*init_property_area 初始化共享内存空间。在 Android 系统中,所有的进程是共享
*系统属性值的,Android 系统提供了一个名为属性的保存空间,在共享内存区域中
*(ASHMEM),创建并初始化属性域*/
static workspace pa_workspace;
static int init_property_area(void)
{
    if (property_area_inited)  //只能初始化一次
        return -1;
    //初始化共享内存区域,位于system_properties.c
    if(__system_property_area_init())
        return -1;
    //初始化工作空间,大小初始化为0
    if(init_workspace(&pa_workspace, 0))
        return -1;
    fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
    property_area_inited = 1;  //设置初始化完成标志
    return 0;
}
 //初始化工作空间,workspace在该文件中被定义,参数<工作空间,空间大小>
static int init_workspace(workspace *w, size_t size)
{
    void *data;
    //#define PROP_FILENAME "/dev/__properties__"  定义在_system_properties.h
    int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW);  //打开内存映射文件
    if (fd < 0)
        return -1;
    w->size = size;  //设置空间大小
    w->fd = fd;  //设置文件句柄,fd 为/dev/__properties__文件句柄
    return 0;
}
void property_load_boot_defaults(void)
{
    //从文件中加载,PROP_PATH_RAMDISK_DEFAULT=“/defalult.prop”
    //定义在_system_properties.h文件中
    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
/*
default.prop 文件中的内容是在 Android系统编译时写入到 boot.img 中的,而每次
开机,boot.img 会进行解压,所以想要修改该文件中的内容可以重新编译系统,或
者修改 boot.img 文件,在/build/core/main.mk 文件中定义了不同属性的值,
ADDITIONAL_DEFAULT_PROPERTIES 变量值就是需要写入到 default.prop 文件中
的内容
build/core/main.mk部分内容如下:
# Target is secure in user builds.
ADDITIONAL_DEFAULT_PROPERTIES += ro.secure=1
# Disallow mock locations by default for user builds
ADDITIONAL_DEFAULT_PROPERTIES += ro.allow.mock.location=0
default.prop文件内容一般如下 :
shell@sp9820w_poc_780:/ $ cat default.prop
#
# ADDITIONAL_DEFAULT_PROPERTIES
#
ro.secure=1
ro.allow.mock.location=0
ro.debuggable=1
camera.disable_zsl_mode=1
persist.sys.usb.config=mtp,adb
*/
//从指定文件中加载属性
static void load_properties_from_file(const char *fn)
{
    char *data;
    unsigned sz;
    //打开文件,读取内容
    data = read_file(fn, &sz);
    if(data != 0) {
        //打开成功,加载property
        load_properties(data);
        //关闭文件
        free(data);
    }
}
//解析property数据,逐行解析
static void load_properties(char *data)
{   
    char *key, *value, *eol, *sol, *tmp;
    sol = data;  //sol指向文件开始
    //char *strchr(const char *str, int c)返回在字符串 str 中第一次出现字符 c 的位置,实现逐行读取
    while((eol = strchr(sol, '\n'))) {  //返回换行符的指针
        key = sol;   //key表示行首的指针
        *eol++ = 0;  //相关于*eol=0; eol++; '\n'替换为0,此时eol为下一行的行首指针
        sol = eol;    //赋值给sol
        value = strchr(key, '=');  //截取一行中'='前面的字符,value指向'='
        if(value == 0) continue;  //如果=前面为空,跳过
        *value++ = 0;  //’=‘替换为0,value指向原'='后面的内容,即实际的value字节地址
        while(isspace(*key)) key++;  //去掉一行前面的空格
        if(*key == '#') continue;  //如果是'#'开头,表示注释,跳过
        tmp = value - 2;  //value-2为key的末尾地址
        while((tmp > key) && isspace(*tmp)) *tmp-- = 0; //判断key末尾是否有空白符,如果有替换为0
        while(isspace(*value)) value++;  //判断value起始内容中是否有空白符,如果有替换为0
        tmp = eol - 2;  //判断value末尾内容中是否有空白符,如果有替换为0
        while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
        //设置属性,关键调用,该方法本章不再继续深究
        property_set(key, value);
    }
}
//启动属性服务
void start_property_service(void)
{
    int fd;
    //1.加载/system/build.prop
    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
    //2.加载 /system/default.prop
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
    //3.加载override props
    load_override_properties();
    /* Read persistent properties after all default values have been loaded. */
    load_persistent_properties();
    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
    if(fd < 0) return;
    fcntl(fd, F_SETFD, FD_CLOEXEC);
    fcntl(fd, F_SETFL, O_NONBLOCK);
    listen(fd, 8);
    property_set_fd = fd;
}

5.3 system_properties.c解析

属性存储在混合(trie)字典树 /(binary)二叉树结构中。每个属性的名称都以'.'分隔,字符和标记放在一起成为一个特里结构。 特里结构的每个级别的兄弟姐妹都存储在一个二叉树。例如,“ro.secure”=“1”可以存储如下:

11.png



特里结构的根结点是空的,后面第二个节点为ro,ro结点是一个二叉树的根结点,它的左孩子为net,右孩子为sys。ro的后驱结点为secure,它也是一个二驻树的根结点,左孩子为com,右孩子为prop,值为1。这样把prop前面的串起来就是ro.secure=1。

bionic/libc/include/sys/system_properties.h

typedef struct prop_info prop_info;
//prop key和value的最大长度
#define PROP_NAME_MAX   32
#define PROP_VALUE_MAX  92

bionic/libc/include/sys/_system_properties.h

#define PROP_AREA_MAGIC   0x504f5250
#define PROP_AREA_VERSION 0xfc6ed0ab
#define PROP_AREA_VERSION_COMPAT 0x45434f76
#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"
#define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"
#define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"
#define PROP_PATH_FACTORY          "/factory/factory.prop"
//定义映射内存空间的大小128K
#define PA_SIZE         (128 * 1024)
//prop service的名称
#define PROP_SERVICE_NAME "property_service"
//prop内存映射文件
#define PROP_FILENAME "/dev/__properties__"

bionic/libc/bionic/system_properties.c

static char property_filename[PATH_MAX] = PROP_FILENAME;
//property共享内存结构体
struct prop_area {
    unsigned bytes_used;  //已使用的字节数
    unsigned volatile serial; //serial 表示整个 prop_area 被修改的次数,包括
新增加的
    unsigned magic;
    unsigned version;
    unsigned reserved[28];
    char data[0];  //实际的共享内存区域
};
//定义全局变量
typedef struct prop_area prop_area;
//property info结构体
struct prop_info {
    //prop_info 中的 serial,其高8 位表示该 prop_info 中 name 的长度,而低 24 位表示该 prop_info 被更新的次数
    unsigned volatile serial;
    char value[PROP_VALUE_MAX];
    char name[0];
};
//定义全局变量
typedef struct prop_info prop_info;
//初始化共享内存
int __system_property_area_init()
{
    return map_prop_area_rw();
}
//初始化可读写的共享内存区域
static int map_prop_area_rw()
{
    prop_area *pa; //prop_area结构体定义在该文件中
    int fd;
    int ret;
    /* dev is a tmpfs that we can use to carve a shared workspace
     * out of, so let's do that...
     */
    //dev是一个临时文件系统,我们可以用来定制共享工作区
    fd = open(property_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 -1;
    }
    //fcntl 根据文件描述词来操作文件特性,给 init_workspace 中创建的文件描述符设置文件描述符标记
    ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
    if (ret < 0)
        goto out;
    if (ftruncate(fd, PA_SIZE) < 0)
        goto out;
    pa_size = PA_SIZE;  //设置映射内存空间大小
    pa_data_size = pa_size - sizeof(prop_area);  //数据空间大小=总空间-prop_area结构体占用的空间
    compat_mode = false;
    /*映射为共享内存,mmap将一个文件或者其它对象映射进内存
      *这里是关键的部分,关于mmap可以参考(https://blog.csdn.net/yangle4695/article/details/52139585)
    */
    /*参数<映射的内存起始地址为NULL=0,映射大小,可读|可写,
      *MAP_SHARED :对映射区域的写入数据会复制回文件内, 而且允许其他映射该文件的进程共享
      * fd文件描述符, offset:表示被映射对象(即文件)从那里开始对映,通常都是用0>
      * 返回被映射区的指针pa
      */
    pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(pa == MAP_FAILED)
        goto out;
    memset(pa, 0, pa_size);  //初始化映射内存区域为 0
    pa->magic = PROP_AREA_MAGIC;
    pa->version = PROP_AREA_VERSION;
    /* reserve root node */
    pa->bytes_used = sizeof(prop_bt);
    /* plug into the lib property services */
    //将 pa 赋值给__system_property_area__
    __system_property_area__ = pa;
    close(fd);
    return 0;
out:
    close(fd);
    return -1;
}
//查询prop
const prop_info *__system_property_find(const char *name)
{
    if (__predict_false(compat_mode)) {
        return __system_property_find_compat(name);
    }
    return find_property(root_node(), name, strlen(name), NULL, 0, false);
}
//前面介绍过了,属性的存储结构为特里结构+二叉树结构,所以查找属性其实就是
//特里结构+二叉树结构的遍历 
static const prop_info *find_property(prop_bt *trie, const char *name,
        uint8_t namelen, const char *value, uint8_t valuelen,
        bool alloc_if_needed)
{
    const char *remaining_name = name;
    while (true) {
        char *sep = strchr(remaining_name, '.');
        bool want_subtree = (sep != NULL);
        uint8_t substr_size;
        prop_bt *root;
        if (want_subtree) {
            substr_size = sep - remaining_name;
        } else {
            substr_size = strlen(remaining_name);
        }
        if (!substr_size)
            return NULL;
        if (trie->children) {
            root = to_prop_obj(trie->children);
        } else if (alloc_if_needed) {
            root = new_prop_bt(remaining_name, substr_size, &trie->children);
        } else {                                                                               
            root = NULL;
        }
        if (!root)
            return NULL;
        trie = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
        if (!trie)
            return NULL;
        if (!want_subtree)
            break;
        remaining_name = sep + 1;
    }
    if (trie->prop) {
        return to_prop_obj(trie->prop);
    } else if (alloc_if_needed) {
        return new_prop_info(name, namelen, value, valuelen, &trie->prop);
    } else {
        return NULL;
    }
}

12.png

5.4 init_parser.c解析

system/core/init/init_parser.c

static list_declare(service_list);
static list_declare(action_list);  //声明action list
static list_declare(action_queue);
//参数<函数指针,函数指针指向函数的参数>
void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
{
    struct action *act;  //定义在init.h中
    struct command *cmd;  //定义在init.h中
    act = calloc(1, sizeof(*act));  //act分配内存
    act->name = name;  //Android 系统属性服务来说,该值为 property_service_init
    list_init(&act->commands);  //初始化,将 listnode 的 next 和 prev 都指向自
    list_init(&act->qlist);  //初始化qlist
    cmd = calloc(1, sizeof(*cmd));  //cmd分配内存
    cmd->func = func;  //设置函数指针,对于属性服务为property_service_init_action函数
    cmd->args[0] = name; //args 表示函数运行的参数值,此处只有一个name参数
    list_add_tail(&act->commands, &cmd->clist); //在action的commands链表的尾部添加节点cmd->clist
    //action_list尾部添加节点act->alist
    list_add_tail(&action_list, &act->alist);
    //
    action_add_queue_tail(act);
}
void action_add_queue_tail(struct action *act)
{
    if (list_empty(&act->qlist)) {  //如果链表为空则添加
        list_add_tail(&action_queue, &act->qlist);
    }
}
//从action_queue获取头节点,并删除头结点 
struct action *action_remove_queue_head(void)
{
    if (list_empty(&action_queue)) {
        return 0;
    } else {
        struct listnode *node = list_head(&action_queue);
        struct action *act = node_to_item(node, struct action, qlist);
        list_remove(node);
        list_init(node);
        return act;
    }
}

queue_builtin_action方法执行完后,action 和 command 结构体以及 action_list 和 action_queue 的关系如下图(queue_builtin_action 在 此 处 并 不 是 最 后 一 次 被 调 用 , 因 此 action_list 以 及action_queue 和 action 之间用虚点线表示,action_list 和 action_queue 之前还有其他的 action结构体变量)


13.png5.5 cutils库解析

目录位置system/core/libcutils,是一些c的函数工具库,现在介绍一些涉及到的文件

system/core/include/cutils/list.h

//可构成双向链表
struct listnode
{   
    struct listnode *next;  
    struct listnode *prev;
};
//宏定义,声明并初始化链表
#define list_declare(name) \
    struct listnode name = { \
        .next = &name, \
        .prev = &name, \
    }

system/core/libcutils/list.c

#include <cutils/list.h>
//初始化链表
void list_init(struct listnode *node)
{
    node->next = node;
    node->prev = node;
}
//末尾添加节点
void list_add_tail(struct listnode *head, struct listnode *item)
{
    item->next = head;
    item->prev = head->prev;
    head->prev->next = item;
    head->prev = item;
}
//移除节点
void list_remove(struct listnode *item)
{
    item->next->prev = item->prev;
    item->prev->next = item->next;
}


六、参考


Android属性系统

Linux 内存映射函数 mmap

trie

Linux 内存映射函数 mmap()函数详解

Android属性系统


目录
相关文章
|
23天前
|
人工智能 搜索推荐 物联网
Android系统版本演进与未来展望####
本文深入探讨了Android操作系统从诞生至今的发展历程,详细阐述了其关键版本迭代带来的创新特性、用户体验提升及对全球移动生态系统的影响。通过对Android历史版本的回顾与分析,本文旨在揭示其成功背后的驱动力,并展望未来Android可能的发展趋势与面临的挑战,为读者呈现一个既全面又具深度的技术视角。 ####
|
21天前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
8天前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
10天前
|
存储 安全 Android开发
探索Android系统的最新安全特性
在数字时代,智能手机已成为我们生活中不可或缺的一部分。随着技术的不断进步,手机操作系统的安全性也越来越受到重视。本文将深入探讨Android系统最新的安全特性,包括其设计理念、实施方式以及对用户的影响。通过分析这些安全措施如何保护用户免受恶意软件和网络攻击的威胁,我们希望为读者提供对Android安全性的全面了解。
|
23天前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
56 16
|
15天前
|
安全 Android开发 iOS开发
深入探讨Android与iOS系统的差异及未来发展趋势
本文旨在深入分析Android和iOS两大移动操作系统的核心技术差异、用户体验以及各自的市场表现,进一步探讨它们在未来技术革新中可能的发展方向。通过对比两者的开放性、安全性、生态系统等方面,本文揭示了两大系统在移动设备市场中的竞争态势和潜在变革。
|
1月前
|
算法 JavaScript Android开发
|
1月前
|
安全 搜索推荐 Android开发
揭秘安卓与iOS系统的差异:技术深度对比
【10月更文挑战第27天】 本文深入探讨了安卓(Android)与iOS两大移动操作系统的技术特点和用户体验差异。通过对比两者的系统架构、应用生态、用户界面、安全性等方面,揭示了为何这两种系统能够在市场中各占一席之地,并为用户提供不同的选择。文章旨在为读者提供一个全面的视角,理解两种系统的优势与局限,从而更好地根据自己的需求做出选择。
82 2
|
1月前
|
安全 搜索推荐 程序员
深入探索Android系统的碎片化问题及其解决方案
在移动操作系统的世界中,Android以其开放性和灵活性赢得了广泛的市场份额。然而,这种开放性也带来了一个众所周知的问题——系统碎片化。本文旨在探讨Android系统碎片化的现状、成因以及可能的解决方案,为开发者和用户提供一种全新的视角来理解这一现象。通过分析不同版本的Android系统分布、硬件多样性以及更新机制的影响,我们提出了一系列针对性的策略,旨在减少碎片化带来的影响,提升用户体验。
|
1月前
|
安全 Android开发 iOS开发
深入探索iOS与Android系统的差异性及优化策略
在当今数字化时代,移动操作系统的竞争尤为激烈,其中iOS和Android作为市场上的两大巨头,各自拥有庞大的用户基础和独特的技术特点。本文旨在通过对比分析iOS与Android的核心差异,探讨各自的优势与局限,并提出针对性的优化策略,以期为用户提供更优质的使用体验和为开发者提供有价值的参考。