• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > 深入剖析Android中init进程实现的C语言源码

深入剖析Android中init进程实现的C语言源码

作者:低调小一 字体:[增加 减小] 来源:互联网 时间:2017-05-28

低调小一 通过本文主要向大家介绍了android init进程,android中init,android init.rc,android init,android init代码分析等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

概述

init是一个进程,确切的说,它是Linux系统中用户空间的第一个进程。由于Android是基于Linux内核的,所以init也是Android系统中用户空间的第一个进程。init的进程号是1。作为天字第一号进程,init有很多重要的工作:

  •     init提供property service(属性服务)来管理Android系统的属性。
  •     init负责创建系统中的关键进程,包括zygote。

以往的文章一上来就介绍init的源码,但是我这里先从这两个主要工作开始。搞清楚这两个主要工作是如何实现的,我们再回头来看init的源码。

这篇文章主要是介绍init进程的属性服务。

    跟init属性服务相关的源码目录如下:

    system/core/init/
    bionic/libc/bionic/
    system/core/libcutils/

</div>

属性服务

在windows平台上有一个叫做注册表的东西,它可以存储一些类似key/value的键值对。一般而言,系统或者某些应用程序会把自己的一些属性存储在注册表中,即使系统重启或应用程序重启,它还能根据之前在注册表中设置的属性值,进行相应的初始化工作。

Android系统也提供了类似的机制,称之为属性服务(property service)。应用程序可以通过这个服务查询或者设置属性。我们可以通过如下命令,获取手机中属性键值对。

adb shell getprop

</div>

例如红米Note手机的属性值如下:

[ro.product.device]: [lcsh92_wet_jb9]
[ro.product.locale.language]: [zh]
[ro.product.locale.region]: [CN]
[ro.product.manufacturer]: [Xiaomi]

</div>


在system/core/init/init.c文件的main函数中,跟属性服务的相关代码如下:

property_init();
queue_builtin_action(property_service_init_action, "property_service_init");

</div>

接下来,我们分别看一下这两处代码的具体实现。
属性服务初始化
创建存储空间

首先,我们先来看一下property_init函数的源码(/system/core/init/property_service.c):

void property_init(void)
{
  init_property_area();
}

</div>


property_init函数中只是简单的调用了init_property_area方法,接下来我们看一下这个方法的具体实现:

static int property_area_inited = 0;
static workspace pa_workspace;
static int init_property_area(void)
{
  // 属性空间是否已经初始化
  if (property_area_inited)
    return -1;

  if (__system_property_area_init())
    return -1;

  if (init_workspace(&pa_workspace, 0))
    return -1;

  fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);

  property_area_inited = 1;
  return 0;
}

</div>

从init_property_area函数,我们可以看出,函数首先判断属性内存区域是否已经初始化过,如果已经初始化,则返回-1。如果没有初始化,我们接下来会发现有两个关键函数__system_property_area_init和init_workspace应该是跟内存区域初始化相关。那我们分别分析一下这两个函数具体实现。

__system_property_area_init

__system_property_area_init函数位于/bionic/libc/bionic/system_properties.c文件中,具体代码实现如下:

struct prop_area {
  unsigned bytes_used;
  unsigned volatile serial;
  unsigned magic;
  unsigned version;
  unsigned reserved[28];
  char data[0];
};
typedef struct prop_area prop_area;
prop_area *__system_property_area__ = NULL;

#define PROP_FILENAME "/dev/__properties__"
static char property_filename[PATH_MAX] = PROP_FILENAME; 

#define PA_SIZE (128 * 1024)


static int map_prop_area_rw()
{
  prop_area *pa;
  int fd;
  int ret;

  /**
   * O_RDWR ==> 读写
   * O_CREAT ==> 若不存在,则创建
   * O_NOFOLLOW ==> 如果filename是软链接,则打开失败
   * O_EXCL ==> 如果使用O_CREAT是文件存在,则可返回错误信息
   */
  fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
  if (fd < 0) {
    if (errno == EACCES) {
      abort();
    }
    return -1;
  }

  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);
  compat_mode = false;

  // mmap映射文件实现共享内存
  pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if (pa == MAP_FAILED)
    goto out;

  /*初始化内存地址中所有值为0*/
  memset(pa, 0, pa_size);
  pa->magic = PROP_AREA_MAGIC;
  pa->version = PROP_AREA_VERSION;
  pa->bytes_used = sizeof(prop_bt);

  __system_property_area__ = pa;

  close(fd);
  return 0;

out:
  close(fd);
  return -1;
}

int __system_property_area_init()
{
  return map_prop_area_rw();
}

