本文的大体流程还是按照书本上来,分三段。

(一)从Main开始到service start

(二)zygote restart

(三)属性服务 (property_service)

由于本文内容较长,重新组织了下文章结构,将原文一分为三。

(三)属性服务 (property_service)

老实说,觉得自己讲不好这部分,建议读者参考《深入理解Android 卷1》 或者网上

其他文章。此处,我只是略提一下。

看到这个property,让我想起了注册表,也想起来以前工作中保存用户设置数据的部分。

涉及到了NAND Flash,ubifs等。当然,此处我们讲的这个property就不提那么多了。

1. 数据在NAND Flash里面,以便下次开机后能得到之前保存的数据。而进程访问

这些数据之前,有做mmap的动作,将数据映射到内存。

2.设置property,有C/S架构组成。客户端的程序位于properties.c,服务端的程序位于

property_service.c。

(1)Init.c的main函数中,我们先看到:property_init();

void property_init(void)
{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;
}

其中,__system_property_area_init 的实现如下:

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

看到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);

我们知道文件property_filename (即 /dev/__properties__)被打开,并以读写,可共享的方式

映射到了内存。(其他全局变量,如pa_data_size和__system_property_area__ 暂放置一边,

也不细节的讲述get_fd_from_env() )

static int init_workspace(workspace *w, size_t size)
{void *data;int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW);if (fd < 0)return -1;w->size = size;w->fd = fd;return 0;
}

看到init_workspace以只读的方式打开 /dev/__properties__文件。

在main函数中接下来看到的是:property_load_boot_defaults

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

反正就是将文件/default.pro 里面的properties 加载到内存。

(2) 接下来是:queue_builtin_action(property_service_init_action, "property_service_init");

和 queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");

按照上面对service start的分析,我们知道property_service_init_action 和

queue_property_triggers_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();return 0;
}
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;
}
int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid)
{struct sockaddr_un addr;int fd, ret;char *secon;fd = socket(PF_UNIX, type, 0);if (fd < 0) {ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));return -1;}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;}secon = NULL;if (sehandle) {ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK);if (ret == 0)setfscreatecon(secon);}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(secon);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;
}

通过load properties这样的函数,将一些文件里面的property加载到内存,然后创建了一个socket,

其名为ANDROID_SOCKET_DIR"/PROP_SERVICE_NAME",也即 "/dev/socket/property_service“

然后调用listen监听这个socket,并将创建的socket描述符赋值给全局变量property_set_fd 。

至于queue_property_triggers_action, 请一层层看进去,会看到有函数处理init.rc里面

<propert_name>=<property_value> 这样的键值对 (字符串比较)。至于所谓的trigger,

其实跟上面对action的分析类似,在此就不赘述了。

(3)那么现在我们来看看我们调用property_set或者property_get的流程。

此处以property_set (platform/system/core/libcutils/properties.c)为例。

int property_set(const char *key, const char *value)
{return __system_property_set(key, value);
}
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;
}

我们看到,是要发送PROP_MSG_SETPROP这个消息的,现在得弄清给谁发。

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;
}
调用了connect连接到服务端的socket,通过send发送数据(消息)。property_service_socket

这个全局变量:"/dev/socket/" PROP_SERVICE_NAME; 即  "/dev/socket/"”property_service“,

当然其实就是"/dev/socket/property_service“。

然后调用了poll函数。此处调用poll的原因,我看了对应地方的代码注释,自己不甚了解,就

不忽悠了。咱们继续忽悠后面的。 调用了send后,会发生什么呢?

我们将回到init.c的main函数中的for循环中。里面的那个poll就是来处理这个的。

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

通过比较两个fd,发现将会执行handle_property_set_fd。

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

会调用property_service.c里面的property_set

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(namelen >= PROP_NAME_MAX) return -1;if(valuelen >= PROP_VALUE_MAX) return -1;if(namelen < 1) 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'", 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;
}
void property_changed(const char *name, const char *value)
{if (property_triggers_enabled)queue_property_triggers(name, value);
}

