欢迎转载,转载请注明:http://blog.csdn.net/zhgxhuaa
Android中的属性主要用来保存一些全局性的信息,这里可以理解为Android中的“注册表”。Android中的属性服务只针对系统开发者使用,并不对应用开发者开发,这通过SystemProperties是hide的可以看出。下面让我们一起来剖析属性服务。
初始化属性空间
在init进程启动一文中我们讲到,init的其中一个作用就是启动系统属性服务。在init进程的main()函数中有这样一句:
@system/core/init/init.c
property_init();//属性服务初始化
property_init()的实现如下:
@system/core/init/property_service.c
void property_init(void)
{
init_property_area();
}
从字面意思来看init_property_area是用来初始化属性存储区域,让我们来看一下:
@system/core/init/property_service.c
static int init_property_area(void)
{
if (property_area_inited)//属性区域已初始化则直接返回,不再初始化
return -1;
if(__system_property_area_init())//通过mmap创建共享内存
return -1;
if(init_workspace(&pa_workspace, 0))//初始化pa_workspace
return -1;
fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
property_area_inited = 1;
return 0;
}
通过上面的代码我们可以知道,属性服务是创建在共享内存上的,通过共享内存实现跨进程的访问。那共享内存是如何创建的呢?来看一下__system_property_area_init的实现:
@/bionic/libc/bionic/system_properties.c
int __system_property_area_init()
{
return map_prop_area_rw();
}
static int map_prop_area_rw()
{
prop_area *pa;
int fd;
int ret;
/* dev is a tmpfs that we can use to carve a shared workspace
* out of, so let's do that...
*/
fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC |
O_EXCL, 0444);//打开property_filename文件,即:/dev/__properties__
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;
}
ret = fcntl(fd, F_SETFD, FD_CLOEXEC);//这里设置为FD_CLOEXEC表示当程序执行exec函数时本fd将被系统自动关闭,表示不传递给exec创建的新进程
if (ret < 0)
goto out;
if (ftruncate(fd, PA_SIZE) < 0)//更改fd指向文件的大小为PA_SIZE(128 * 1024)大小
goto out;
pa_size = PA_SIZE;//设置属性空间的大小为PA_SIZE(128 * 1024)
pa_data_size = pa_size - sizeof(prop_area);//属性空间中可以用来保存属性的区域的大小,prop_area用来保存属性空间自身的一些信息
compat_mode = false;
pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//映射属性文件到进程
if(pa == MAP_FAILED)
goto out;
memset(pa, 0, pa_size);//初始化属性区域
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 */
__system_property_area__ = pa;
close(fd);
return 0;
out:
close(fd);
return -1;
}
上面的内容比较简单,不过最后的赋值语句可是大有来头。_system_property_area_是bionic libc库中输出的一个变量,为什么这里要给她赋值呢?
原来,虽然属性区域是由init进程创建的,但是Android系统希望其他进程也能够读取这块内存的东西。为了做到这一点,它便做了一下两项工作:
- 把属性区域创建在共享内存上,而共享内存是可以跨进程的。
- 如何让其他进程知道这个共享内存呢?Android利用gcc的constructor属性,这个属性指明了一个_libc_prenit函数,当bionic libc库被加载时,将自动调用这个_libc_prenit,这个函数内部完成共享内存到本地进程的映射工作。(这一段说明转自:《深入理解Android:卷1》)
关于上面的内容来看下面的代码:
@/bionic/libc/bionic/libc_init_dynamic.cpp// We flag the __libc_preinit function as a constructor to ensure
// that its address is listed in libc.so's .init_array section.
// This ensures that the function is called by the dynamic linker
// as soon as the shared library is loaded.
__attribute__((constructor)) static void __libc_preinit() {
// Read the kernel argument block pointer from TLS.
void* tls = const_cast<void*>(__get_tls());
KernelArgumentBlock** args_slot = &reinterpret_cast<KernelArgumentBlock**>(tls)[TLS_SLOT_BIONIC_PREINIT];
KernelArgumentBlock* args = *args_slot;
// Clear the slot so no other initializer sees its value.
// __libc_init_c