Android init启动理解

本节涉及源码有:

根目录为system:

Init.c (core\init)

Parser.c (core\init)

Builtins.c (core\init)

Keywords.h (core\init)

Init.rc (core\rootdir)

Property_service.c (core\init)

Properties.c (core\libcutils)

Libc_init_common.c (f:\db\src\bionic\libc\bionic)

Libc_init_dynamic.c (f:\db\src\bionic\libc\bionic)


概述:

init是一个进程,确切地说,它是linux系统中用户空间的第一个进程。由于Android是基于linux内核的,所以init也是Android系统中用户空间的第一个进程,它的进程号是1。它具有很重要的职责:

1.如何创建zygote。zygote是Java世界的开创者

2.init的属性服务是如何工作的。


init分析:

ini进程的入口函数是main,它的代码如下:









总结一下init的工作流程:

1.解析两个配置文件,我们将分析其中对init.rc文件的解析。

2.执行各个阶段的动作。early-init,init,early-boot,boot,创建zygote的工作就是在其中的某个阶段完成的。

3.调用property_init初始化属性相关的资源,并通过property_start_service启动属性服务

4.init进入一个无线循环,并且等待一些事情的发生。重点关注init如何处理来自socket和来自属性服务器的相关事情


一、解析配置文件:

init会解析两个配置文件,一个是系统配置文件init.rc,一个是硬件平台相关的配置,以HTC G7为例,配置文件为init.bravo.rc其中brava是硬件平台的名称。对这两个配置文件进行分析,调用的是同一个parse_config_file函数。下面就来看这个函数,在分析过程中以init.rc为主。

看下如何进行解析的:


读取完文件的内容后,将调用parse_config进行解析,这个函数的代码如下所示:



从整体来看parse_config首先会找到配置文件的一个section,然后针对不同的section使用不同的解析函数来解析。那么,什么是section呢?这和init.rc文件的组织结构有关。先不必急着去看init.rc,还是先到代码中去寻找答案。


1.关键字的定义:

keywords.h中定义了init中使用的关键字:




Keywords.h好像没有什么奇特之处,它不过是个简单的头文件,为什么说它的用法很有意思呢?下面看下代码中是如何使用它的:



终于领略到了keyword.h的神奇之粗,原来它干了两件事情:

1. 第一次包含keyword.h的时候,它声明了一些诸如do_class_start的函数,另外还定义了一个枚举,枚举值为K_class,K_mkdir等关键字。

2. 第二次包含keywords.h后,得到了一个keyword_info结构体数组,这个keyword_info结构体数组以前定义的枚举值为索引,存储对于的关键字信息,这些信息包括了关键字名称,处理函数,处理函数的参数个数,以及属性。


目前,关键字信息中最重要的就是symol和flags了,什么样的关键字被认为是section呢?

根据keywords.h的定义,当symobl为on或service的时候就表示section:

KEYWORD(on,          SECTION, 0, 0)
KEYWORD(service,     SECTION, 0, 0)


有了上面的知识,在来看配置文件init.rc的内容就比较容易了。

解析init.rc

截取内容如下:



总结:

从上面的init.rc的分析可知:

1.一个section的内容从这个表示section的关键字开始,到下一个标识的section的地方结束

2.init.rc中出现了名为early-init,boot和init的section,这里的boot和init就是前面介绍的4个动作执行阶段中的boot和init。也就是说,在boot阶段执行的动作都是由boot这个section定义的。


解析Services

以在zygote为例:



解析section的入口函数是parse_new_section,代码如下:



在解析service时,用到了parse_service和parse_line_service这两个函数,在分别介绍它们之前,先看init是如何组织Services的。


Service结构体:

init中使用了一个叫service的结构体来保存与service section相关的信息。不妨来看看这个结构体,代码如下所示:




现在已经了解的service结构体,相对来说还算是清晰易懂的。而zygote中的那三个onrestart该怎么表示呢?请看service结构体中使用的action结构体


了解上面的知识后,你是否能猜到parse_serviceparse_line_service的作用了呢?马上就来看它们。


可以看到parse_service函数只是搭建了一个service的架子。具体的内容尚需由后面的解析函数来填充。来看service的另外一个鸡西函数parse_line_service.


