Android4.4属性系统-内存空间共享

简介: 笔记

一、Android4.4属性系统系列文章


Android4.4属性系统-初始化

Android4.4属性系统-系统服务

Android4.4属性系统-内存空间共享

Android4.4属性系统-属性获取

Android4.4属性系统-属性设置

Android4.4-属性的使用总结


二、写在前面-如何阅读本系列文章


本系列文章大部分是对源码的解析和注释,所以读起来枯燥无味,并且杂乱,这是阅读系统源码无法避免的,如果你有条件,可以点击下载Android4.4源码,阅读源码可以使用eclise,AndroidStudio,vim等。

文章的章节安卓是按照代码模块区分的,例如init进程代码,libcutils代码是按章节来区分,但不同模块的代码之间是有关联的,阅读时需要经常跳转,通过搜索功能进行页内搜索即可


三、内存空间共享


通过前文分析可知,Android 系统属性是通过在内核空间中开辟的一个共享内存来存储

属性信息的,那么在整个系统中,其他进程是如何读取这块内存并映射到当前进程空间中的

呢?


3.1 属性服务的启动和停止

system/core/init/init.c

//处理控制消息函数,参数<动作,服务名称>,属性服务名称是property_service
void handle_control_message(const char *msg, const char *arg)
{
    if (!strcmp(msg,"start")) {
        msg_start(arg);  //启动属性服务
    } else if (!strcmp(msg,"stop")) {
        msg_stop(arg);  //停止属性服务
    } else if (!strcmp(msg,"restart")) {
        msg_restart(arg);  //重启属性服务
    } else {
        ERROR("unknown control msg '%s'\n", msg);
    }
}
//启动指定的服务
static void msg_start(const char *name)
{   
    struct service *svc = NULL;
    char *tmp = NULL;
    char *args = NULL;
    //根据name查询系统中的服务
    if (!strchr(name, ':'))
        svc = service_find_by_name(name);
    else {
        tmp = strdup(name);
        if (tmp) { 
            args = strchr(tmp, ':');
            *args = '\0';
            args++;
            svc = service_find_by_name(tmp);
        }
    }
    if (svc) {
        service_start(svc, args);  //启动查找到的服务
    } else {
        ERROR("no such service '%s'\n", name);
    }
    if (tmp)
        free(tmp);
}
//启动服务
void service_start(struct service *svc, const char *dynamic_args)
{
    //省略部分代码
    ...
    pid = fork();  //fork 出子进程
    if (pid == 0) {  //判断是否是子进程,fork()返回0表示是子进程
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;
        umask(077);
        if(!strcmp(svc->name, "zygote")){
            BOOTINFO("service_start name is %s \n", svc->name);
        }
        if (properties_inited()) {  //properties_inited 函数判断属性区域是否已经初始化
            get_property_workspace(&fd, &sz);  //获取prop_area 起始地址以及大小
            sprintf(tmp, "%d,%d", dup(fd), sz);\
            //add_environment 将获取的属性区域信息添加到环境变量中
            //但是此处的环境变量实际上是定义在 init.c 中的长度为 32 的静态指针数组 static const char
*ENV[32]
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }
        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);
        setsockcreatecon(scon);
        //省略部分代码
        ....
         setpgid(0, getpid()); //设置pid,gid
        //省略部分代码
        ....
        // execve 执行相应的 service 进程,该函数最终会执行到
        //__libc_init_common 函数(源码位于bionic/libc/bionic/libc_init_common.cpp)
        execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
}
const char *ENV[32];
/* add_environment - add "key=value" to the current environment */
int add_environment(const char *key, const char *val)
{
    int n;
    for (n = 0; n < 31; n++) {
        if (!ENV[n]) {  //找到一个未填充的入口
            size_t len = strlen(key) + strlen(val) + 2; //计算key+value的总长度len
            char *entry = malloc(len);  //分配一块len大小的内存区域
            snprintf(entry, len, "%s=%s", key, val);
            ENV[n] = entry;  //将内存中分配用来存储环境变量值的起始地址复制给该未填充的入口
            return 0;
        }
    }
    return 1;
}