《深入理解Android 卷1》读书笔记 (一)—— Android Init之属性服务 (property_service)相关推荐

  1. 《Java核心技术 卷Ⅰ》读书笔记一

    Java核心技术·卷 I(原书第10版) 作者: [美] 凯.S.霍斯特曼(Cay S. Horstmann) 出版社: 机械工业出版社 原作名: Core Java Volume I - Funda ...

  2. 第一行代码Android第二章读书笔记

    第一行代码Android第二章读书笔记 Activity 1.1 手动创建活动 1.2 Toast和Menu/销毁活动 Intent 2.1 显示/隐式 2.2 传递/返回数据 活动的生命周期 3.1 ...

  3. Android 12 init(3) 属性服务

    文章托管在gitee上 Android Notes , 同步csdn 本文基于Android12 分析 在 init 的启动第二阶段,启动属性服务线程,提供相关属性服务,给其他进程提供设置属性的支持, ...

  4. Android智能指针——读书笔记

    目录结构 目录结构 参考资料 概述 背景知识 GC经典问题 轻量级指针 实现原理分析 构造函数 析构函数 应用实例分析 强指针和弱指针 强指针的实现原理分析 增加对象的弱引用计数 增加对象的强引用计数 ...

  5. 《深入理解Linux内核》 读书笔记

    深入理解Linux内核 读书笔记 一.概论 操作系统基本概念 多用户系统 允许多个用户登录系统,不同用户之间的有私有的空间 用户和组 每个用于属于一个组,组的权限和其他人的权限,和拥有者的权限不一样. ...

  6. Android进阶之光 读书笔记

    第一章, Android 5.6.7新特性 1.RecycleView的自定义分割线 public class DividerItemDecoration extends RecycleView.It ...

  7. Android群英传读书笔记——第十二章:Android 5.X新特性详解

    第十二章目录 12.1 Android5.X UI设计初步 12.1.1 材料的形态模拟 12.1.2 更加真实的动画 12.1.3 大色块的使用 12.2 Material Design主题 12. ...

  8. 【读书笔记《Android游戏编程之从零开始》】6.Android 游戏开发常用的系统控件(TabHost、ListView)...

    3.9 TabSpec与TabHost TabHost类官方文档地址:http://developer.android.com/reference/android/widget/TabHost.htm ...

  9. Android进阶之光读书笔记——第三章:View体系与自定义View

    第三章 View体系与自定义View 本章将介绍Android中十分重要的View,在多本书中View是必讲的一节,Android群英传就讲了不少的View的知识,那么在这里我们再去复习一遍吧 3.1 ...

最新文章

  1. 14. 函数返回值为引用?
  2. 转:谷歌离线地图基础
  3. asp.net core2.2 多用户验证和授权
  4. android里R.layout.的问题
  5. HTML5游戏开发系列教程5(译)
  6. Tensflow学习笔记(一)——TF生成并查看数据
  7. 创建图表_三种建立Excel图表的方法,谁用谁知道
  8. GPS经纬度转84坐标系
  9. MATLAB公式希腊字母表
  10. 微信推送封面尺寸_微信公众平台图片尺寸是多少?
  11. java pow_Java pow()方法
  12. Django新增数据
  13. java 将html转为word导出 (富文本内容导出word)
  14. 手机怎么用外嵌字幕_剪映教程大全:剪映加字幕、设置封面、变速等教程详解!...
  15. (zhuan)富文本 Attributes 下划线、删除线等
  16. Linux2--修改root密码,文件操作
  17. Error opening data file Tesseract-OCR\tessdata/eng.traineddata问题 解决
  18. android设备压差表怎么校准,MY-DJ101
  19. Linux的 常用命令
  20. 05- 线性回归算法 (LinearRegression) (算法)

热门文章

  1. 使用Gson解析json格式的字符串
  2. 有关看门狗(监控芯片)
  3. SpringBoot集成百度uid-generator唯一ID生成器
  4. 删除mysql数据库中表abc语句_MySQL数据库常用命令大全
  5. 毕业生自传(南柯一梦)
  6. Windows IIS服务教程
  7. Fortran二维数组赋值
  8. 数据结构-顺序表基本操作(C语言实现)
  9. vue--实现todo案例
  10. 1. 在顺序存储结构的职工工资表中,职工工资信息包括:职工号(no)、姓名(name )、职称(pro)、工资(sal)等四项信息,请编写一完整的程序。