了解parse_line_service




可以看到parse_line_service是将配置文件的内容填充到service结构体中。

下面是zygote解析完成后的结果:


从上图中可知:

1. service_list链表将解析后的service全部连接到了一起,并且是一个双向链表,前向节点用prev表示,后项节点用next表示。

2. socketinfo也是一个双向链表,因为zygote只有一个socket,所以画了一个虚框作为链表的示范

3. onrestart通过commands指向一个commands链表,zygote有三个commands.


zygote这个service解析完了,现在就是“万事具备,只欠东风”了,接下来了解init是如何控制servies的?

1.启动zygote

在init.rc有这样一句话


class_start标识一个COMMAND,对应的处理函数为do_class_start,它位于boot section的范围内。为什么收它很重要呢?还记得init进程中的四个执行阶段吗?当init进程执行到下面几句话时,do_class_start就会被执行


下面看看do_class_start函数:


参数为default的话最终调用service_start_if_not_disabled

看看该函数的实现:



service_start的实现:



原来,zygote是通过fork和execv公共创建的!但是service结构中的那个onrestart好像没有派上用场,原因何在?


重启zygote

应该根据名字就可以猜到onrestart是在zygote重启时用的,下面先看 在zygote死后,它的父进程init会有什么动作:


signal_fd就是在init中通过socketpair创建的两个socket中的一个,既然会往这个signal_fd中发送数据,那么另外一个socket就一定能接收到,这样就会导致init从poll函数中返回,代码如下:





通过上面的代码可以知道onrestart的作用了,但是zygote本身又在哪里重启的呢?答案在下面的代码中:


这样,zygote又回来了!


属性服务

Key/Value键值对,一般而言,系统或某些应用程序会把自己的一些属性存储在注册表中,即使系统重启或应用程序重启,它还能够根据之前在注册表中设置的属性,进行相应的初始化工作。Android中的这种机制就称为属性服务。

可以使用:

adb shell 

getprop 查看当前系统有那些属性




属性服务是如何实现的?

在init.c中与属性服务有关的代码有:



1. 属性服务初始化:


在property_init函数中先调用init_property_area函数,创建一块用于存储属性的存储区域,然后加载default.prop文件中的内容。先看看init_property_area 是如何工作的,它的代码如下所示:



上面的内容比较简单,不过最后的赋值语句可是大有来头。__system_property_area__是bionic libc库中输出的一个变量,为什么这里要给他赋值?

原来,虽然属性区域是由init进程创建的,但Android系统希望其他进程也能读取这块内存里的东西。为了做到这一点,Android便做了以下的两项工作:

1. 把属性区域创建到共享内存上,共享内存是可以跨进程的。。这一点,已经在上面的代码中见到了,init_workspace函数内部将创建这个共享内存

2.如何让其他进程直到这个共享内存:Android利用了gcc的constructor属性,这个属性指明了一个__libc_prenit函数,当bionic libc库被加载时,将自动调用这个__libc_prenit,这个函数内部就将完成共享内存到本地进程的映射工作.


客户端进程获取存储空间


__libc_init_common函数为:




上面代码中有很多地方与共享内存有关。在以后的章节会对共享内存有关的问题进行介绍。

总之,通过这种方式,客户端进程就直接直接读取属性空间了,但是没有权限设置属性。客户端进程又是如何设置属性的呢


属性服务器的分析:

1. 启动属性服务器

init进程会启动一个属性服务器,而客户端只能通过与属性服务器交互来设置属性。先来看属性服务器的内容,它由start_property_service函数启动,代码如下所示:


属性服务创建了一个用来接收请求的socket,可这个请求在哪里被处理呢?事实上,在init中的for循环处已经进行相关处理了。


处理设置属性请求

接收请求的地方在init进程中,代码如下所示:


当属性服务器收到客户端请求时,init会调用handle_property_set_fd进行处理。这个函数的代码如下所示:




当客户端的权限满足要求时,init就调用property_set进行相关处理。这个函数比较简单,代码如下所示:



好,属性服务端的工作已经了解了,下面看客户端是如何设置属性的。


客户端发送请求

客户端通过property_set发送请求,property_set由libcutuls库提供,代码如下所示:



至此,属性服务器就介绍完了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值