3.2 property_service.c

system/core/init/property_service.c

//判断属性区域是否已经初始化
int properties_inited(void)
{
    return property_area_inited;
}
//获取prop_area 起始地址以及大小
//pa_workspace 在 init_workspace 函数中初始化,fd 为/dev/__properties__的文件描述符,size
//表示 prop_area 内存区域的大小,该值固定为 PA_SIZE=128K,定义在_system_properties.h
void get_property_workspace(int *fd, int *sz)
{
    *fd = pa_workspace.fd;
    *sz = pa_workspace.size;
}

3.3 bionic/libc库

bionic/libc/bionic/libc_init_static.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,位于system_properties.c
  __system_properties_init(); // Requires 'environ'.
}

bionic/libc/bionic/system_properties.c

//属性系统初始化
int __system_properties_init()
{
    return map_prop_area();
}
//初始化可读写的共享内存区域
static int map_prop_area_rw()
{
    prop_area *pa; //prop_area结构体定义在该文件中
    int fd;
    int ret;
    /* dev is a tmpfs that we can use to carve a shared workspace
     * out of, so let's do that...
     */
    //dev是一个临时文件系统,我们可以用来定制共享工作区
    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;
    }
    //fcntl 根据文件描述词来操作文件特性,给 init_workspace 中创建的文件描述符设置文件描述符标记
    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);  //数据空间大小=总空间-prop_area结构体占用的空间
    compat_mode = false;
    /*映射为共享内存,mmap将一个文件或者其它对象映射进内存
      *这里是关键的部分,关于mmap可以参考(https://blog.csdn.net/yangle4695/article/details/52139585)
    */
    /*参数<映射的内存起始地址为NULL=0,映射大小,可读|可写,
      *MAP_SHARED :对映射区域的写入数据会复制回文件内, 而且允许其他映射该文件的进程共享
      * fd文件描述符, offset:表示被映射对象(即文件)从那里开始对映,通常都是用0>
      * 返回被映射区的指针pa
      */
    pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(pa == MAP_FAILED)
        goto out;
    memset(pa, 0, pa_size);  //初始化映射内存区域为 0
    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 */
    //将 pa 赋值给__system_property_area__
    __system_property_area__ = pa;
    close(fd);
    return 0;
out:
    close(fd);
    return -1;
}
//查询prop
const prop_info *__system_property_find(const char *name)
{
    if (__predict_false(compat_mode)) {
        return __system_property_find_compat(name);
    }
    return find_property(root_node(), name, strlen(name), NULL, 0, false);
}
//前面介绍过了,属性的存储结构为特里结构+二叉树结构,所以查找属性其实就是
//特里结构+二叉树结构的遍历 
static const prop_info *find_property(prop_bt *trie, const char *name,
        uint8_t namelen, const char *value, uint8_t valuelen,
        bool alloc_if_needed)
{
    const char *remaining_name = name;
    while (true) {
        char *sep = strchr(remaining_name, '.');
        bool want_subtree = (sep != NULL);
        uint8_t substr_size;
        prop_bt *root;
        if (want_subtree) {
            substr_size = sep - remaining_name;
        } else {
            substr_size = strlen(remaining_name);
        }
        if (!substr_size)
            return NULL;
        if (trie->children) {
            root = to_prop_obj(trie->children);
        } else if (alloc_if_needed) {
            root = new_prop_bt(remaining_name, substr_size, &trie->children);
        } else {                                                                               
            root = NULL;
        }
        if (!root)
            return NULL;
        trie = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
        if (!trie)
            return NULL;
        if (!want_subtree)
            break;
        remaining_name = sep + 1;
    }
    if (trie->prop) {
        return to_prop_obj(trie->prop);
    } else if (alloc_if_needed) {
        return new_prop_info(name, namelen, value, valuelen, &trie->prop);
    } else {
        return NULL;
    }
}


