1.首先init()进程在启动的过程对属性服务做了如下操作:

1)property_init(); //创建属性存储空间
2)property_load_boot_defaults(); //从 “/default.prop”中加载默认属性
3)queue_builtin_action(property_service_init_action, “property_service_init”); //初始化属性服务,创建名为“property_service”的socket,该socket文件存放在“/dev/socket/”目录下。然后绑定socket,开始监听。
4)queue_builtin_action(queue_property_triggers_action, “queue_property_triggers”); //首先将“queue_property_triggers”插入到执行action队列,这个action对应的执行函数位“queue_property_triggers_action”

2.文件系统-知识补充:

1)tmpfs文件系统
tmpfs是一种虚拟的内存文件系统,它会将所有的文件存储在虚拟内存中,并且tmpfs下的所有文件均为临时性文件,tmpfs是一个独立的文件系统,不是快设备,只要挂载即可使用。它驻留在RAM中,大小自动变化,断电后tmpfs的内容就会消失。

2)devpts文件系统
devpts文件系统为伪终端(pty-pseudo-terminal slave)提供了一个标准接口,它的标准挂接点是/dev/pts。伪终端是成对的逻辑终端设备(即master主和slave从设备,对master的操作会反应到slave上)

3)proc文件系统
proc是一个非常重要的文件系统,它可以看做是内核内部数据的接口,通过它可以获取系统的信息,同时也能在运行时修改特定的参数,只需要在对应的文件中添加新的值即可,如果修改失败,则只能重新启动设备。

4)sysfs文件系统
与proc文件系统相似,sysfs文件系统也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂载到/sys目录下。sysfs文件系统是linux2.6内核引入,它把连接到系统上的设备和总线组织为一个分级的文件,使得他们可以在用户空间存取。

在init进程开始时,创建了三个文件夹,分别为/dev,/proc和/sys。之后又创建了/dev/pts和/dev/socket两个文件夹,分别用于伪终端和用于与服务通信的socket。以上四中文件系统均被挂载到制定的目录上。

3.下面针对上面四点逐一解析;
1)创建属性存储空间

void property_init(void){    init_property_area();}

init_property_area()函数如下:

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;}

其中property_area_inited标记属性存储区域是否初始化,__system_property_area_init()函数如下,该函数位于“bionic/libc/bionic/system_properties.cpp”中:

int __system_property_area_init(){    return map_prop_area_rw();}

map_prop_area_rw()函数如下:

static int map_prop_area_rw(){    /* dev is a tmpfs that we can use to carve a shared workspace     * out of, so let's do that...     */    const int 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;    }    // TODO: Is this really required ? Does android run on any kernels that    // don't support O_CLOEXEC ?    const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);    if (ret < 0) {        close(fd);        return -1;    }    if (ftruncate(fd, PA_SIZE) < 0) {        close(fd);        return -1;    }    pa_size = PA_SIZE;    pa_data_size = pa_size - sizeof(prop_area);    compat_mode = false;    void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);    if (memory_area == MAP_FAILED) {        close(fd);        return -1;    }    prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);    /* plug into the lib property services */    __system_property_area__ = pa;    close(fd);    return 0;}

由于dev是tmpfs系统,所以可以用来开辟一块共享存储空间。map_prop_area_rw()函数首先创建属性文件“/dev/_properties_”,大小PA_SIZE为128*1024,然后将该文件的对象映射进内存。由于属性是存放在一个混合的线索/二进制树结构(prop_bt)中,所以在内存区域中new了一个prop_area的头节点,并赋给了变量”_system_property_area_“.
之后,init_workspace()函数初始化工作空间,其中workspace的结构体如下:

typedef struct {    size_t size;    int fd;} workspace;

此时,fd为之间创建的属性文件的open操作的句柄。size为初始化指定大小。最后将“property_area_inited”置为1,至此,属性存储空间开辟完成。

4.从 “/default.prop”中加载默认属性

void property_load_boot_defaults(void){    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);}

其中PROP_PATH_RAMDISK_DEFAULT文件为”/default.prop”

static void load_properties_from_file(const char *fn, const char *filter){    char *data;    unsigned sz;    data = read_file(fn, &sz);    if(data != 0) {        load_properties(data, filter);         free(data);    }}

read_file()即我们平时普通的读文件操作。load_properties函数采用递归的方式解析读取内容中的key-value对。次函数不予解释。

5.初始化属性服务

queue_builtin_action(property_service_init_action, "property_service_init");

这里,queue_builtin_action的第一个参数是一个函数,第二个参数是action的名称以及命令队列中的名称。

static int property_service_init_action(int nargs, char **args){    /* read any property files on system or data and     * fire up the property service.  This must happen     * after the ro.foo properties are set above so     * that /data/local.prop cannot interfere with them.     */    start_property_service();    if (get_property_set_fd() < 0) {         ERROR("start_property_service() failed\n");        exit(1);    }    return 0;}

如注释所说,读取系统中所有的属性文件,启动属性服务。

