android 开机启动流程分析(04)init启动中关键服务-属性服务

    xiaoxiao2022-07-04  122

    该系列文章总纲链接:专题分纲目录 android 开机启动流程分析


    本章关键点总结 & 说明:

    说明:思维导图是基于之前文章不断迭代的,本章内容我们关注➕属性服务 部分即可

    关于属性服务说明: android的属性服务类似于windows的注册表服务,系统或者应用会把自己的一些属性储存在注册表中 即使系统重启也能够根据之前注册表中的属性进行相应初始化工作

    1 属性服务初始化{property_init()的分析} property_init的过程关注2件事:创建映射 & 然其他进程知道映射空间1.1 创建一块共享内存的内存映射空间{该空间可以通过mmap被其他进程访问},初始化完毕后再次打开

    //property_init() -just call->:init_property_area(),实现如下: static int property_area_inited = 0; static int init_property_area(void)//初始化属性存储区域 { if (property_area_inited)//通过全局变量控制,仅执行一次,之后执行无效 return -1; if(__system_property_area_init())//-just call->:map_prop_area_rw(),打开节点并初始化一块区域并关闭 return -1; if(init_workspace(&pa_workspace, 0))//重新打开设备节点"/dev/__properties__" return -1; fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);//在exec中使用该程序该文件描述符不可用,但在fork中可以 property_area_inited = 1;//表示已经初始化完毕 return 0; }

    其中,关键方法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... //这里打开的关键节点为"/dev/__properties__" const int fd = open(property_filename,O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); ... // 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);//在exec中使用该程序该文件描述符不可用,但在fork中可以 ... (ftruncate(fd, PA_SIZE)//ftruncate会将参数fd指定的文件大小改为参数length指定的大小。 pa_size = PA_SIZE; // PA_SIZE被定义为(128 * 1024) pa_data_size = pa_size - sizeof(prop_area);//这里的prop_area实际上就是数据头而已 compat_mode = false; void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//mmap ... prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION); __system_property_area__ = pa; //plug into the lib property services close(fd); return 0; }

    1.2 其他进程如何知道该共享内存

    android采用了gcc的constructor属性,该属性指明一个_libc_preinit函数,当bionic库被加载时将自动调用_libc_preinit,关键call过程如下:

    __libc_preinit -call-> __libc_init_common(*args)->__system_properties_init->map_prop_area:

    这里更关键分析map_prop_area,实现如下:

    static int map_prop_area() { int fd(open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)); if (fd >= 0) { /* For old kernels that don't support O_CLOEXEC */ const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC); ... } bool close_fd = true; if ((fd < 0) && (errno == ENOENT)) { fd = get_fd_from_env(); close_fd = false; } ... const int map_result = map_fd_ro(fd); if (close_fd) { close(fd); } return map_result; }

    get_fd_from_env的实现如下:

    static int get_fd_from_env(void) { char *env = getenv("ANDROID_PROPERTY_WORKSPACE");//一定有某个地方添加了该环境变量,这里取出 if (!env) { return -1; } return atoi(env); }

    map_fd_ro的实现如下:

    static int map_fd_ro(const int fd) { struct stat fd_stat; (fstat(fd, &fd_stat); ... pa_size = fd_stat.st_size; pa_data_size = pa_size - sizeof(prop_area); void* const map_result = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0); ... prop_area* pa = reinterpret_cast<prop_area*>(map_result); ... if (pa->version == PROP_AREA_VERSION_COMPAT) { compat_mode = true; } __system_property_area__ = pa; return 0;

    总结:该流程的目的是将init的初始化的那块property属性空间映射到本地进程,以便于使用

    2 属性服务默认项初始化{property_load_boot_defaults()} 属性的加载流程如下:

    #define PROP_PATH_RAMDISK_DEFAULT "/default.prop" //property_load_boot_defaults->load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);实现如下 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);->//这里最终会循环调用property_set(key, value);来设置各个属性 free(data); } }

    3 启动属性服务{queue_builtin_action(property_service_init_action, "property_service_init")} 这里着重分析property_service_init_action,实现如下:

    //property_service_init_action()-just call ->start_property_service();实现如下: //主要工作为,创建socket,但此时并未接收客户端 void start_property_service(void) { int fd; //creates a Unix domain socket in ANDROID_SOCKET_DIR* ("/dev/socket") as dictated in init.rc 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; }

    4 对action中on property处理{如果属性满足条件} {queue_builtin_action(queue_property_triggers_action, "queue_property_triggers")} 这里着重分析queue_property_triggers_action,实现如下:

    //queue_property_triggers_action->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>=<value> */ 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); } } } } } }

    此过程中,如果属性条件满足on property:<name>=<value>,则将其放在对应的action执行队列中

    5 load_all_props_action的执行{on late-init中通过trigger load_all_props_action来处理}

    #define PROP_PATH_SYSTEM_BUILD "/system/build.prop" #define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop" #define PROP_PATH_VENDOR_BUILD "/vendor/build.prop" #define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop" #define PROP_PATH_FACTORY "/factory/factory.prop" void load_all_props(void) { load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL); load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL); load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL); load_properties_from_file(PROP_PATH_FACTORY, "ro.*"); load_override_properties(); //{本地属性会覆盖之前的默认属性} /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); //读取"/data/property"下的persist属性并执行property_set }

    6 属性设置变更处理

    6.1 属性设置流程6.1.1 当系统上层{java层或者native层}执行property_set操作时,执行如下操作:

    //property_set-just call->__system_property_set int __system_property_set(const char *key, const char *value) { 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; //结构体msg初始化 prop_msg msg; memset(&msg, 0, sizeof msg); msg.cmd = PROP_MSG_SETPROP; strlcpy(msg.name, key, sizeof msg.name); strlcpy(msg.value, value, sizeof msg.value); //发送msg消息 const int err = send_prop_msg(&msg); if (err < 0) { return err; } return 0; }

    继续分析send_prop_msg,实现如下:

    static int send_prop_msg(const prop_msg *msg) { const int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);//创建socket并初始化 ... const size_t namelen = strlen(property_service_socket); ... (connect(fd, reinterpret_cast<sockaddr*>(&addr), alen)) < 0) //连接property service ... (send(fd, msg, sizeof(prop_msg), 0));//发送msg消息 ... if (num_bytes == sizeof(prop_msg)) {//写入成功后还要进行poll操作,保证服务端收到请求并正确处理 pollfd pollfds[1]; pollfds[0].fd = fd; pollfds[0].events = 0; //如果server端已经close fd,则客户端pollup挂起,这里会检测到 const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) { result = 0; } } close(fd); return result; }

    整个过程实际上就是向property service 服务发送消息并等待处理结束

    6.1.2 分析主要针对init中的这段代码{接收到属性变更后的处理流程}

    for(;;) { ... if (!property_set_fd_init && get_property_set_fd() > 0) {//只有第一次会进入,ufds初始化工作 ufds[fd_count].fd = get_property_set_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; property_set_fd_init = 1; } ... if (!action_queue_empty() || cur_action) timeout = 0; 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();//处理属性事件 ... } } }

    handle_property_set_fd的处理实现如下:

    void handle_property_set_fd() { ... ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size))//accept操作 (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) //检测socket options ufds[0].fd = s; ufds[0].events = POLLIN; ufds[0].revents = 0; nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms)); ... r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT)); ... 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) {//如果属性是以ctl.开头,启动后面的服务 // Keep the old close-socket-early behavior when handling ctl.* properties. close(s); if (check_control_mac_perms(msg.value, source_ctx)) {//SELinux校验相关 /**handle_control_message 分支(!strcmp(msg,"start"))->msg_start(arg);//service_find_by_name->service_start,启动服务 分支(!strcmp(msg,"stop")) ->msg_stop(arg);//service_find_by_name->service_stop,关闭服务 分支(!strcmp(msg,"restart"))->msg_restart(arg);//service_find_by_name->service_restart,重启服务 */ handle_control_message((char*) msg.name + 4, (char*) msg.value); } ... } else { if (check_perms(msg.name, source_ctx)) {//SELinux校验相关 property_set((char*) msg.name, (char*) msg.value);//设置属性 } ... close(s); } freecon(source_ctx); break; ... } }

    6.2 属性获取流程

    //int property_get-call->system_property_get(key, value) int __system_property_get(const char *name, char *value) { const prop_info *pi = __system_property_find(name); if (pi != 0) { return __system_property_read(pi, 0, value); } else { value[0] = 0; return 0; } }

    继续分析__system_property_find,实现如下:

    //__system_property_find-call-> __system_property_find_compat(name) __LIBC_HIDDEN__ const prop_info *__system_property_find_compat(const char *name) { prop_area_compat *pa = (prop_area_compat *)__system_property_area__;//共享内存头地址 unsigned count = pa->count; unsigned *toc = pa->toc; unsigned len = strlen(name); prop_info_compat *pi; if (len >= PROP_NAME_MAX) return 0; if (len < 1) return 0; while(count--) {//从共享内存中找到相应属性结构体 unsigned entry = *toc++; if(TOC_NAME_LEN(entry) != len) continue; pi = TOC_TO_INFO(pa, entry); if(memcmp(name, pi->name, len)) continue; return (const prop_info *)pi;//找到则直接返回 } return 0;//未找到返回0 }

    说明:获取属性的流程不再走socket,而是直接从共享内存中读取

    最新回复(0)