init.rc介绍

init.rc介绍

我先来做个名词解释,什么是init.rc,那就要从什么是init说起。init是由Android的Linux内核启动的第一个第一个进程,这个进程非常特殊,它的PID永远是1,并且这个进程是不会死亡的,如果它死亡,内核就会崩溃。init进程启动后会fork出很多及其重要的系统进程,比如我们做应用开发的时候都耳熟能详的zygote进程,我们所有的应用程序的进程都由zygote拉起。解释完了init进程,我们再说init.rc,init.rc是一个规定init进程行为和动作的配置文件。init进程可以做哪些事情,都由它规定。关于init.rc的详细介绍,大家可以参考这篇文章:

http://qiangbo.space/2017-01-28/AndroidAnatomy_Init/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io。

我们这里只对init.rc做一个简单的介绍,init.rc文件中只包含两种声明,on和service,我们可以把on称为行为,把service称为服务(这里的服务和应用开发中四大组件中的服务以及通过context.getSystemService()所得到的系统服务都不是一个东西,我一直不知道该怎么给它起名,姑且叫它init服务)。service声明了服务以及服务的各种行为。我们标题中说的开机启动进程就是这里的init服务。service只定义服务,但不能让服务做任何事情,如果你需要服务能够产生启动或者停止等相关动作,你就需要on,每个on下面的有各种命令,其中就包括很多对init服务的操作。这里要提到的是,我们要修改的init.rc文件在device/friendly-arm/nanopi3目录下,也就是厂商定制的版本,如果你使用的是别的开发板,可以去相应的目录找找。我们来看看init.rc中on和service两个典型的定义:

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000


    # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
    write /sys/fs/selinux/checkreqprot 0


    # Set the security context for the init process.
    # This should occur before anything else (e.g. ueventd) is started.
    setcon u:r:init:s0
    
    # Set the security context of /adb_keys if present.
    restorecon /adb_keys


    start ueventd
    # create mountpoints
    mkdir /mnt 0775 root system


    ......
    ......
    ......


service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

#后面的一行是注释,这个不用我多说。我们看到里面有很多东西感觉很晕,现在我们只重点关注几行,我们看到on下面有一行是“start ueventd”,而下面service的名字也是ueventd,这表明什么,我想大家都猜到了,那就是在early-init这个on启动了ueventd这个service。对,on就是这样启动sercice的,当然,on还有例如restart以及stop等等其它对service的操作,分别是让service停止并重新启动以及停止,不过on并不只是为service而生的,它还有许多其它的命令,在这里我就不详细介绍了,大家可以去网上搜索相关文章或者看这本书《构建嵌入式Android系统》。
我们看完了on再看看service,service我也只是简单介绍一下,service关键字声明了你要定义一个service,而ueventd就是这个service的名字,至于后面的目录则是这个service对应的可执行文件在系统中的位置。注意:这里是说在系统中的位置,也就是在开发板运行你的Android源码编译的系统后的目录,而不是源码的目录,至于Android源码的编译,等下再讲。接下来,我们可以看到service下面也有很多东西,这里我们不叫它们命令了,叫属性或者参数也许会比较好,其中比较重要的是class core,表示这个service属于core这个class,class我们不需要深入去管它,只要把它理解成一组service的集合就行,至于后面的属性,等下我们开始配置service的时候再说。

正式开始在init.rc中配置service

一上午已经过去了,不知不觉我已经写了这么多,但是重点才刚刚要开始。
我们先来理一理,我们现在通过上文的讲解已经得到了什么。我们已经编写了一个自己的C语言程序loop,并且把它放在了Android的源码中,Android的源码也编译好了,如何把编译好的Android系统刷到开发板上并启动我们也已经学会了。但是最重要的目的还没实现,那就是让loop这个可执行文件开机就可以被启动。而现在我们正要做的就是这件事。
首先打开Android源码目录,进入device/friendly-arm/nanopi3文件夹下,然后打开inti.rc文件,我们正式开始配置。
首先定义一个service,还记得service是怎么定义的吗,我这里定义的语句如下:service qya system/bin/loop。相信不难理解吧,我们定义了一个服务叫qya,它对应的程序是system/bin目录下的loop。这些上面都讲了。然后我在这个service下面增加几个配置属性:
service qya /system/bin/loop
    class main
    console
    oneshot

其中console表示服务需要并运行在控制台,oneshot表示服务只运行一次,在退出时将服务设置为禁用。你可以根据你的需要来增加这些属性,我写的两个属性并不一定都是必须的。《构建嵌入式Android系统》这本书的第六章对这些属性参数介绍的非常详细,如果网上找不到相应的文章可以拿这本书看看。
也许有人会发现,class main这是个什么东西,好像没讲,然而我并不是忘了,我在这里再详细讲讲。我之前讲过start命令是在on中启动serice,但是通览整个init.rc文件,我们会发现,直接使用start命令启动service的情况非常少,我之前也用这种方式试过几次,但都未能成功。所以我在仔细阅读和查询资料后发现,大多数inti进程fork出来的开机启动进程都是用另外一种方式来启动。我们来看看下面一个on行为的定义:
on property:vold.decrypt=trigger_restart_min_framework
    class_start main
