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”可以存储如下:
特里结构的根结点是空的,后面第二个节点为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; } }
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结构体变量)
5.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属性系统