</div>

 
代码比较好理解,主要内容是利用mmap映射property_filename创建了一个共享内存区域,并将共享内存的首地址赋值给全局变量__system_property_area__。

关于mmap映射文件实现共享内存IPC通信机制,可以参考这篇文章:mmap实现IPC通信机制
init_workspace

接下来,我们来看一下init_workspace函数的源码(/system/core/init/property_service.c):

typedef struct {
  void *data;
  size_t size;
  int fd;
}workspace;

static int init_workspace(workspace *w, size_t size)
{
  void *data;
  int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW);
  if (fd < 0)
    return -1;

  w->size = size;
  w->fd = fd;
  return 0;
}

</div>

客户端进程访问属性内存区域

虽然属性内存区域是init进程创建的,但是Android系统希望其他进程也能够读取这块内存区域里的内容。为了做到这一点,init进程在属性区域初始化过程中做了如下两项工作:

    把属性内存区域创建在共享内存上,而共享内存是可以跨进程的。这一点,在上述代码中是通过mmap映射/dev/__properties__文件实现的。pa_workspace变量中的fd成员也保存了映射文件的句柄。
    如何让其他进程知道这个共享内存句柄呢?Android先将文件映射句柄赋值给__system_property_area__变量,这个变量属于bionic_lic库中的输出的一个变量,然后利用了gcc的constructor属性,这个属性指明了一个__lib_prenit函数,当bionic_lic库被加载时,将自动调用__libc_prenit,这个函数内部完成共享内存到本地进程的映射工作。

只讲原理是不行的,我们直接来看一下__lib_prenit函数代码的相关实现:

void __attribute__((constructor)) __libc_prenit(void);
void __libc_prenit(void)
{
  // ...
  __libc_init_common(elfdata); // 调用这个函数
  // ...
}


__libc_init_common函数为:

void __libc_init_common(uintptr_t *elfdata)
{
  // ...
  __system_properties_init(); // 初始化客户端的属性存储区域
}

 
__system_properties_init函数有回到了我们熟悉的/bionic/libc/bionic/system_properties.c文件:

static int get_fd_from_env(void)
{
  char *env = getenv("ANDROID_PROPERTY_WORKSPACE");

  if (! env) {
    return -1;
  }

  return atoi(env);
}

static int map_prop_area()
{
  bool formFile = true;
  int result = -1;
  int fd;
  int ret;

  fd = open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
  if (fd >= 0) {
    /* For old kernels that don't support O_CLOEXEC */
    ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
    if (ret < 0)
      goto cleanup;
  }

  if ((fd < 0) && (error == ENOENT)) {
    fd = get_fd_from_env();
    fromFile = false;
  }

  if (fd < 0) {
    return -1;
  }

  struct stat fd_stat;
  if (fstat(fd, &fd_stat) < 0) {
    goto cleanup;
  }

  if ((fd_stat.st_uid != 0)
      || (fd_stat.st_gid != 0)
      || (fd_stat.st_mode & (S_IWGRP | S_IWOTH) != 0)
      || (fd_stat.st_size < sizeof(prop_area))) {
    goto cleanup;
  }

  pa_size = fd_stat.st_size;
  pa_data_size = pa_size - sizeof(prop_area);

  /* 
   * 映射init创建的属性内存到本地进



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 深入剖析Android中init进程实现的C语言源码

相关文章

  • 2017-05-28C++中四种对象生存期和作用域以及static的用法总结分析
  • 2017-05-28浅谈哈希表存储效率一般不超过50%的原因
  • 2017-05-28基于对话框程序中让对话框捕获WM_KEYDOWN消息的实现方法
  • 2017-05-28C++派生访问说明符小记(推荐)
  • 2017-05-28关于STL的erase()陷阱-迭代器失效问题的总结
  • 2017-05-28C++快速幂与大数取模算法示例
  • 2017-05-28C++入门之基础语法学习教程
  • 2017-05-28Linux下C语言实现C/S模式编程
  • 2017-05-28基于C++实现的各种内部排序算法汇总
  • 2017-05-28纯C语言:分治假币问题源码分享

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 深入分析C++中几个最不常用的关键字
    • 详解计数排序算法及C语言程序中的实现
    • Objective-C的内省(Introspection)用法小结
    • 用C语言判断字符是否为空白字符或特殊字符的方法
    • C语言中函数与指针的应用总结
    • new和malloc的区别深入解析
    • C语言数组指针(指向数组的指针)详解
    • C语言实现类似wget的进度条效果
    • 剖析C++的面向对象编程思想
    • json格式解析和libjson的用法介绍(关于cjson的使用方法)

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有