• Android系统10 RK3399 init进程启动(三十八) property属性系统初始化代码分析


    配套系列教学视频链接:

          安卓系列教程之ROM系统开发-百问100ask

    说明

    系统:Android10.0

    设备: FireFly RK3399 (ROC-RK3399-PC-PLUS)

    前言

    除了了解属性的基本概念,还要知道如何利用属性开发和编程, 更要理解属性背后的涉及原理, 也就是源码也要去研究一下, 本章节重点介绍属性系统初始化。


    一, 属性系统完整的执行逻辑

    属性的初始化分为客户端和服务器端, 我们重点讲解服务端,也就是init进程的初始化整理过程,详细代码就不一一展开, 主要重点讲解主线代码, 先上框架图:

    1. 服务段(init进程)负责创建共享内存(即属性安全上下文文件),并以可读写的方式mmap映射全部共享内存, 而客户端如果要读属性, 就以只读方式mmap映射特定属性的共享内存。
    2. init进程会收集各个分区中的property_contexts文件,把文件里的内容写入/dev/__properties__/property_info, 并将文件内容进行TrieBuilerNode对象序列化,每个对象都有一个索引(从0开始), 每个对象包含了属性名和对应的安全上下文文件名(多个属性对应一个安全上下文文件)。
    3. 客户端和服务端(init进程)都会将/dev/__properties__/property_info作为一个桥梁信息文件,分别构建一个匿名共享内存, 构建一个ContextNode对象数组,ContextNode会记录共享内存的地址,并将property_info文件中的TrieBuilerNode对象和ContextNode对象做成一一对应的关系。
    4. 不管是客户端读取属性,还是服务器端修改属性, 最终都是通过属性名, 找到TrieBuilerNode和它的index索引, 根据索引就可以找到一一对应的ContextNode, 之后根据ContexNode找到共享内存的地址,并操作内存中的属性值。

    二, 服务端init 进程主要的初始化代码

    核心代码: 

    1. int SecondStageMain(int argc, char** argv){
    2. //创建/dev/__properties__/property_info,并对内容进行序列化
    3. // 构建和映射所有属性的共享内存
    4. property_init();
    5. // 读取特定设备树中的信息,并设置ro.boot开头的属性, RK3399上没有相关信息。
    6. process_kernel_dt();
    7. //将内核中cmdline中所有的有=号的参数,以及特殊的androidboot.xx=xxx的形式设置成相应的属性
    8. //如果是模拟器, bootargs中包含: androidboot.hardware=ranchu 和 init=/init
    9. // 就会设置ro.kernel.androidboot.hardware= ranchu ro.kernel.init=/init 和 ro.boot.hardware=ranchu,
    10. //如果是真机, 只针对androidboot.xx=xx的参数设置,
    11. //如androidboot.storagemedia=emmc, 就会有root.boot.storagemedia=emmc
    12. process_kernel_cmdline();
    13. // 将列表中特定属性的值设置到另外一个属性的值中去
    14. //如将ro.boot.serialno的值设置到ro.serialno中去
    15. //一般ro.boot开头的属性很多都是来自内核的cmdline
    16. export_kernel_boot_props();
    17. //加载各个分区中的属性文件,如prop.default, build.pro, default.prop,之前详细讲过属性文件
    18. property_load_boot_defaults(load_debug_prop);
    19. //创建本地套接字, 用于接收客户端的设置请求,并将套接字文件描述符加入到epoll监控机制
    20. //套接字有数据之后的处理函数是: handle_property_set_fd()
    21. StartPropertyService(&epoll);
    22. }

     其中property_init()的调用过程:

    property_init()

            |

          CreateSerializedPropertyInfo();// 1

            __system_property_area_init() // 2

                   |

               system_properties.AreaInit()

                      |

                      contexts_ = new (contexts_data_) ContextsSerialized()

                      contexts_->Initialize(true, property_filename_, fsetxattr_failed)

    注释1: init进程会收集各个分区中的property_contexts文件,把文件里的内容写入/dev/__properties__/property_info, 并将文件内容进行TrieBuilerNode对象序列化,每个对象都有一个索引(从0开始), 每个对象包含了属性名和对应的安全上下文文件名

    注释2: 最终调用ContextsSerialized::Initialize()

    ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed)函数的第一个参数非常重要,

    如果为true, 就会创建:

    1. 创建所有/dev/__properties__/property_info/u:object_r:*:s0属性安全上下文文件,
    2. mmap的内存时, 会时可读写属性。

    如果为false,则不会创建安全上下文文件,并且mmap时只读方式, 客户端就是走这个逻辑。

    三,  客户端初始化的主要代码

    客户端的程序一般都会加载libc, 而动态库加载的时候, 就会自动对属性进行初始化,初始化函数就是__system_properties_init(),具体调用的过程通过如下分析来说明: 

     int __system_properties_init(),通过搜索:

    __libc_init_common in libc_init_common.cpp (/bionic/libc/bionic) :   __system_properties_init();

    如下所示:

    1. void __libc_init_common() {
    2. //省略代码...
    3. __system_properties_init(); // Requires 'environ'.
    4. __libc_init_fdsan(); // Requires system properties (for debug.fdsan).
    5. }

    该函数会被如下函数调用: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.

    // We give this constructor priority 1 because we want libc's constructor

    // to run before any others (such as the jemalloc constructor), and lower

    // is better (http://b/68046352).

    __attribute__((constructor(1))) __libc_preinit()

                              |

                         __libc_preinit_impl();

                                     |

                                      __libc_init_common();

                                        |

                                       __system_properties_init();

                                                        |

                                                system_properties.Init(PROP_FILENAME)

    注意__attribute__((constructor))这个属性, 根据代码中注释, 加了该属性的函数会放在libc.so的.init_array段, 这个gcc的特有属性确保了加载libc.so动态库的时候, 该函数会尽快被动态linker调用, 即程序使用了libc, 就会尽早执行__libc_preinit()函数。这样一来,我们的函数放心调用property_get(),因为一开始属性就被初始化。

    bool SystemProperties::Init(const char* filename){

    contexts_ = new (contexts_data_) ContextsSerialized();

    contexts_->Initialize(false, property_filename_, nullptr)

    }

    有别于init启动过程中的初始化, 此处contexts_->Initialize(false, property_filename_, nullptr)的第一个参数为false,就决定了这个过程不会创建所有/dev/__properties__/property_info/u:object_r:*:s0属性安全上下文文件, 并且mmap的时候, 只是只读属性。

    四,总结

    详细代码就不具体贴出来了, 代码量非常多, 重点是把基本逻辑讲清楚, 细读代码,就交给大家去进行。同时强调的是, 属性代码使用其实很简单, 但是背后的框架逻辑代码其实还是蛮复杂的, 理解这些可以帮助大家形成一些设计思维。

  • 相关阅读:
    js深度学习(三)
    8个很棒的Vue开发技巧
    【快应用】如何实现快应用页面退出时弹框确认
    easyrecovery数据恢复软件免费版下载
    轻松搭建Linux的环境
    Java 提取HTML文件中的文本内容
    swin-transformer初步理解
    Spring Boot 整合 NoSQL(3)
    Spring MVC使用SessionLocaleResolver实现用户自定义切换语言实例
    JavaScript游戏恢复
  • 原文地址:https://blog.csdn.net/ldswfun/article/details/126905115