Property Service学习(一)

                                                 PropertyService

一、SystemProperty

属性服务是android系统中重要的一个服务,主要是起到属性的管理,以及处理属性状态变化。每个属性都是一个key-value的形式存在。许多程序的运行和动作的执行都依赖与Property Service。以开机动画为例,在android开机动画显示的过程时,SurfaceFlinger服务就是通过修改ctrl.start和ctrl.stop属性值来启动和停止开机动画。Setprop(“ctl.start”,“bootanim”);

 

二、SystemProperty  code:

1、  FrameWork

Framework通过SystemProperties提供的接口操作系统属性,通过JNI调用native service的方法。

路径:JB/frameworks/base/core/java/android/os/SystemProperties.java

2、  JNI

         JNI通过android_os_SystemProperties.cpp为Framework提供接口。

路径:JB/frameworks/base/core/jni/android_os_SystemProperties.cpp

         JNI中调用的接口:JB/system/core/libcutils/properties.c 

         任何进程操作系统属性都需要通过properties.c中的property_set和property_get,最终生成libcutils.so

3、  PropertyService

属性服务其实没有独立的运行的进程,其实是和init进程是同一个进程,即在init完成了所有的初始化动作的时候就进入了一个死循环,监听系统属性的变化,子进程运行状态等。

       Property服务路径:JB/system/core/init/init.c

JB/system/core/init/ property_service.c

三、SystemProperty 服务

1、  System Property 服务以及监听的创建

init.c main函数中:

int main(intargc, char **argv)
{
 //创建属性信息的共享内存
    property_init();
//将property service的启动增加到action queue中,执行该函数后会创建socket server端
    queue_builtin_action(property_service_init_action,"property_service_init"); 
     //进入主循环中
    *  - 查询action队列并执行。
     *  - 重启需要重启的服务
     *  - 轮询属性事件
    for(;;) {
        int nr, i, timeout = -1;
        execute_one_command(); //执行action queue中的方法
        restart_processes();
        nr = poll(ufds, fd_count, timeout);
        if (nr <= 0)
            continue;
 
        for (i = 0; i < fd_count; i++) {
            if (ufds[i].revents == POLLIN) {
                if (ufds[i].fd ==get_property_set_fd())
                    handle_property_set_fd();// property 监听socketserver端
                else if (ufds[i].fd ==get_keychord_fd())
                    handle_keychord();
                else if (ufds[i].fd ==get_signal_fd())
                    handle_signal();
            }
        }
    }
    return 0;
}
 


voidstart_property_service(void)
{
    int fd;
    //加载属性配置文件
   load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
    load_override_properties();
    load_persistent_properties();
//创建socket server
    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;
}


其中property server创建的共享内存用来保存属性的key-value的值。由此可见,property service是运行在init进程中的。并通过建立socket通信来实现property的管理,而具体是如何实现的?

2、  Property 监听 socket

  handle_property_set_fd();

void handle_property_set_fd()
{
       //等待建立通信
    if ((s = accept(property_set_fd, (structsockaddr *) &addr, &addr_size)) < 0) {
        return;
    }
    //检查 socket option信息
    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED,&cr, &cr_size) < 0) {
        close(s);
        ERROR("Unable to recieve socketoptions\n");
        return;
    }
    //接受 client端发送的属性设置信息
    r = TEMP_FAILURE_RETRY(recv(s, &msg,sizeof(msg), 0));
    if(r != sizeof(prop_msg)) {
        ERROR("sys_prop: mis-match msgsize recieved: %d expected: %d errno: %d\n",
              r, sizeof(prop_msg), errno);
        close(s);
        return;
    }
    switch(msg.cmd) {
    case PROP_MSG_SETPROP:
        msg.name[PROP_NAME_MAX-1] = 0;
        msg.value[PROP_VALUE_MAX-1] = 0;
        //处理ctl.开头的信息
        if(memcmp(msg.name,"ctl.",4)== 0) {
            // Keep the old close-socket-earlybehavior when handling
            // ctl.* properties.
            close(s);
            if (check_control_perms(msg.value,cr.uid, cr.gid, source_ctx)) {
                handle_control_message((char*)msg.name + 4, (char*) msg.value);
            } else {
                ERROR("sys_prop: Unable to%s service ctl [%s] uid:%d gid:%d pid:%d\n",
                        msg.name + 4,msg.value, cr.uid, cr.gid, cr.pid);
            }
        } else {
           //检查uid,gid,判断进程的权限
            if (check_perms(msg.name, cr.uid,cr.gid, source_ctx)) {
            //执行set命令,修改系统属性值
                property_set((char*) msg.name,(char*) msg.value);
            }
          else {
                ERROR("sys_prop:permission denied uid:%d name:%s\n",
                      cr.uid, msg.name);
            }
 
            // Note: bionic's property clientcode assumes that the
            // property server will not closethe socket until *AFTER*
            // the property is written tomemory.
            close(s);
        }
        break;
    default:
        close(s);
        break;
    }
}


3、  属性的修改

