Android10 Framework—Init进程-8.服务端属性文件创建和mmap映射

本章主要讲"属性文件创建和mmap映射",现给出完整数据流程图 上一章中讲解了上图左侧"属性安全上下文序列化",右侧部分就是"属性文件创建和mmap映射"做的工作,入口代码为__system_property_area_init

c 复制代码
void property_init() {
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
        LOG(FATAL) << "Failed to load serialized property info file";
    }
}

bionic/libc/include/sys/_system_properties.h
#define PROP_FILENAME "/dev/__properties__"

// bionic/libc/include/sys/_system_properties.h 
// bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_area_init() {
  bool fsetxattr_failed = false;
  return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
}

可以看到__system_property_area_init是调用到system_properties.AreaInit,system_properties实际是图中SystemProperties对象,它是一个大管家对象,它又如下作用:

  • 通过它里面的contexts_可以找到所有属性文件的映射地址,然后访问它们
  • system_property_api.cpp文件中所有API接口其实都是调用SystemProperties对象中相关接口 在源码中并没有看到system_properties实例创建的地方,通过其注释我们可以窥见端倪
c 复制代码
class SystemProperties {
 public:
  friend struct LocalPropertyTestState;
  friend class SystemPropertiesTest;
  // Note that system properties are initialized before libc calls static initializers, so
  // doing any initialization in this constructor is an error.  Even a Constructor that zero
  // initializes this class will clobber the previous property initialization.
  // We rely on the static SystemProperties in libc to be placed in .bss and zero initialized.
  SystemProperties() = default;
  // Special constructor for testing that also zero initializes the important members.
  explicit SystemProperties(bool initialized) : initialized_(initialized) {
  }
}

可见system_properties实例是由libc调用初始化的。

c 复制代码
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
  if (strlen(filename) >= PROP_FILENAME_MAX) {
    return false;
  }
  strcpy(property_filename_, filename);

  contexts_ = new (contexts_data_) ContextsSerialized();
  if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {
    return false;
  }
  initialized_ = true;
  return true;
}

在AreaInit方法中又调用了ContextsSerialized->Initialize

注意:第一个参数为writable为true表示可写的,这是一个比较重要的参数

c 复制代码
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
  filename_ = filename;
  if (!InitializeProperties()) {
    return false;
  }

  if (writable) {
    mkdir(filename_, S_IRWXU | S_IXGRP | S_IXOTH);
    bool open_failed = false;
    if (fsetxattr_failed) {
      *fsetxattr_failed = false;
    }

    for (size_t i = 0; i < num_context_nodes_; ++i) {
      if (!context_nodes_[i].Open(true, fsetxattr_failed)) {
        open_failed = true;
      }
    }
    if (open_failed || !MapSerialPropertyArea(true, fsetxattr_failed)) {
      FreeAndUnmap();
      return false;
    }
  } else {
    if (!MapSerialPropertyArea(false, nullptr)) {
      FreeAndUnmap();
      return false;
    }
  }

Initialize完成了橙色框中上面部分的工作:

  • 加载"/dev/properties/property_info"文件的内容(前一章属性安全上下文序列化的内容)
  • 创建ContextNode数组
  • 创建属性文件,并mmap映射属性文件,地址保存到ContextNode中

加载property_info

c 复制代码
bool ContextsSerialized::InitializeProperties() {
  //加载/dev/__properties__/property_info
  if (!property_info_area_file_.LoadDefaultPath()) {
    return false;
  }
  
  .....省略代码

  return true;
}

//system/core/property_service/libpropertyinfoparser/property_info_parser.cpp
bool PropertyInfoAreaFile::LoadDefaultPath() {
  return LoadPath("/dev/__properties__/property_info");
}

bool PropertyInfoAreaFile::LoadPath(const char* filename) {
  int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);

  auto mmap_size = fd_stat.st_size;

  void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
  if (map_result == MAP_FAILED) {
    close(fd);
    return false;
  }

  auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);
  if (property_info_area->minimum_supported_version() > 1 ||
      property_info_area->size() != mmap_size) {
    munmap(map_result, mmap_size);
    close(fd);
    return false;
  }

  close(fd);
  mmap_base_ = map_result;
  mmap_size_ = mmap_size;
  return true;
}
  • 使用mmap映射"/dev/properties/property_info"
  • 然后让PropertyInfoAreaFile->mmap_base_指向这块内存区域的起始地址
  • 然后让PropertyInfoAreaFile->mmap_size_指向这块内存区域的大小 这样后续就可以通过PropertyInfoAreaFile类操作里面的文件了。

创建ContextNode数组

c 复制代码
bool ContextsSerialized::InitializeProperties() {
  .....省略代码
  
  //创建ContextNode数组
  if (!InitializeContextNodes()) {
    FreeAndUnmap();
    return false;
  }

  return true;
}

