init进程【3】——属性服务
来源:互联网 发布:雅思网络培训班 编辑:程序博客网 时间:2024/06/07 23:25
【转载自这里】:http://blog.csdn.net/zhgxhuaa
Android中的属性主要用来保存一些全局性的信息,这里可以理解为Android中的“注册表”。Android中的属性服务只针对系统开发者使用,并不对应用开发者开发,这通过SystemProperties是hide的可以看出。下面让我们一起来剖析属性服务。
初始化属性空间
在init进程启动一文中我们讲到,init的其中一个作用就是启动系统属性服务。在init进程的main()函数中有这样一句:
@system/core/init/init.c
- property_init();//属性服务初始化
@system/core/init/property_service.c
- void property_init(void)
- {
- 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_common() will change the TLS area so the old one won't be accessible anyway.
- *args_slot = NULL;
- __libc_init_common(*args);
- // Hooks for the debug malloc and pthread libraries to let them know that we're starting up.
- pthread_debug_init();
- malloc_debug_init();
- }
@/bionic/libc/bionic/libc_init_common.cpp
- void __libc_init_common(KernelArgumentBlock& args) {
- // Initialize various globals.
- environ = args.envp;
- errno = 0;
- __libc_auxv = args.auxv;
- __progname = args.argv[0] ? args.argv[0] : "<unknown>";
- __abort_message_ptr = args.abort_message_ptr;
- // AT_RANDOM is a pointer to 16 bytes of randomness on the stack.
- __stack_chk_guard = *reinterpret_cast<uintptr_t*>(getauxval(AT_RANDOM));
- // Get the main thread from TLS and add it to the thread list.
- pthread_internal_t* main_thread = __get_thread();
- main_thread->allocated_on_heap = false;
- _pthread_internal_add(main_thread);
- __system_properties_init(); // Requires 'environ'.
- }
- int __system_properties_init()
- {
- return map_prop_area();
- }
- static int map_prop_area()
- {
- bool fromFile = true;
- int result = -1;
- int fd;
- int ret;
- fd = open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
- if (fd >= 0) {
- /* For old kernels that don't support O_CLOEXEC */
- ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
- if (ret < 0)
- goto cleanup;
- }
- if ((fd < 0) && (errno == ENOENT)) {
- /*
- * For backwards compatibility, if the file doesn't
- * exist, we use the environment to get the file descriptor.
- * For security reasons, we only use this backup if the kernel
- * returns ENOENT. We don't want to use the backup if the kernel
- * returns other errors such as ENOMEM or ENFILE, since it
- * might be possible for an external program to trigger this
- * condition.
- */
- fd = get_fd_from_env();
- fromFile = false;
- }
- if (fd < 0) {
- return -1;
- }
- struct stat fd_stat;
- if (fstat(fd, &fd_stat) < 0) {
- goto cleanup;
- }
- if ((fd_stat.st_uid != 0)
- || (fd_stat.st_gid != 0)
- || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0)
- || (fd_stat.st_size < sizeof(prop_area)) ) {
- goto cleanup;
- }
- pa_size = fd_stat.st_size;
- pa_data_size = pa_size - sizeof(prop_area);
- prop_area *pa = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0);
- if (pa == MAP_FAILED) {
- goto cleanup;
- }
- if((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION &&
- pa->version != PROP_AREA_VERSION_COMPAT)) {
- munmap(pa, pa_size);
- goto cleanup;
- }
- if (pa->version == PROP_AREA_VERSION_COMPAT) {
- compat_mode = true;
- }
- result = 0;
- __system_property_area__ = pa;
- cleanup:
- if (fromFile) {
- close(fd);
- }
- return result;
- }
- static int map_prop_area_rw()
- {
- ......
- fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC |
- O_EXCL, 0444);
- ......
- pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//映射属性文件到进程
- ......
- }
在map_prop_area()中:
- static int map_prop_area()
- {
- ......
- fd = open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
- ......
- prop_area *pa = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0);
- ......
- }
属性服务
启动属性服务
在init进程的main()中,我们可以看见如下语句:
@system/core/init/init.c
- queue_builtin_action(property_service_init_action, "property_service_init");
这句话的意思是启动action链表中的property服务:
- 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();
- return 0;
- }
@system/core/init/property_service.c
- void start_property_service(void)
- {
- int fd;
- load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
- load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
- load_override_properties();
- /* Read persistent properties after all default values have been loaded. */
- load_persistent_properties();
- 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;
- }
在Android中定义了5个存储属性的文件,它们分别是:
@/bionic/libc/includes/sys/_system_properties.h
- #define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
- #define PROP_PATH_SYSTEM_BUILD "/system/build.prop"
- #define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop"
- #define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"
- #define PROP_PATH_FACTORY "/factory/factory.prop"
在start_property_service()的最后创建了一个名为"property_service"的socket,启动监听,并将socket的句柄保存在property_set_fd中。
处理设置属性请求
接收属性设置请求的地方在init进程中:
- 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();
- else if (ufds[i].fd == get_keychord_fd())
- handle_keychord();
- else if (ufds[i].fd == get_signal_fd())
- handle_signal();
- }
- }
@system/core/init/property_service.c
- void handle_property_set_fd()
- {
- prop_msg msg;
- int s;
- int r;
- int res;
- struct ucred cr;
- struct sockaddr_un addr;
- socklen_t addr_size = sizeof(addr);
- socklen_t cr_size = sizeof(cr);
- char * source_ctx = NULL;
- if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
- return;
- }
- /* Check socket options here */
- if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
- close(s);
- ERROR("Unable to receive socket options\n");
- return;
- }
- r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0));
- if(r != sizeof(prop_msg)) {
- ERROR("sys_prop: mis-match msg size received: %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;
- if (!is_legal_property_name(msg.name, strlen(msg.name))) {
- ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
- close(s);
- return;
- }
- getpeercon(s, &source_ctx);
- if(memcmp(msg.name,"ctl.",4) == 0) {
- // Keep the old close-socket-early behavior 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 {
- if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) {
- 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 client code assumes that the
- // property server will not close the socket until *AFTER*
- // the property is written to memory.
- close(s);
- }
- freecon(source_ctx);
- break;
- default:
- close(s);
- break;
- }
- }
- int property_set(const char *name, const char *value)
- {
- prop_info *pi;
- int ret;
- size_t namelen = strlen(name);
- size_t valuelen = strlen(value);
- if (!is_legal_property_name(name, namelen)) return -1;
- if (valuelen >= PROP_VALUE_MAX) return -1;
- pi = (prop_info*) __system_property_find(name);
- if(pi != 0) {
- /* ro.* properties may NEVER be modified once set */
- if(!strncmp(name, "ro.", 3)) return -1;
- __system_property_update(pi, value, valuelen);
- } else {
- ret = __system_property_add(name, namelen, value, valuelen);
- if (ret < 0) {
- ERROR("Failed to set '%s'='%s'\n", name, value);
- return ret;
- }
- }
- /* If name starts with "net." treat as a DNS property. */
- if (strncmp("net.", name, strlen("net.")) == 0) {
- if (strcmp("net.change", name) == 0) {
- return 0;
- }
- /*
- * The 'net.change' property is a special property used track when any
- * 'net.*' property name is updated. It is _ONLY_ updated here. Its value
- * contains the last updated 'net.*' property.
- */
- property_set("net.change", name);
- } else if (persistent_properties_loaded &&
- strncmp("persist.", name, strlen("persist.")) == 0) {
- /*
- * Don't write properties to disk until after we have read all default properties
- * to prevent them from being overwritten by default values.
- */
- write_persistent_property(name, value);
- } else if (strcmp("selinux.reload_policy", name) == 0 &&
- strcmp("1", value) == 0) {
- selinux_reload_policy();
- }
- property_changed(name, value);
- return 0;
- }
从上面的代码可以看出:首先会判断所设置属性的属性名和属性值是否超过限制,这个限制定义如下:
@bionic/libc/includes/sys/system _properties.h
- #define PROP_NAME_MAX 32
- #define PROP_VALUE_MAX 92
- pa_size = PA_SIZE;//设置属性空间的大小为PA_SIZE(128 * 1024)
- pa_data_size = pa_size - sizeof(prop_area);//属性空间中可以用来保存属性的区域的大小,prop_area用来保存属性空间自身的一些信息
- compat_mode = false;
在判断文新添加属性的合法性以后接下来会判断该属性是否以存在,如果已存在则更新属性的值即可,如果不存在则增加新的属性。
然后还有剩下的一些其他判断,这里就不再赘述。到时最后一句property_changed()引起了我的注意:
- property_changed(name, value);
- on property:vold.decrypt=trigger_restart_min_framework
- class_start main
- on property:vold.decrypt=trigger_restart_framework
- class_start main
- class_start late_start
OK,到这里属性服务相关的东东基本介绍完了。接下来简单介绍一下如何设置系统属性。
设置系统属性
C代码中属性的设置是由libcutils库提供的,代码如下:@system/core/libutils/properties.c
- int property_set(const char *key, const char *value)
- {
- return __system_property_set(key, value);
- }
@/bionic/libc/bionic/system_property
- int __system_property_set(const char *key, const char *value)
- {
- int err;
- prop_msg msg;
- if(key == 0) return -1;
- if(value == 0) value = "";
- if(strlen(key) >= PROP_NAME_MAX) return -1;
- if(strlen(value) >= PROP_VALUE_MAX) return -1;
- memset(&msg, 0, sizeof msg);
- msg.cmd = PROP_MSG_SETPROP;
- strlcpy(msg.name, key, sizeof msg.name);
- strlcpy(msg.value, value, sizeof msg.value);
- err = send_prop_msg(&msg);
- if(err < 0) {
- return err;
- }
- return 0;
- }
- static int send_prop_msg(prop_msg *msg)
- {
- struct pollfd pollfds[1];
- struct sockaddr_un addr;
- socklen_t alen;
- size_t namelen;
- int s;
- int r;
- int result = -1;
- s = socket(AF_LOCAL, SOCK_STREAM, 0);
- if(s < 0) {
- return result;
- }
- memset(&addr, 0, sizeof(addr));
- namelen = strlen(property_service_socket);
- strlcpy(addr.sun_path, property_service_socket, sizeof addr.sun_path);
- addr.sun_family = AF_LOCAL;
- alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
- if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen)) < 0) {
- close(s);
- return result;
- }
- r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0));
- if(r == sizeof(prop_msg)) {
- // We successfully wrote to the property server but now we
- // wait for the property server to finish its work. It
- // acknowledges its completion by closing the socket so we
- // poll here (on nothing), waiting for the socket to close.
- // If you 'adb shell setprop foo bar' you'll see the POLLHUP
- // once the socket closes. Out of paranoia we cap our poll
- // at 250 ms.
- 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 {
- // Ignore the timeout and treat it like a success anyway.
- // The init process is single-threaded and its property
- // service is sometimes slow to respond (perhaps it's off
- // starting a child process or something) and thus this
- // times out and the caller thinks it failed, even though
- // it's still getting around to it. So we fake it here,
- // mostly for ctl.* properties, but we do try and wait 250
- // ms so callers who do read-after-write can reliably see
- // what they've written. Most of the time.
- // TODO: fix the system properties design.
- result = 0;
- }
- }
- close(s);
- return result;
- }
Java层设置属性的方法位于SystemProperties类中,在该类中实现了一系列set/get方法,Java代码中就是通过这些set/get方法设置和读取系统属性的。
@/framework/base/core/ java/android/os/SystemProperties.java
- /**
- * Set the value for the given key.
- * @throws IllegalArgumentException if the key exceeds 32 characters
- * @throws IllegalArgumentException if the value exceeds 92 characters
- */
- public static void set(String key, String val) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
- if (val != null && val.length() > PROP_VALUE_MAX) {
- throw new IllegalArgumentException("val.length > " +
- PROP_VALUE_MAX);
- }
- native_set(key, val);
- }
- private static native void native_set(String key, String def);
- init进程【3】——属性服务
- init进程【4】——属性服务
- Android init进程——属性服务
- init进程【4】——属性服务
- init进程【3】——Init 脚本语言
- init进程【3】——Init 脚本语言
- android init 进程分析 (4 属性服务)
- 《深入理解Android 卷1》读书笔记 (一)—— Android Init之属性服务 (property_service)
- init进程【1】——init启动过程
- init进程【1】——init启动过程
- init进程【1】——init启动过程
- init进程【1】——init启动过程
- init进程【2】——解析配置文件
- Android init进程——源码分析
- Android系统启动流程——init进程
- Android init进程——解析配置文件
- init进程【2】——解析配置文件
- init进程【2】——解析配置文件
- MySQL基本用法
- cocos植物大战僵尸(五)选择植物卡片:待选择植物卡片管理器类
- 连载《一个程序猿的生命周期》- 29、解决基层员工提出的难题,也是无奈之事
- 我的S5pv210裸机编程
- Redhat6.5下配置NTP时间服务器
- init进程【3】——属性服务
- BlueTooth: 浅析CC2540的OSAL原理
- STM32F10x.s文件
- [poj 1811]质数分解
- 第2周项目2-程序的多文件组织
- AJAX体验--Post请求
- linux下expdp和impdp命令
- 飞机大战
- 黑马程序员--学习笔记--多线程基础