静态的程序代码和程序运行时的状态,有很大的不同。理解代码,要理解其运行时的样子。
这一篇主要说说 android property service 的运行时状态。
先来看看这个图大致理解一下:

可以查看下手机的 /dev/__properties__ 文件(Kitkat 4.4) :
| 1 2 |  | 
Init 进程,创建 /dev/__properties__ 文件,map 到内存,然后从 /default.prop等文件中加载 properties, 写入 /dev/__properties__. 然后启动 property_service , 就是建立一个 property_service 的 socket ,监听这个 socket ,其他进程通过向这个 socket 发送 proper_set 的消息来完成 properties 的设置。
proper_get 是在每个进程在初始化时(libc中) 建立了 /dev/__properties__到内存的 map ,得到了可以直接访问的 address,可以直接遍历 properties 的存储空间完成查找.
下面细细到来:
Init 进程,Android 用户空间的第一个进程,内核启动之后会执行这个 init 程序。进入到 init 的main 函数。 在 init.c 的 main 函数里面,初始化 property 相关的存储空间。
system/core/init/init.c
| 1
 |  | 
property_init 是调用 property_service.c 的方法。
/system/core/init/property_service.c
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |  | 
__system_property_area_init 方法在 bionic/libc/bionic/system_properties.c
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |  | 
首先创建 /dev/__properties__ ,并且用 mmap 映射到内存.(通过变量__system_property_area__ 的共享,完成 property_service 和 libc 中 properties 相关的交互。)
| 1 2 3 4 5 6 7 8 9 10 |  | 
回到 init.c , 加载 boot defults properties:
init.c
| 1
 |  | 
property_service.c:
| 1 2 3 4 5 6 |  | 
load_properties_from_file 这个里面就是读取文件,然后 set_property。
回到 init.c , 启动 property service :
init.c:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |  | 
在 start_property_service 中,加载 properties (/system/build.prop, /system/default.prop, /data/property/xxx),创建 /dev/socket/property_service 这个 socket, 并且监听这个 socket 来接受 set property 的消息。
property_service.c:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |  | 
回到 init.c :
init 进程在 main 函数的最后,进入一个无限循环,等待 /dev/socket/property_service 和其他 fd 的事件并处理。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |  | 
handler_property_set_fd 里面接收 /dev/socket/property_service 的消息并处理。
property_service.c:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |  | 
在 property_set 之前会 check_perms, 不同的 property_set 需要什么权限呢?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |  | 
接着就 property_set:
先查询有的话,就 __system_property_update , 没有就 __system_property_add ,如果是 persist.的话,要 write_persistent_property 写入到 /data/property/xxx
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |  | 
OK, proper_set 流程先到这里,下面来看看 proper_get . proper_get 直接向下到 libc 的 __system_property_get 的 api.
/bionic/libc/bionic/system_properties.c
| 1 2 3 4 5 6 7 8 9 10 11 |  | 
这里是调用了 __system_property_find 来查找这个值。在往下看:
| 1 2 3 4 5 6 7 |  | 
这里调用 find_property 来访问 root_node(), 什么是 root_node()?
| 1 2 3 4 5 6 7 8 9 10 11 12 |  | 
实际上获取 __system_property_area__->data的地址开始访问。OK, __system_property_area__ 是哪里? 正是 /dev/properties map 到内存的地址。
init.c 进程调用了 system_properties.c 的 __system_property_area_init –> map_prop_area_rw 在这里:
创建 property_filename(/dev/_-properties__), mmap 到内存, 将地址赋值给 __system_property_area__ 。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 |  | 
但是,慢着! 这些都是在 init 进程里面执行的,其他进程调用 __system_property_find 怎么可以直接访问 __system_property_area__ ? 难道 其他进程和 init 共享了这个地址,这是不可能的,这个变量怎会传递到 init 的子进程?就算传递了,它们怎么可能访问相同的地址呢?它们可是不同进程啊,各自使用自己独享的内存空间啊!
所以,呵呵! 其他进程肯定也对 __system_property_area__ 进行初始化了! 在哪里 ?
/bionic/libc/bionic/libc_init_common.cpp 中 调用了 __system_properties_init()
| 1 2 3 4 |  | 
/bionic/libc/bionic/system_properties.c
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |  | 
以 RDONLY 模式打开了 /dev/__properties__ 文件,并且 mmap 到内存,将地址赋值给 __system_property_area__ ! 所以,其他进程可以直接访问 __system_property_area__ 不过这个肯定和 init 进程里那个是不同的!!
OK! 结束,下一篇讲讲 property 存储区的数据结构 !