如上可知系统属性都需要通过properties.c中的property_set和property_get这两个方法的操作,如何通过这两个方法和property端的socket server进行异步的通讯呢?首先看一下property_se这个方法:

intproperty_set(const char *key, const char *value)
{
    if(strcmp(key,"net.dns1")==0) {
        char ethDnsInfo[20];
        char dnsinfo[100];
        int len =__system_property_get("net.eth0.dns1", ethDnsInfo);
        if( len > 0 &&strcmp(value,ethDnsInfo) != 0)
            snprintf(dnsinfo, sizeof(dnsinfo),"nameserver %s\nnameserver %s",ethDnsInfo,value);
        else        
            snprintf(dnsinfo, sizeof(dnsinfo),"nameserver %s",value);
        FILE *p = NULL;
        p =fopen("/etc/resolv.conf","w");
        if (p != NULL)
            fputs(dnsinfo,p);
        fclose(p);
    }
    //通过如下的函数进行设定
    return __system_property_set(key,value);
}


进一步分析:

int__system_property_set(const char *key, const char *value)
{
    //最终通过这个函数进行发送
    err = send_prop_msg(&msg);
    if(err< 0) {
        return err;
    }
    return 0;
}


深追到底:

static intsend_prop_msg(prop_msg *msg)
{
   //创建socket
    s = socket(AF_LOCAL, SOCK_STREAM, 0);
    if(s < 0) {
        return result;
    }
   //连接socket server
    if(TEMP_FAILURE_RETRY(connect(s, (structsockaddr *) &addr, alen)) < 0) {
        close(s);
        return result;
    }
   //发送属性设置信息
    r = TEMP_FAILURE_RETRY(send(s, msg,sizeof(prop_msg), 0));
 
    if(r == sizeof(prop_msg)) {
        pollfds[0].fd = s;
        pollfds[0].events = 0;
        r = TEMP_FAILURE_RETRY(poll(pollfds, 1,250 /* ms */));
        if (r == 1 && (pollfds[0].revents& POLLHUP) != 0) {
            result = 0;
        } else {
            result = 0;
        }
    }
    close(s);
    return result;
}


如上我们终于看到了整个socket通信的过程了,通过socket完成了属性设置的整个执行动作。通过property_set向property service中的socket server发送属性更改的命令,然后在property中完成属性的设置或者增加,直接以读写的方式操作共享内存。而对于get是如何实现的?

4、  属性的获取:

还是按照上面的方法,逐步的去分析

intproperty_get(const char *key, char *value, const char *default_value)
{
    int len;
   //获取属性值
    len = __system_property_get(key, value);
    if(len > 0) {
        return len;
    }
    if(default_value) {
        len = strlen(default_value);
        memcpy(value, default_value, len + 1);
    }
    return len;
}


进一步深入:

int__system_property_get(const char *name, char *value)
{
    //如下的步骤主要是获取共享内存中name所在的位置,后面再进行分析
    constprop_info *pi = __system_property_find(name);
    if(pi != 0) {
    //读取name对应的value值
        return __system_property_read(pi, 0,value);
    } else {
        value[0] = 0;
        return 0;
    }
}


再深入往下看

int__system_property_read(const prop_info *pi, char *name, char *value)
{
    unsigned serial, len;
   
    for(;;) {
        serial = pi->serial;
        //阻塞等待,防止property service修改name对应的属性值
        while(SERIAL_DIRTY(serial)) {
            __futex_wait((volatile void*)&pi->serial, serial, 0);
            serial = pi->serial;
        }
        len = SERIAL_VALUE_LEN(serial);
        //仅仅执行了一个拷贝动作
        memcpy(value, pi->value, len + 1);
        if(serial == pi->serial) {
            if(name != 0) {
                strcpy(name, pi->name);
           }
            return len;
        }
    }
}


很失望吧,没有看到类似属性修改中socket的异步操作,这点确实很困惑。既然属性修改已经可以通过socket完成异步的操作,而属性获取为什么会这个诡异呢!

后来通过网上前辈们的帖子才明白了,这样设计的优点了。在android的应用运行和操作中,属性的获取的使用频率会远远大于属性修改的使用频率,既然大量的使用属性获取的操作,那么就需要考虑在操作中的效率问题。

为了提高get动作的执行效率,那么可以直接已只读的方式访问共享内存中保存的数据信息。而set动作的由于执行的频率较低,所以通过socket的方式进行操作。但是为了防止在get的时候,propertyservice中其他进程修改正在获取的属性,造成读写冲突,因此在get的时候增加了一个wait的动作。

四、SystemProperty 服务中属性操作的框架

 

如图 set动作通过socket机制,发送属性更改信息给property service,propery service直接操作共享内存进行修改。

Get动作直接操作共享内存,已只读的方式获取了对应的值。

至于如何进行跨进程的共享内存的操作,后面继续奋斗。

 

参考:

1、  http://leave001.blog.163.com/blog/static/1626912932013030101531571/

2、  http://www.cnblogs.com/bastard/archive/2012/10/11/2720314.html

 

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值