我在这里再简单说一下on,on分为两种,第一种一共有7个,它们是一定会随inti进程的启动而执行的,比如我们上面介绍init.rc的时候展示的early-init正是这7个on中的第一个。而第二种on则是在满足某些特定条件时才会启动的,比如我们这里的这个on就是第二种。我们看到它下面有一条class_start main命令,而我们的service下面第一个属性正是class main。所以可以理解了,这个class_start main命令是启动main下面所有service。通览init.rc文件,我们会发现属于main的service非常多,所以我们的service也就搭一趟便车,挤上main的队伍。

什么是SELinux/SEAndroid?

看起来我们最后一步已经做了,把我们的loop程序成功添加到init.rc的文件中。你以为这样你就成功了吗?图样图森破!我们将会面临本文从开头到现在为止最大的挑战。我们的进程会被SEAndroid这个东西禁止掉,从而你无论怎么ps -Z你也不会看到它的存在。那么,SEAndroid到底是个什么东西?如果你想深入研究它,那可得好好花上一段时日了,我在这里给出两位大神的系列文章,专门分析什么是SEAndroid。这两位大神分别是罗升阳前辈和阿拉神农前辈,两人讲解SEAndroid的文章地址如下:
http://blog.csdn.net/luoshengyang/article/details/35392905
http://blog.csdn.net/innost/article/details/19299937/
其中,罗升阳老师的文章比较长,分析的比较理论,有助于你全面而细致的了解SEAndroid。而阿拉神农的文章则更加实用,让你快速能看懂这个东西,但是理论上并没有罗老师的深,大家可以各取所需。
我在这里越俎代庖的稍微讲一下什么是SEAndroid。老习惯,一句话:Android的系统安全机制。它来源于Linux系统中的SELinux。关于它们的历史我这里也都不讲了,总之SEAndroid是这样管理权限的:凡是任何想要运行的进程,想要做任何事情,都必须在安全策略配置文件中赋予权限,如果没有声明某个权限,那它就没有这个权限。要理解其实很容易,做应用开发的时候,我们常常需要在AndroidManifest.xml文件中赋予应用权限。比如,如果你的应用想要读写磁盘数据,那你就要写permission语句,赋予它读写磁盘的语句,如果你的应用想要访问网络,那你就需要写一条关于网络的permission语句,以准许它访问网络。SEAndroid也是类似的东西,你的进程想要干什么,你就得给它写一个.te文件,然后在文件中使用allow语句赋予它权限,不同的是SEAndroid的安全策略文件.te比AndroidManifest.xml可是难写多了。
我这里对编写.te文件和上面Android.mk一样,不做深入介绍。想详细了解如何编写.te文件,就去参考阿拉神农前辈的文章。我这里只做一个简单的介绍。
我们随便来找一条.te文件中的allow语句:allow netd proc:file write。这条语句是阿拉神农的文章中给出的示例,他在文章详细分析了这条语句每个词是干什么的,但我在这里也不多说,还是老惯例,一句话:允许netd进程对proc type的file进行写(write)操作。
我们这里把这条语句换一下:写成:allow a b:c d。那意思就是允许a进程,对c这个b类型的objec class进行d操作。是不是一下子就理解了?不过这里的类型(type)是SEAndroid中的概念,很难三言两语说清楚,所以请看上面的文章,而file也不是我们通常理解的文件,而是一种object class,表示一种可以被操作的对象,比如,除了file以外还有Dir,Socket等等,由系统规定,这里也不展开了。
到这里我再废话两句,SEAndroid把操作系统中的东西分为两种,能发起动作的进程,以及只能被进程操作的文件,而allow语句则就是规定允许谁对谁做什么的。其实SEAndroid的知识远不止这些,还有例如MLS分级系统等等,不过这里就不讨论了,再提示一下,想深入了解的,看上面两篇文章吧!
好的,现在我们要开始真正动手了。首先进入Android源码目录,然后进入:external/sepolicy。还是和上面一样,我也不知道怎么创建一个.te文件,我们直接随便找个.te文件,不过说是随便,我们不能真的随便,我们也要想一想,怎么做成功率高,记得我说过我们自定义的service是属于main这个class的吧,那我们就找一个同样在main这个class下面的其他服务的.te文件来修改,因为在同一个class中的进程,所需的权限应该是相近的。
好,我们找到一个,然后把它的名字改成loop.te。注意啊,我们的service的名字叫qya,但是我们要执行的程序叫loop,SEAndroid是赋予进程权限而不是赋予service权限,所以我们的.te名字叫loop.te而不是qya.te。.te文件中除了allow语句以外还有一些其它语句,比如type等等,它们是什么意思,大家就自己去查一查吧,
我这里给出大家一个投机取巧的办法吗,如果你不想现在了解SEAndroid,那我就教你一个办法,比如我的.te文件是复制uncrypt的,那进到这个文件里面以后,就把文件中所有写uncrypt的地方全部改成loop,一般来说就可以了。如果编译的时候不通过,你就仔细看错误提示,我当时不通过的原因是有两行allow语句和domain.te文件中的neverallow语句相冲突,看字面意思也能明白,neverallow的意思是从不允许,如果这里允许了一件事情,那里又不允许这件事情,势必发生冲突,那我们只需要把我们的.te文件中的相关allow语句注释掉或者删掉就行了。

 

你可能感兴趣的:(Android)