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

配套系列教学视频链接:

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

说明

系统:Android10.0

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

前言

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


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

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

Android系统10 RK3399 init进程启动(三十九) property属性系统初始化代码分析_第1张图片

  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 进程主要的初始化代码

核心代码: 

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

 其中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();

如下所示:

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

该函数会被如下函数调用: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的时候, 只是只读属性。

四,总结

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

你可能感兴趣的:(android,Android属性系统,安卓property,RK3399驱动,Android系统底层)