四、总结


以上分析了 Android 属性系统区域内存的共享,主要是在 init 进程中将 prop_area 信息存储到环境变量中,在进程中获取该环境变量并解析,调用 mmap 进行内存空间映射,由于此内存空间是只读的,所以所有对属性系统区域的更改最终需要通过 init 进程来完成。由于每个进程都会执行这个函数,system_property_area是一个 static 变量,所以每个进程都有一个对系统属性的引用,可以通过这个静态变量来访问系统属性区域内存信息。


五、参考


Android属性系统

Linux 内存映射函数 mmap

trie-特里结构

Linux 内存映射函数 mmap()函数详解


目录
相关文章
|
1月前
|
人工智能 搜索推荐 物联网
Android系统版本演进与未来展望####
本文深入探讨了Android操作系统从诞生至今的发展历程,详细阐述了其关键版本迭代带来的创新特性、用户体验提升及对全球移动生态系统的影响。通过对Android历史版本的回顾与分析,本文旨在揭示其成功背后的驱动力,并展望未来Android可能的发展趋势与面临的挑战,为读者呈现一个既全面又具深度的技术视角。 ####
|
1月前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
18天前
|
机器学习/深度学习 人工智能 缓存
【AI系统】推理内存布局
本文介绍了CPU和GPU的基础内存知识,NCHWX内存排布格式,以及MNN推理引擎如何通过数据内存重新排布进行内核优化,特别是针对WinoGrad卷积计算的优化方法,通过NC4HW4数据格式重排,有效利用了SIMD指令集特性,减少了cache miss,提高了计算效率。
35 3
|
21天前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
22天前
|
存储 安全 Android开发
探索Android系统的最新安全特性
在数字时代,智能手机已成为我们生活中不可或缺的一部分。随着技术的不断进步,手机操作系统的安全性也越来越受到重视。本文将深入探讨Android系统最新的安全特性,包括其设计理念、实施方式以及对用户的影响。通过分析这些安全措施如何保护用户免受恶意软件和网络攻击的威胁,我们希望为读者提供对Android安全性的全面了解。
|
22天前
|
机器学习/深度学习 人工智能 算法
【AI系统】内存分配算法
本文探讨了AI编译器前端优化中的内存分配问题,涵盖模型与硬件内存的发展、内存划分及其优化算法。文章首先分析了神经网络模型对NPU内存需求的增长趋势,随后详细介绍了静态与动态内存的概念及其实现方式,最后重点讨论了几种节省内存的算法,如空间换内存、计算换内存、模型压缩和内存复用等,旨在提高内存使用效率,减少碎片化,提升模型训练和推理的性能。
42 1
|
1月前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
68 16
|
28天前
|
安全 Android开发 iOS开发
深入探讨Android与iOS系统的差异及未来发展趋势
本文旨在深入分析Android和iOS两大移动操作系统的核心技术差异、用户体验以及各自的市场表现,进一步探讨它们在未来技术革新中可能的发展方向。通过对比两者的开放性、安全性、生态系统等方面,本文揭示了两大系统在移动设备市场中的竞争态势和潜在变革。
|
Java 调度 Android开发
android体系课-系统启动流程-之zygote进程启动过程源码分析
笔者刚开始学习Android的时候也和大部分同学一样,只会使用一些应用层面的知识,对于一些比较常见的开源框架如<mark>RxJava</mark>,<mark>OkHttp</mark>,<mark>Retrofit</mark>,以及后来谷歌推出的<mark>协程</mark>等,都只在使用层面,对于他们<mark>内部原理</mark>,基本没有去了解觉得够用就可以了,又比如Activity,Service等四大组件的使用原理,系统开机过程,Launcher启动过程等知之甚少,知其然而不知其所以然,结果就是出现某些问题,不知道从哪里找原因,只能依赖万能的百度,但是百度看多了,你会发现自己