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

在这里插入图片描述

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_builting_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,实现如下:

扫描二维码关注公众号,回复: 14976855 查看本文章
//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:=,则将其放在对应的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,而是直接从共享内存中读取

猜你喜欢

转载自blog.csdn.net/wzx311/article/details/129993926