void start_property_service(void){    int fd;    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL);    if(fd < 0) return;    fcntl(fd, F_SETFD, FD_CLOEXEC);    fcntl(fd, F_SETFL, O_NONBLOCK);    listen(fd, 8);    property_set_fd = fd;}

创建与属性服务通信的socket,创建函数如下:

/* * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR * ("/dev/socket") as dictated in init.rc. This socket is inherited by the * daemon. We communicate the file descriptor's value via the environment * variable ANDROID_SOCKET_ENV_PREFIX ("ANDROID_SOCKET_foo"). */int create_socket(const char *name, int type, mode_t perm, uid_t uid,                  gid_t gid, const char *socketcon){    struct sockaddr_un addr;    int fd, ret;    char *filecon;    if (socketcon)        setsockcreatecon(socketcon);    fd = socket(PF_UNIX, type, 0);     if (fd < 0) {        ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));        return -1;    }    if (socketcon)        setsockcreatecon(NULL);    memset(&addr, 0 , sizeof(addr));    addr.sun_family = AF_UNIX;    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",             name);    ret = unlink(addr.sun_path);    if (ret != 0 && errno != ENOENT) {        ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno));        goto out_close;    }    filecon = NULL;    if (sehandle) {        ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK);        if (ret == 0)            setfscreatecon(filecon);    }    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));    if (ret) {        ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));        goto out_unlink;    }    setfscreatecon(NULL);    freecon(filecon);    chown(addr.sun_path, uid, gid);    chmod(addr.sun_path, perm);    INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",         addr.sun_path, perm, uid, gid);    return fd;out_unlink:    unlink(addr.sun_path);out_close:    close(fd);    return -1;}

在“/dev/socket”目录下创建一个Unix domain socket(同一台主机进程间通信,不需要网络协议,打包拆包等操作,只是将应用层数据从一个进程拷贝到另一个进程)Unix Domain Socket有SOCK_DGRAM或SOCK_STREAM两种工作模式,类似于UDP和TCP。
UNIX Domain socket与网络socket类似,可以与网络socket对比应用。不同如下:

1.address family为AF_UNIX(PF_UNIX的宏)
2.因为应用与IPC,所以Unix Domain socket不需要IP和端口,取而代之的是文件路径表示网络地址

用socket函数创建套接字之后,要绑定该socket和路径(此处有待斟酌),最后监听该端口

6.将“queue_property_triggers”插入到执行action队列

static int queue_property_triggers_action(int nargs, char **args){    queue_all_property_triggers();    /* enable property triggers */    property_triggers_enabled = 1;     return 0;}

queue_all_property_triggers()函数如下:

void queue_all_property_triggers(){    struct listnode *node;    struct action *act;    list_for_each(node, &action_list) {        act = node_to_item(node, struct action, alist);        if (!strncmp(act->name, "property:", strlen("property:"))) {            /* parse property name and value               syntax is property:<name>= */            const char* name = act->name + strlen("property:");            const char* equals = strchr(name, '=');            if (equals) {                char prop_name[PROP_NAME_MAX + 1];                 char value[PROP_VALUE_MAX];                int length = equals - name;                if (length > PROP_NAME_MAX) {                    ERROR("property name too long in trigger %s", act->name);                } else {                    int ret;                    memcpy(prop_name, name, length);                    prop_name[length] = 0;                    /* does the property exist, and match the trigger value? */                    ret = property_get(prop_name, value);                    if (ret > 0 && (!strcmp(equals + 1, value) ||                                    !strcmp(equals + 1, "*"))) {                        action_add_queue_tail(act);                    }                }            }        }    }}

该函数主要将解析init.rc文件得到的action列表中的property_action插入到action队列的末尾。

以上即为init进程中属性服务的解释,写这篇博客的目的主要是刚开始学习android源代码,找到方法才是最重要的。并以此鼓励自己。

更多相关文章

  1. 深入源码分析non-sdk并绕过Android(安卓)9.0反射限制
  2. Android(安卓)WebView中软键盘会遮挡输入框相关问题
  3. Android(安卓)XML属性在文档中的位置
  4. 实现activity全屏显示
  5. Android设置EditText输入类型:setInputType()方法和android:input
  6. Android动画知识汇总
  7. Android(安卓)Netd
  8. Android(安卓)解决自定义控件布局中match_parent属性无效
  9. Task和Activity相关的一些属性

随机推荐

  1. 科普NA公链(Nirvana):NAC公链采用基于NIO的N
  2. DolphinDB脚本语言的混合范式编程
  3. 如何避免ajax重复请求?
  4. HTML、HTML5重难点
  5. 数据调度组件:基于Azkaban协调时序任务执
  6. 7个最佳CSS优化技巧,可缩短页面加载时间
  7. 软测经典面试题(二)
  8. HBase优化 | 合理的使用编码压缩
  9. HBase重磅 | ApsaraDB HBase数据存储与分
  10. 快速掌握 10 个 HTML5 必备技巧