bool ContextsSerialized::InitializeContextNodes() {
  auto num_context_nodes = property_info_area_file_->num_contexts();
  auto context_nodes_mmap_size = sizeof(ContextNode) * num_context_nodes;
  // We want to avoid malloc in system properties, so we take an anonymous map instead (b/31659220).
  void* const map_result = mmap(nullptr, context_nodes_mmap_size, PROT_READ | PROT_WRITE,
                                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (map_result == MAP_FAILED) {
    return false;
  }

  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_result, context_nodes_mmap_size,
        "System property context nodes");

  context_nodes_ = reinterpret_cast<ContextNode*>(map_result);
  num_context_nodes_ = num_context_nodes;
  context_nodes_mmap_size_ = context_nodes_mmap_size;

  for (size_t i = 0; i < num_context_nodes; ++i) {
    new (&context_nodes_[i]) ContextNode(property_info_area_file_->context(i), filename_);
  }

  return true;
}
  • 先从PropertyInfoAreaFile中读取映射数据结构中num_contexts数量,其实就是图中的TrieBuilder->contexts数组
  • 然后映射分配了num_context_nodes个ContextNode大小的空间
  • 让ContextsSerialized.context_nodes_指向映射空间的起始地址
  • 让ContextsSerialized.num_context_nodes_指向映射空间ContextNode的个数
  • 让ContextsSerialized.context_nodes_mmap_size_指向映射空间的大小
  • 最后初始化每个ContextNode的context(安全上下文)和filename,filename是在前面__system_property_area_init函数中调用时传入的"/dev/properties"

创建属性文件

c 复制代码
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
  .....省略代码

  if (writable) {
  .....省略代码
    for (size_t i = 0; i < num_context_nodes_; ++i) {
      if (!context_nodes_[i].Open(true, fsetxattr_failed)) {
        open_failed = true;
      }
    }
    
  } else {
    if (!MapSerialPropertyArea(false, nullptr)) {
      FreeAndUnmap();
      return false;
    }
  }
  return true;
}

循环遍历context_nodes_数组调用open方法创建属性文件

c 复制代码
//调用
context_nodes_[i].Open(true, fsetxattr_failed)

//实现
bool ContextNode::Open(bool access_rw, bool* fsetxattr_failed) {
  lock_.lock();
  if (pa_) {
    lock_.unlock();
    return true;
  }

  char filename[PROP_FILENAME_MAX];
  int len = async_safe_format_buffer(filename, sizeof(filename), "%s/%s", filename_, context_);
  if (len < 0 || len >= PROP_FILENAME_MAX) {
    lock_.unlock();
    return false;
  }

  if (access_rw) {
    pa_ = prop_area::map_prop_area_rw(filename, context_, fsetxattr_failed);
  } else {
    pa_ = prop_area::map_prop_area(filename);
  }
  lock_.unlock();
  return pa_;
}
  • 先构建属性文件的文件名格式为/dev/properties+context_(安全上下文名称)
  • access_rw为ture,表示以可读写的方式映射这个属性上下文(在属性服务框架中讲过init进程是唯一可以修改属性的进程,所以是可读写的方式映射)
c 复制代码
constexpr size_t PA_SIZE = 128 * 1024;

prop_area* prop_area::map_prop_area_rw(const char* filename, const char* context,
                                       bool* fsetxattr_failed) {
  /* dev is a tmpfs that we can use to carve a shared workspace
   * out of, so let's do that...
   */
  const int fd = open(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 nullptr;
  }

  if (context) {
    if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) {
      async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                            "fsetxattr failed to set context (%s) for \"%s\"", context, filename);
      /*
       * fsetxattr() will fail during system properties tests due to selinux policy.
       * We do not want to create a custom policy for the tester, so we will continue in
       * this function but set a flag that an error has occurred.
       * Init, which is the only daemon that should ever call this function will abort
       * when this error occurs.
       * Otherwise, the tester will ignore it and continue, albeit without any selinux
       * property separation.
       */
      if (fsetxattr_failed) {
        *fsetxattr_failed = true;
      }
    }
  }

  if (ftruncate(fd, PA_SIZE) < 0) {
    close(fd);
    return nullptr;
  }

  pa_size_ = PA_SIZE;
  pa_data_size_ = pa_size_ - sizeof(prop_area);

  void* const memory_area = mmap(nullptr, pa_size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if (memory_area == MAP_FAILED) {
    close(fd);
    return nullptr;
  }

  prop_area* pa = new (memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);

  close(fd);
  return pa;
}
  • 创建属性文件
  • fsetxattr为创建的属性文件设置安全上下文
  • 然后映射属性文件,大小为128k
  • prop_area->data指向文件映射的起始地址
  • 最后将ContextNode->pa_指向prop_area
rust 复制代码
prop_area->pa_size_:整个prop_area数据结构的大小(包含data[0])
prop_area->pa_data_size_:prop_area->data[0]可用最大空间

在/dev/__properties__下创建的属性文件如下

到此为止完整数据流程图中右侧的主要数据结构都构建完成,只剩下属性文件的数据了。

相关推荐
666xiaoniuzi1 小时前
深入理解 C 语言中的内存操作函数:memcpy、memmove、memset 和 memcmp
android·c语言·数据库
沐言人生6 小时前
Android10 Framework—Init进程-9.服务端属性值初始化
android·android studio·android jetpack
沐言人生6 小时前
Android10 Framework—Init进程-7.服务端属性安全上下文序列化
android·android studio·android jetpack
追光天使6 小时前
【Mac】和【安卓手机】 通过有线方式实现投屏
android·macos·智能手机·投屏·有线
小雨cc5566ru6 小时前
uniapp+Android智慧居家养老服务平台 0fjae微信小程序
android·微信小程序·uni-app
一切皆是定数7 小时前
Android车载——VehicleHal初始化(Android 11)
android·gitee
一切皆是定数7 小时前
Android车载——VehicleHal运行流程(Android 11)
android
problc7 小时前
Android 组件化利器:WMRouter 与 DRouter 的选择与实践
android·java
图王大胜8 小时前
Android SystemUI组件(11)SystemUIVisibility解读
android·framework·systemui·visibility