Android init进程——属性服务

本文详细介绍了Android系统中init进程的属性服务,包括属性服务的作用、初始化过程和客户端如何访问及设置属性。重点分析了属性内存区域的创建、初始化以及客户端如何通过属性服务器进行属性设置的交互。
摘要由CSDN通过智能技术生成

目录


概述

init是一个进程,确切的说,它是Linux系统中用户空间的第一个进程。由于Android是基于Linux内核的,所以init也是Android系统中用户空间的第一个进程。init的进程号是1。作为天字第一号进程,init有很多重要的工作:

  1. init提供property service(属性服务)来管理Android系统的属性。
  2. init负责创建系统中的关键进程,包括zygote。

以往的文章一上来就介绍init的源码,但是我这里先从这两个主要工作开始。搞清楚这两个主要工作是如何实现的,我们再回头来看init的源码。

这篇文章主要是介绍init进程的属性服务。

跟init属性服务相关的源码目录如下:

  1. system/core/init/
  2. bionic/libc/bionic/
  3. system/core/libcutils/

属性服务

在windows平台上有一个叫做注册表的东西,它可以存储一些类似key/value的键值对。一般而言,系统或者某些应用程序会把自己的一些属性存储在注册表中,即使系统重启或应用程序重启,它还能根据之前在注册表中设置的属性值,进行相应的初始化工作。

Android系统也提供了类似的机制,称之为属性服务(property service)。应用程序可以通过这个服务查询或者设置属性。我们可以通过如下命令,获取手机中属性键值对。

adb shell getprop

例如红米Note手机的属性值如下:

[ro.product.device]: [lcsh92_wet_jb9]
[ro.product.locale.language]: [zh]
[ro.product.locale.region]: [CN]
[ro.product.manufacturer]: [Xiaomi]

在system/core/init/init.c文件的main函数中,跟属性服务的相关代码如下:

property_init();
queue_builtin_action(property_service_init_action, "property_service_init");

接下来,我们分别看一下这两处代码的具体实现。


属性服务初始化


创建存储空间

首先,我们先来看一下property_init函数的源码(/system/core/init/property_service.c):

void property_init(void)
{
    init_property_area();
}

property_init函数中只是简单的调用了init_property_area方法,接下来我们看一下这个方法的具体实现:

static int property_area_inited = 0;
static workspace pa_workspace;
static int init_property_area(void)
{
    // 属性空间是否已经初始化
    if (property_area_inited)
        return -1;

    if (__system_property_area_init())
        return -1;

    if (init_workspace(&pa_workspace, 0))
        return -1;

    fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);

    property_area_inited = 1;
    return 0;
}

从init_property_area函数,我们可以看出,函数首先判断属性内存区域是否已经初始化过,如果已经初始化,则返回-1。如果没有初始化,我们接下来会发现有两个关键函数__system_property_area_initinit_workspace应该是跟内存区域初始化相关。那我们分别分析一下这两个函数具体实现。


__system_property_area_init

__system_property_area_init函数位于/bionic/libc/bionic/system_properties.c文件中,具体代码实现如下:

struct prop_area {
    unsigned bytes_used;
    unsigned volatile serial;
    unsigned magic;
    unsigned version;
    unsigned reserved[28];
    char data[0];
};
typedef struct prop_area prop_area;
prop_area *__system_property_area__ = NULL;

#define PROP_FILENAME "/dev/__properties__"
static char property_filename[PATH_MAX] = PROP_FILENAME; 

#define PA_SIZE (128 * 1024)


static int map_prop_area_rw()
{
    prop_area *pa;
    int fd;
    int ret;

    /**
     * O_RDWR ==> 读写
     * O_CREAT ==> 若不存在,则创建
     * O_NOFOLLOW ==> 如果filename是软链接,则打开失败
     * O_EXCL ==> 如果使用O_CREAT是文件存在,则可返回错误信息
     */
    fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
    if (fd < 0) {
        if (errno == EACCES) {
            abort();
        }
        return -1;
    }

    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);
    compat_mode = false;

    // mmap映射文件实现共享内存
    pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (pa == MAP_FAILED)
        goto out;

    /*初始化内存地址中所有值为0*/
    memset(pa, 0, pa_size);
    pa->magic = PROP_AREA_MAGIC;
    pa->version = PROP_AREA_VERSION;
    pa->bytes_used = sizeof(prop_bt);

    __system_property_area__ = pa;

    close(fd);
    return 0;

out:
    close(fd);
    return -1;
}

int __system_property_area_init()
{
    return map_prop_area_rw();
}

代码比较好理解,主要内容是利用mmap映射property_filename创建了一个共享内存区域,并将共享内存的首地址赋值给全局变量__system_property_area__。

Tips
主要是想说一下struct prop_area的结构体定义很精妙。特别是char data[];的定义。这里char data[]可以理解成动态数组指针,但是它并没有被占用内存空间。所以计算pa->bytes_used的时候,并没有计算char data[]占用的空间。

但是,char data[]又是动态数组,我理解mmap分配的内存,prop_

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值