精华内容
下载资源
问答
  • 主要介绍了Android init.rc文件简单介绍的相关资料,需要的朋友可以参考下
  • 主要介绍了Android init.rc文件详解及简单实例的相关资料,需要的朋友可以参考下
  • Android Init Language是android系统很重要的组成 文件来自于\system\core\rootdir 里面有init.rc,init.usb.rc,init.zygote64.rc,init-debug.rc,ueventd.rc
  • init.rc切换usb模式用脚本替换, 避免特殊需求对代码修改风险. 附件是展讯7731平台切换到的mtp模式的实现,pid,vid等可以改. 格式为: adb shell ./mtp.sh & #也可以把第一行屏蔽,然后执行./mtp.sh
  • Android——init.rc脚本

    2019-03-24 02:19:28
    NULL 博文链接:https://folksy.iteye.com/blog/1093567
  • PRODUCT_COPY_FILES += \ device/hisilicon/bigfish/etc/init.rc:root/init.rc \ 改成如下: PRODUCT_COPY_FILES += \ device/kedacom/${TARGET_PRODUCT}/etc/init.rc:root/init.rc \ 以上就是如何关闭启动服务中的...

    因为某些特殊的逻辑,希望启动的时候不去打开开机动画等服务。

    之前也提到过开机动画的替换,只需要在"/system/media"目录下去更改bootanimation.zip就好了。

    其中一些更细致的内容,比如desc.txt等配置,其他博客里会提到。

    安卓制作开机动画

     

    本次主要研究的如何完全关闭开机动画、开机铃声等。

     

    开机动画的逻辑之前也研究过。

    在系统启动时,会自动执行Ramdisk中的rc脚本,其中里面有一个文件/root/init.rc,它就是主系统服务javascript:;我们打开这个脚本,会发现里面有许多service开头的句子,这就是系统服务。它的作用就是在开机Boot启动时,启动system/bin下的二进制文件和sh脚本,以开启需要的系统服务。执行脚本后会按脚本顺序启动系统服务

    系统服务启动后,一般先会启动媒体服务,紧接着启动开机关机动画和铃声服务,因为开关机动画和铃声是依赖媒体服务的,如果增加过删除此处的代码,就可以达到开启或者关闭动画和铃声的目的。

    这时可能又会有朋友问了,关闭开关机动画和铃声不是进入system/media直接删除掉文件不就行了吗?不需要这么麻烦吧。
    那这里又牵涉到一个地方,如果直接删除掉动画和铃声,系统在启动服务时,如果在system/media下搜索不到动画和铃声文件,它就会加载默认的开关机动画,默认的开关机动画是一个ANDROID,位于system/framework/framework-res.apk,解开框架文件,在assets资源文件夹下,有一个images文件下,有两张png图片,就是安卓默认的开关机动画。所以直接删除动画和铃声文件是无法彻底关闭动画和铃声的。我们只能通过修改系统服务来彻底开启和关闭。

     

    在/root/init.rc中,下面这段脚本就是对应的启动开机动画的服务,只要将这段脚本删除掉就能实现关闭开机动画

    service bootanim /system/bin/bootanimation          
        class core                                                         
        user root         
        group graphics audio
        disabled                                           
        oneshot 

    Android 根目录下可以看到 init.rc ,但是却无力修改,改了也白瞎,重启就恢复了,ramdisk....

    init.rc 是 rootfs 中的文件,Android 将 rootfs(initramfs) link 到 linux kernel image 中,生成正常启动的boot.img, recovery 模式的 recovery.img.
    Android 每次 reboot 的时候 bootloader 都要将 boot.img 加载到ram 中,其中一部分是真正的 linux kernel image (zImage), 另外一部分就是 initramfs (包含 init, init.rc, etc).
    之后,Linux kernel 初始化的时候会把 initramfs 以 rootfs 类型 mount 到 / .
    这样,你用 adb shell 或 串口 修改 / 上的init.rc, 因为下次重启会重新加载,你的修改自然就没了.

    在这边,生成的boot.img是整合到kernel.img中的。
     

     

     

    PS:

    在这边又有个小插曲,因为安卓的这个init.rc文件在一个公有的目录下,修改这个文件可能会导致所有项目中的init.rc遭到修改。

    因此需要改动device.mk文件,将以下的这段代码替换成指定的目录下的init.rc文件。

    PRODUCT_COPY_FILES += \
        device/hisilicon/bigfish/etc/init.rc:root/init.rc \

    改成如下:

     PRODUCT_COPY_FILES += \
       device/kedacom/${TARGET_PRODUCT}/etc/init.rc:root/init.rc \

     

    以上就是如何关闭启动服务中的bootanimation这个服务,

    其他的服务开启关闭也可以参照这块。

     

    展开全文
  • init.rc 复习看这个之前,先看看大神总结的文章这篇文章总结的非常到位,但是因为代码不是最新的Android版本,对我们最新的Android版本不适用。http://gityuan....

    init.rc 复习

    看这个之前,先看看大神总结的文章

    这篇文章总结的非常到位,但是因为代码不是最新的Android版本,对我们最新的Android版本不适用。

    http://gityuan.com/2016/02/05/android-init/
    

    #init rc文件拷贝

    拷贝其实也就是把文件放到机器的某个位置,我们也可以使用这个命令来完成我们需要的功能。

    PRODUCT_COPY_FILES += $(LOCAL_PATH)/factory_init.project.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw/factory_init.project.rc
    PRODUCT_COPY_FILES += $(LOCAL_PATH)/init.project.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw/init.project.rc
    PRODUCT_COPY_FILES += $(LOCAL_PATH)/meta_init.project.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw/meta_init.project.rc
    PRODUCT_COPY_FILES += $(LOCAL_PATH)/init.recovery.mt8167.rc:root/init.recovery.mt8167.rc
    

    #触发 trigger 

    system/core/rootdir/init.rc

    如果我们想触发某种 trigger,就可以这样去做,Android也是通过这样的机制来决定各个阶段做不同的事情的。

    # Mount filesystems and start core system services.
    on late-init
     /*触发更早的文件系统*/
        trigger early-fs
    
        # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter
        # '--early' can be specified to skip entries with 'latemount'.
        # /system and /vendor must be mounted by the end of the fs stage,
        # while /data is optional.
        /*vendor 需要在fs 挂载之后才能挂载*/
        trigger fs
        trigger post-fs
    
        # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
        # to only mount entries with 'latemount'. This is needed if '--early' is
        # specified in the previous mount_all command on the fs stage.
        # With /system mounted and properties form /system + /factory available,
        # some services can be started.
        trigger late-fs
    
        # Now we can mount /data. File encryption requires keymaster to decrypt
        # /data, which in turn can only be loaded when system properties are present.
        trigger post-fs-data
    
        # Now we can start zygote for devices with file based encryption
        trigger zygote-start
    
        # Load persist properties and override properties (if enabled) from /data.
        trigger load_persist_props_action
    
        # Remove a file to wake up anything waiting for firmware.
        trigger firmware_mounts_complete
    
        trigger early-boot
        trigger boot
    

    #Android 服务启动顺序

    on early-init
    on init
    on late-init
        trigger post-fs      
        trigger load_system_props_action
        trigger post-fs-data  
        trigger load_persist_props_action
        trigger firmware_mounts_complete
        trigger boot   
    
    on post-fs      //挂载文件系统
        start logd
        mount rootfs rootfs / ro remount
        mount rootfs rootfs / shared rec
        mount none /mnt/runtime/default /storage slave bind rec
        ...
    
    on post-fs-data  //挂载data
        start logd
        start vold   //启动vold
        
    //android 9.0 中变成了 在main.c代码中去触发 
        ATRACE_BEGIN("VoldNativeService::start");
        if (android::vold::VoldNativeService::start() != android::OK) {
            LOG(ERROR) << "Unable to start VoldNativeService";
            exit(1);
        }
        ATRACE_END();
    
    on boot      //启动核心服务
        ...
        class_start core //启动core class
    

    #解析init.*.rc文件的代码位置

    system/core/init/
      - init.cpp
    /*可以从system/core里面去看代码,找到你想要的东西*/
    

    #查看服务的状态

    getprop | grep init.svc
    /*服务指的是用service指定的服务*//*比如*/
    # adbd is controlled via property triggers in init.<platform>.usb.rc
    service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
        class core
        socket adbd stream 660 system system
        disabled
        seclabel u:r:adbd:s0

    在机器中执行结果如下

    Android 9.0:/ # getprop |grep init.svc
    [init.svc.XoSetCap]: [stopped]
    [init.svc.adbd]: [running]
    [init.svc.aee-reinit]: [stopped]
    [init.svc.aee_aed]: [running]
    [init.svc.aee_aedv]: [running]
    [init.svc.atcid-daemon-u]: [stopped]
    [init.svc.audio-daemon]: [stopped]
    [init.svc.audioserver]: [running]
    [init.svc.batterywarning]: [running]
    [init.svc.bluetooth-1-0]: [running]
    [init.svc.bluetoothd]: [stopped]
    [init.svc.bootanim]: [stopped]
    [init.svc.bootlogoupdater]: [stopped]
    [init.svc.bugreport]: [stopped]
    [init.svc.camerahalserver]: [running]
    [init.svc.cameraserver]: [running]
    [init.svc.clear-bcb]: [stopped]
    [init.svc.console]: [running]
    [init.svc.drm]: [running]
    [init.svc.dumpstate]: [stopped]
    [init.svc.dumpstatez]: [stopped]
    [init.svc.flash_recovery]: [stopped]
    [init.svc.fuse_usbotg]: [stopped]
    [init.svc.gatekeeperd]: [running]
    [init.svc.ged_srv]: [running]
    [init.svc.hdmi]: [stopped]
    

    查看adbd 的服务状态

    Android 9.0:/ # getprop |grep adb
    [init.svc.adbd]: [running]
    [persist.sys.usb.config]: [adb]
    [ro.adb.secure]: [1]
    [ro.boottime.adbd]: [6366465736]
    [service.adb.root]: [1]
    [sys.usb.config]: [adb]
    [sys.usb.state]: [adb]
    Knowin inSight10:/ #
    

    adbd 服务定义在 init.usb.rc里面

    adbd的服务启动和usb强相关,服务的启动和停止总是和usb关连在一起。

    # Copyright (C) 2012 The Android Open Source Project
    #
    # USB configuration common for all android devices
    #
    
    on post-fs-data
        chown system system /sys/class/android_usb/android0/f_mass_storage/lun/file
        chmod 0660 /sys/class/android_usb/android0/f_mass_storage/lun/file
        chown system system /sys/class/android_usb/android0/f_rndis/ethaddr
        chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr
        mkdir /data/misc/adb 02750 system shell
        mkdir /data/adb 0700 root root
    
    # adbd is controlled via property triggers in init.<platform>.usb.rc
    service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
        class core
        socket adbd stream 660 system system
        disabled
        seclabel u:r:adbd:s0
    
    on boot
        setprop sys.usb.configfs 0
    
    # Used to disable USB when switching states
    on property:sys.usb.config=none && property:sys.usb.configfs=0
        stop adbd
        write /sys/class/android_usb/android0/enable 0
        write /sys/class/android_usb/android0/bDeviceClass 0
        setprop sys.usb.state ${sys.usb.config}
    
    # adb only USB configuration
    # This is the fallback configuration if the
    # USB manager fails to set a standard configuration
    on property:sys.usb.config=adb && property:sys.usb.configfs=0
        write /sys/class/android_usb/android0/enable 0
        write /sys/class/android_usb/android0/idVendor 18d1
        write /sys/class/android_usb/android0/idProduct 4EE7
        write /sys/class/android_usb/android0/functions ${sys.usb.config}
        write /sys/class/android_usb/android0/enable 1
        start adbd
        setprop sys.usb.state ${sys.usb.config}
    

    #vendor 和 system/vendor Link在一起

    vendor 英文解释是供应商的意思,对我们开发而言,也就是除开google的那一套之外,都可以归类到vendor下面去,比如我们自己客制化的hal,也可以放到vendor下面去。

        # Link /vendor to /system/vendor for devices without a vendor partition.
        symlink /system/vendor /vendor
    

    在设备上查看

    Closed connection on port 1.
    android 9.0:/ # ls /system/vendor/
    app bin build.prop cache data default.prop etc firmware lib lost+found nvdata odm overlay persist protect_f protect_s res ueventd.rc va
    android 9.0:/ # ls /vendor/
    app bin build.prop cache data default.prop etc firmware lib lost+found nvdata odm overlay persist protect_f protect_s res ueventd.rc va
    

    # class 对应的属性 core 、main、late_start

    class 是类别的意思,一共有三种类别。

    • core

    ueventd、logd、healthd、adbd(disabled)、lmkd(LowMemoryKiller)、servicemanager、vold、debuggerd、surfaceflinger、bootanim(disabled)等

    core分类中的服务都是一些核心服务,它们不启动,后续的服务或系统启动就可能出问题。比如servicemanager,binder通信大管家,它的启动很靠前,因为,其他服务启动时候需要向servicemanager注册binder服务。vold,负责存储类设备的挂载;还有surfaceflinger,图形显示的核心服务等。

    • main

    debuggerd64、drm、media、ril-daemon、installd、flash_recovery、racoon(disabled)、mtpd(disabled)、keystore、dumpstate(disabled)、mdnsd(disabled)、pre-recovery(disabled)、cmd_services(disabled)、phasecheckserver、zygote等。

    main分类中的服务是一些系统的基本服务,有了这些服务android系统、Java世界才能正常运行。

    • late_start

      字面意思是晚些启动。/device/中一些硬件厂商的.rc文件中会将一些服务设置为该类。

    #Android 增加一个property

    我们有时候需要增加一个property 在init.rc 里面做判断,用来判断某个服务是否已经启动。

    这个property 也会在设备启动后的build.prop中显示出来。

    weiqifa@bsp-ubuntu1804:~/mt8167s-9.0-sdk$ git diff build/make/tools/buildinfo.sh
    diff --git a/build/make/tools/buildinfo.sh b/build/make/tools/buildinfo.sh
    index 6400b1b402..cdc973275c 100755
    --- a/build/make/tools/buildinfo.sh
    +++ b/build/make/tools/buildinfo.sh
    @@ -75,3 +75,4 @@ echo "system_init.startsensorservice=1"
     echo "ro.knowin.version.firmware=$BUILD_FIRMWARE_VERSION"
     echo "nfc.debug_enabled=0"
     echo "nfc.nxp_log_level_nci=0"
    +echo "sys.zigbee.enable=0"
    weiqifa@bsp-ubuntu1804:~/mt8167s-9.0-sdk$ 
    

    #Android 分区fstab

    这部分和init.rc关系不大,但是分区的挂载都是在init.rc里面去触发的,所以需要关注。

    device\mediateksample\xxx\recovery_emmc.fstab
    recovery_emmc.fatab
    

    文件内容

    boot             /boot       emmc      defaults        defaults
    /dev/block/mmcblk0p2      /cache      ext4      defaults        defaults
    /dev/block/mmcblk0p3      /data       ext4      defaults        defaults
    misc            /misc       emmc      defaults        defaults
    recovery        /recovery   emmc      defaults        defaults
    /dev/block/mmcblk0p4      /sdcard     vfat      defaults        defaults
    /dev/block/mmcblk0p6      /system     ext4      defaults        defaults
    

    #给增加的property增加权限

    不要以为增加了property就可以使用权限了,还需要给增加的property在某个位置修改一下。

    这一步至关重要,要不然我们会发现触发了property,但是程序却没有执行。

    weiqifa@bsp-ubuntu1804:~/mt8167s-9.0-sdk$ git diff system/core/init/stable_properties.h
    diff --git a/system/core/init/stable_properties.h b/system/core/init/stable_properties.h
    old mode 100644
    new mode 100755
    index 4972d101f1..262039c57b
    --- a/system/core/init/stable_properties.h
    +++ b/system/core/init/stable_properties.h
    @@ -58,6 +58,7 @@ static const std::set<std::string> kExportedActionableProperties = {
         "vold.post_fs_data_done",
         "vts.native_server.on",
         "wlan.driver.status",
    +    "sys.zigbee.enable",
     };
     
     }  // namespace init
    weiqifa@bsp-ubuntu1804:~/mt8167s-9.0-sdk$ 
    

    #增加权限后,在init.rc 判断才能触发?

    +on property:sys.zigbee.enable=1
    +    chmod 0666 /dev/ttyMT0
    +    chmod 0666 /dev/ttyMT1
    +    chmod 0666 /dev/ttyMT2
    

    #在init.rc 里面加上打印日志

     write /dev/kmsg "[UART0]=== weiqifa === start post-fs-data"
    

    然后在dmesg 里面可以看到打印的信息

    #常见的问题

    列出的问题是我们项目中遇到的,如果大家有遇到,可以按照我们的修改方式进行修改。

    我们在项目中给adb加了key值在data分区下,在使用的过程中,会发现data目录下的key被篡改,通过修改,把文件放在vendor下后正常。

    被篡改的原因可能因为权限问题,也可以尝试在放文件的时候,修改文件权限只对adbd服务可读访问。

    还有一个问题是我们的应用,需要使用的一些资源,放在data下,也遇到被篡改。

    修改的方法与上面的类似,我们把文件放在vendor下,然后每次开机的时候,再拷贝到data目录下,确保每次开机文件都存在。

    当然,也可以让app直接访问vendor下的文件。

    我最这部分的理清主要为了解决我们项目中有一个服务启动速度慢,原来依赖的是data分区,我把代码修改为依赖sdcard分区,在sdcard分区挂载完成后,触发我们服务的启动,以达到服务开机快速启动的效果。

    推荐阅读:

    专辑|Linux文章汇总

    专辑|程序人生

    专辑|C语言

    我的知识小密圈

    展开全文
  • 安卓11 init初始化以及init.rc的解析执行过程详解

    千次阅读 热门讨论 2021-01-07 16:12:47
    最近做了一个高通平台安卓的需求,功能使得data分区在第一次启动时,自动适配emmc/ufs的实际大小,在此过程中对init的执行以及.rc文件的解析流程有了一些理解,趁热打铁!!在这里总结一下!!! 这里以mtk平台为例...

            最近做了一个高通平台安卓的需求,功能使得data分区在第一次启动时,自动适配emmc/ufs的实际大小,在此过程中对init的执行以及.rc文件的解析流程有了一些理解,但是对于一些细节的东西还不清楚,在这里提出几个自己疑惑的关键问题,趁热打铁!!梳理并寻找答案!!!

    这里以高通平台为例,基于最新的安卓11,init这块的代码mtk与高通基本是一模一样的(差异很小),都是中间层的东西;

    1,init进程在第二初始阶段如何加载init.rc,在整个工程中有几个地方存放rc文件,存放有什么规律??

    2,根据目前的理解,rc文件中的cmd最终映射到了对应的函数执行,如下图,他们是什么时候被调用的?? 例如:rc文件中cmd  mount_all  最终会映射执行do_mount_all()函数;

    3,*.rc文件加载的时候就伴随执行吗,还是是分开的??

    4,目前看分区并不是一次性挂载的,分区分了几次挂载,这几次区分有什么规律??

            对于init在整个系统中(宏观)的执行流程想必大家都很清楚了,init进程是linux内核启动后创建的第一个进程,地位非常重要,init进程在初始化过程中会启动很多重要的守护进程,因此理解init进程的启动过程可以使我们更好的理解安卓系统,init本身也是一个守护进程,linux内核加载完毕后,会首先启动init进程,启动过程中会解析linux配置脚本init.rc文件,根据init.rc文件的内容,init进程会装载Android的文件系统,创建系统目录,初始化属性系统,启动android系统重要的守护进程, 这些进程包括USB守护进程,adb守护进程,vold守护进程,rild守护进程等;

            最后init进程也会作为守护进程来执行修改属性请求,重启崩溃的进程操作;

    init进程初始化过程

            init进程的源码位于目录system/core/init 下,程序的入口函数main()位于文件main.cpp中;

    main函数的流程;

    int main(int argc, char** argv) {
    #if __has_feature(address_sanitizer)
        __asan_set_error_report_callback(AsanReportCallback);
    #endif
    
        if (!strcmp(basename(argv[0]), "ueventd")) {
            return ueventd_main(argc, argv);//uevent的初始化
        }
    
        if (argc > 1) {
            if (!strcmp(argv[1], "subcontext")) {
                android::base::Initialling(argv, &android::base::KernelLogger);
                const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();//命令映射表
                
                return SubcontextMain(argc, argv, &function_map);
            }
    
            if (!strcmp(argv[1], "selinux_setup")) {//selinux的初始化设置
                return SetupSelinux(argv);
            }
    
            if (!strcmp(argv[1], "second_stage")) {
                return SecondStageMain(argc, argv); //init的第二阶段初始化
            }
        }
    
        return FirstStageMain(argc, argv); //init的第一阶段初始化
    }
    system/core/init/main.cpp

    main函数一开始,首先初始化了守护进程uevent,然后紧接着的就是init的初始化过程;

    init引导序列分为三个主要阶段:1 first_stage_init;

                                                        2 selinux_setup;(可选)

                                                        3 second_stage_init;

    其中 first_stage_init负责设置最低限度的基本需求用以加载系统其余部分,具体来说,包括“/dev”,“/proc”的挂载,挂载“early mount”分区(这包括所有包含系统代码的分区,例如system和vendor)对于有ramdisk的设备,将system.img挂载到“/”;

    一旦first_stage_init完成接着执行 execs /system/bin/init 并以“selinux_setup”作为参数,在这个阶段,SELinux可选地编译并加载到系统中,这个阶段主要是加载初始化selinux相关的东西,关于此更多的细节在Selinux.cpp中;

    最后,一旦该阶段结束,它将再次使用"second_stage"参数执行/system/bin/init。此时,init的主要阶段将运行并通过init继续引导init.rc脚本

    由此我们可以知道rc文件的解析执行均在init的第二阶段,因此我们需要重点关注init初始化的第二阶段

    second_stage_init;

    ...

    初始化属性系统;

    初始化信号;

    LoadBootScripts(am, sm); //加载*.rc文件

    进入while(1)循环,监听处理到达的事件;

    init.rc语言

    这部分将介绍init.rc文件的格式这是理解解析过程的根本;

    Android Init语言由5大类语句组成:
    Actions, Commands, Services, Options, 和 Imports.

    以下是对各个语句的简单解释

    actions

    actions其实就是以序列的commands的集合,每个actions都有一个trigger,它用于决定action的执行时机,当一个符合action触发条件的事件发生了,此action会加入到执行队列的末尾,除非它已经在队列里;

    每一个action都将以此从队列中取出,此action的每个command都将依次执行,在这些命令执行时init还同时处理这其他活动(设备节点的创建和销毁,设置属性,重启进程);

    services:

    services是一个后台程序,在init中启动,如果退出了可以由系统重启(可选);

    options

    options是services的修饰项,它们决定一个services何时以及如何运行;

    triggers

    Triggers是一个用于匹配某种事件类型的字符串,它将使对应的actions执行;

    触发器分为事件触发器和属性触发器。

    事件触发器
    是由'trigger'命令或init可执行文件中的QueueEventTrigger()函数触发的字符串。它们采用简单字符串的形式,如'boot'或'late-init'。

    属性触发器
    是指指定属性将值更改为给定的新值或指定属性将值更改为任何新值时触发的字符串。它们分别采用'property:='和'property:=\*'的形式。属性触发器将在init的初始引导阶段额外计算并相应地触发。

    一个操作可以有多个属性触发器,但可能只有一个事件触发器。

    commands

    command是actions的命令列表中的命令,或者是service的选项参数命令;

    import

    一般用作 “import <path>”,扩展当前配置。如果path是一个目录,该目录中的每个文件都被解析为一个配置文件。它不是递归的,嵌套的目录将不会被解析。

    import关键字不是命令,而是它自己的部分,这意味着它不是作为action的一部分发生的,而是在解析文件时处理导入;

    第一级安装设备的实际顺序是:
        1. 解析/init.rc,然后递归地解析它的每个导入(此处递归是import的递归,不是文件夹的递归,文件夹不支持递归);
        2. /system/etc/init/的内容按字母顺序排列并按顺序解析,在每个文件解析后递归地进行导入;
        3. 步骤2重复/vendor/etc/init,然后是/odm/etc/init;

    -----------------

    /init.rc是主要的.rc文件,由init可执行文件在开始执行时加载。它负责系统的初始设置。

    在加载主目录/init.rc后,init立即加载包含在/{system,vendor,odm}/etc/init/目录中的所有文件。

    rc文件的存放目录以及目的:
        1 /system/etc/init/  用于核心系统项,例如 SurfaceFlinger, MediaService和logd。
        2 /vendor/etc/init/  是针对SoC供应商的项目,如SoC核心功能所需的actions或守护进程。
        3 /odm/etc/init/      用于设备制造商的项目,如actions或运动传感器或其他外围功能所需的守护进程。

    以下是个人认为理解init.rc脚本重要的几点内容:

    1,init.rc文件是以section为单位组织的,一个section可以包含多行,section可以分为两大类,一类是action,另一类是service;action以关键字on开始,表示一组命令的集合,service以关键字service开始,表示启动某个进程的方式和参数;

    2,section以on或service开始,直到下一个on或者service结束,中间的所有行都属于这一个section(空行或者注释不具有分割作用),截取init.rc部分如下

    on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
        exec_start boringssl_self_test_apex32
    on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
        exec_start boringssl_self_test_apex64
    
    service boringssl_self_test32 /system/bin/boringssl_self_test32
        setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
        reboot_on_failure reboot,boringssl-self-check-failed
        stdio_to_kmsg

    根据1,2可以得出,上图有3个section,其中两个是action,一个是service;

    3,无论是action还是service,并不是按照文件的编排顺序执行的,他们只是一份定义,至于执行与否以及什么时候执行取决于init在运行时的操作.
     

    脚本文件的解析过程:

    init进程启动时最重要的工作就是解析并执行启动文件init.rc,本节介绍init进程解析脚本文件的流程;

    这里我们主要以解析action为例讲解,service的解析过程同理,首先讲解基本流程,然后列举实例进行解析分析;

    init.rc文件以及*.rc文件加载是从下面这个函数开始的:

    static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
        Parser parser = CreateParser(action_manager, service_list);
    
        std::string bootscript = GetProperty("ro.boot.init_rc", "");
        if (bootscript.empty()) {
            parser.ParseConfig("/system/etc/init/hw/init.rc");
            if (!parser.ParseConfig("/system/etc/init")) {
                late_import_paths.emplace_back("/system/etc/init");
            }
            // late_import is available only in Q and earlier release. As we don't
            // have system_ext in those versions, skip late_import for system_ext.
            parser.ParseConfig("/system_ext/etc/init");
            if (!parser.ParseConfig("/product/etc/init")) {
                late_import_paths.emplace_back("/product/etc/init");
            }
            if (!parser.ParseConfig("/odm/etc/init")) {
                late_import_paths.emplace_back("/odm/etc/init");
            }
            if (!parser.ParseConfig("/vendor/etc/init")) {
                late_import_paths.emplace_back("/vendor/etc/init");
            }
        } else {
            parser.ParseConfig(bootscript);
        }
    }

    以下是主要的解析流程:

    ParseConfig 函数传入需要解析的rc文件的路径,如果是目录,则遍历该目录取出所有的 rc 文件并调用 ParseConfigFile 函数进行解析,如果是文件路径,则直接调用 ParseConfigFile 函数进行解析。

    从代码中可以看出,init 解析 rc 文件的过程中,首先调用 ReadFile 函数将 rc 文件的内容全部保存为字符串,存在 data 中,然后调用 ParseData 进行解析。ParseData 函数会根据关键字解析出service和action,最终挂在到 service_list 与 action_manager  的向量(vector)上。

    下面分析一下 ParseData 函数,根据关键字的不同会调用不同的 parser 去解析(多态),action 使用 ActionParser,而 service 使用 ServiceParser 解析,该部分定义在LoadBootScrip()函数的第一行,parser.AddSectionParser()方法为parser的map成员section_parsers_创建了三个SectionParser,分别用来解析service,on,import的section;

    下面重点分析ParseData函数:

    next_token(&parse_state)处理数据类型使用parse_state的构体返回:

    struct parse_state
    {
        char *ptr;      // 要解析的字符串
        char *text;     // 解析到的字符串,可以理解为返回一行的数据
        int line;       // 解析到第行数
        int nexttoken;  // 解析状态,有 T_EOF T_NEWLINE T_TEXT
    };//其中 T_EOF 表示字符串解析结束,T_NEWLINE 表示解析完一行的数据,T_TEXT 表示解析到一个单词

    其中 T_EOF 表示字符串解析结束,T_NEWLINE 表示解析完一行的数据,T_TEXT 表示解析到一个单词,

    void Parser::ParseData(const std::string& filename, std::string* data) {
        data->push_back('\n');  // TODO: fix tokenizer
        data->push_back('\0');
    
        parse_state state;
        state.line = 0;
        state.ptr = data->data();
        state.nexttoken = 0;
    
        SectionParser* section_parser = nullptr;
        int section_start_line = -1;
        std::vector<std::string> args;
    
        // If we encounter a bad section start, there is no valid parser object to parse the subsequent
        // sections, so we must suppress errors until the next valid section is found.
        bool bad_section_found = false;
    
        auto end_section = [&] {//lambda类似于一个函数指针
            bad_section_found = false;
            if (section_parser == nullptr) return;
            //同样是调用相应的section的EndSection()结束该section的解析;
            if (auto result = section_parser->EndSection(); !result.ok()) {
                parse_error_count_++;
                LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
            }
    
            section_parser = nullptr;
            section_start_line = -1;
        };
    
        for (;;) {
            switch (next_token(&state)) {
                case T_EOF:
                    end_section();
    
                    for (const auto& [section_name, section_parser] : section_parsers_) {
                        section_parser->EndFile();//解析到文件末尾,则结束
                    }
    
                    return;
                case T_NEWLINE: { // 开始处理新的一行
                    state.line++;
                    if (args.empty()) break;
                    // If we have a line matching a prefix we recognize, call its callback and unset any
                    // current section parsers.  This is meant for /sys/ and /dev/ line entries for
                    // uevent.
                /*    auto line_callback = std::find_if(
                        line_callbacks_.begin(), line_callbacks_.end(),
                        [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
                    if (line_callback != line_callbacks_.end()) {
                        end_section();
    
                        if (auto result = line_callback->second(std::move(args)); !result.ok()) {
                            parse_error_count_++;
                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        }
                */              //这部分是uevent的暂时跳过
                //section_parsers_ 是一个map,是在LoadBootScrip()第一行创建了三个键值对  "on"      ---> ActionParser
                //                                                                 "import"  ---> ImportParser
                //                                                                 "service" ---> ServiceParser
                //args[0]为某一行的第一个单词,该行由可能为section的开头,也有可能为command行,count()方法判断该行的第一个单词是不是为on/import/serivce,如果是则返回1
                    } else if (section_parsers_.count(args[0])) { 
                        end_section();//在处理新的section前,结束之前的section;
                        section_parser = section_parsers_[args[0]].get(); //依据args[0]是on/import/service取出其对应的解析方法的地址ActionParser/ImportParser/ServiceParser
                        section_start_line = state.line;
                        if (auto result =
                                    section_parser->ParseSection(std::move(args), filename, state.line); //调用相应的section解析函数(ActionParser/ImportParser/ServiceParser)解析section
                            !result.ok()) {
                            parse_error_count_++;
                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                            section_parser = nullptr;
                            bad_section_found = true;
                        }
                    } else if (section_parser) {//该行的第一个单词不是新的section的开头,则使用上一次的解析方法的函数ActionParser/ImportParser/ServiceParser解析其命令行
                        if (auto result = section_parser->ParseLineSection(std::move(args), state.line);//解析命令
                            !result.ok()) {
                            parse_error_count_++;
                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        }
                    } else if (!bad_section_found) {
                        parse_error_count_++;
                        LOG(ERROR) << filename << ": " << state.line
                                   << ": Invalid section keyword found";
                    }
                    args.clear();
                    break;
                }
                case T_TEXT: //解析到一个单词,就把它存入args中
                    args.emplace_back(state.text);
                    break;
            }
        }
    }

    next_token()函数的作用就是寻找单词结束或者行结束标志,如果是单词结束标志就将单词push到args中,如果是行结束标志,则根据第一个单词来判断是否是一个section,section的标志只有三个"on","service","import",如果是"section",则调用相应的ParseSection()来处理一个新的section,否则把这一行继续作为前“section”所属的行来处理。

    ParseSection()被用来解析一个新的section,ParseLineSection()被用来解析该section下的命令行,依据注释内容我们可以绘制出解析的基本流程图如下:

    针对action,import,service分别定义了其对应的三个函数(不列举import了),这三个函数在解析中扮演了重要的角色;

    ActionParser::ParseSection()                               ServiceParser::ParseSection()                  ....

    ActionParser::ParseLineSection()                        ServiceParser::ParseLineSection() 

    ActionParser::EndSection()                                  ServiceParser::EndSection()

    还是以下面action为例进行说明,

    on boot
        ifup io
        start sshd

     首先next_token解析到 on boot这一行,根据args[0] 值为“on” 取出ActionParser的指针,首先调用ActionParser::ParseSection处理这个新的action;

    Result<void> ActionParser::ParseSection(std::vector<std::string>&& args,
                                            const std::string& filename, int line) {
        std::vector<std::string> triggers(args.begin() + 1, args.end());
        if (triggers.size() < 1) {
            return Error() << "Actions must have a trigger";
        }
        Subcontext* action_subcontext = nullptr;
        if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {
            action_subcontext = subcontext_;
        }
        std::string event_trigger;
        std::map<std::string, std::string> property_triggers;
        if (auto result =
                    ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
            !result.ok()) {
            return Error() << "ParseTriggers() failed: " << result.error();
        }
    #ifdef G1122717
        for (const auto& [property, _] : property_triggers) {
            action_manager_->StartWatchingProperty(property);
        }
    #endif
        auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
                                               property_triggers);
        action_ = std::move(action);
        return {};
    }

     ActionParser::ParseSection函数首先解析trigger,以trigger作为参数(triggle分为事件触发和属性触发,都会被赋值给action对象的相应事件成员)构造一个结构action 并赋值给ActionParser的成员函数action_,之后使用ActionParser::ParseLineSection处理下一行命令,需要处理该on boot下的行命令ifup io,调用ActionParser::ParseLineSection对命令行进行处理,这个函数首先会根据ifup这个命令查询BuiltinFunctionMap表找到ifup对应的处理函数,

    Result<void> Action::AddCommand(std::vector<std::string>&& args, int line) {
        if (!function_map_) {
            return Error() << "no function map available";
        }
        auto map_result = function_map_->Find(args);
        if (!map_result.ok()) {
            return Error() << map_result.error();
        }
        commands_.emplace_back(map_result->function, map_result->run_in_subcontext, std::move(args),
                               line);
        return {};
    }

    如上图ifup对应的处理函数为do_ifup,找到该函数后,根据该函数以及其参数 “io” 构造出一个command实例,并把它push到第一步构造出的action_的成员commands_中,接着继续调用ActionParser::ParseLineSection()处理下一行命令start sshd同样最后把其push到commands_中;

    Result<void> ActionParser::EndSection() {
        if (action_ && action_->NumCommands() > 0) {
            action_manager_->AddAction(std::move(action_));
        }
        return {};
    }

    此时,ActionParser的成员函数action_ 就代表这个解析出来的action的整体,(这里可以看出action类就是对一个整个action的抽象)最后调用ActionParser::EndSection()把这个解析出来的整体的action_添加到ActionParser的单例成员(ActionManager类型)action_manager_中,ActionManager单例成员action_manager_中包含了所有解析出来的action,相当于之前使用的链表,这就是一个action的解析过程;

    执行action

    action和service解析完成之后,所有的action和service都被挂在ActionParser->action_manager_->actions_ServiceParser->service_list_->services_ 这两个向量里;向量中的每一个action和service都完整的描述了一个action和service的section,在解析完成之后SecondStageMain()函数会调用如下代码:

        am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
        am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
        am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
        am.QueueEventTrigger("early-init");
    
        // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
        am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
        // ... so that we can start queuing up actions that require stuff from /dev.
        am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
        am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
        Keychords keychords;
        am.QueueBuiltinAction(
                [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
                    for (const auto& svc : ServiceList::GetInstance()) {
                        keychords.Register(svc->keycodes());
                    }
                    keychords.Start(&epoll, HandleKeychord);
                    return {};
                },
                "KeychordInit");
    
        // Trigger all the boot actions to get us started.
        am.QueueEventTrigger("init");

    am.QueueEventTrigger("early-init");意为early-init时间已经到来,可以执行triggle只为early-init的action了,QueueEventTrigger()函数只把triggle加入到ActionManager的event_queue_中,对于 am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups") 该函数在创建action的同时,把该事件触发添加到ActionManager的事件队列中,后续会遍历ActionManager的event_queue_找到的triggle对应的action的command会被依次执行,过程如下:

    while (true) {
            // By default, sleep until something happens.
            // 决定timeout的时间
            auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
    
            auto shutdown_command = shutdown_state.CheckShutdown();
            // 判断是否执行了关机
            if (shutdown_command) {
                HandlePowerctlMessage(*shutdown_command);
            }
            //判断是否有事件需要处理
            if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
                am.ExecuteOneCommand();
            }
            if (!IsShuttingDown()) {
                auto next_process_action_time = HandleProcessActions();
    
                // If there's a process that needs restarting, wake up in time for that.
                if (next_process_action_time) {
                    epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                            *next_process_action_time - boot_clock::now());
                    if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
                }
            }
    
            if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
                // If there's more work to do, wake up again immediately.
                if (am.HasMoreCommands()) epoll_timeout = 0ms;
            }
    
            auto pending_functions = epoll.Wait(epoll_timeout);
            if (!pending_functions.ok()) {
                LOG(ERROR) << pending_functions.error();
            } else if (!pending_functions->empty()) {
                // We always reap children before responding to the other pending functions. This is to
                // prevent a race where other daemons see that a service has exited and ask init to
                // start it again via ctl.start before init has reaped it.
                ReapAnyOutstandingChildren();
                for (const auto& function : *pending_functions) {
                    (*function)();
                }
            }
            if (!IsShuttingDown()) {
                HandleControlMessages();
                SetUsbController();
            }
        }
    
        return 0;
    }

    init最终会进入了无限循环的监听状态,可以看到这里面一个核心函数就是 am.ExecuteOneCommand();该函数具体如下:

    void ActionManager::ExecuteOneCommand() {
        {
            auto lock = std::lock_guard{event_queue_lock_};
            // 编译event_queue_ 队列直到有事件处理
            while (current_executing_actions_.empty() && !event_queue_.empty()) {
                //遍历action的向量(链表),包括所有解析出来的action,每一个action都包含了完整的信息(command,triggle等)
                for (const auto& action : actions_) {
                // 一个action是否要执行,事件trigger和属性trigger都必须要满足,这里检查event_queue_的第一个元素的属性事件是不是满足,会从属性map表里查找其值,如果满足才会执行下一步
                    if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
                                   event_queue_.front())) {
                        //如果满足这证明该action需要执行,把action压入current_executing_actions_当前执行队列中;
                        current_executing_actions_.emplace(action.get());
                    }
                }
                event_queue_.pop();
            }
        }
    
        if (current_executing_actions_.empty()) {
            return;
        }
        // 从当前需要执行的action队列中取出第一个要执行的action
        auto action = current_executing_actions_.front();
    
        if (current_command_ == 0) {
            std::string trigger_name = action->BuildTriggersString();
            LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
                      << ":" << action->line() << ")";
        }
        // 开始执行action
        action->ExecuteOneCommand(current_command_);
    
        // 如果这是当前需要执行的action的最后一个命令,则从current_executing_actions_队列中移除该action
        // 如果这个action只执行依次,则从actions_向量中移除它
        ++current_command_;
        if (current_command_ == action->NumCommands()) {
            current_executing_actions_.pop();
            current_command_ = 0;
            if (action->oneshot()) {
                auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
                actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),
                               actions_.end());
            }
        }
    }

    在上一步中,QueueEventTrigger("early-init")把early-init加入到event_queue_的队列中,ExecuteOneCommand()一开始就遍历之前解析的action向量表,使用每一个action自己的eventtrigger和event_queue_队列中的第一个trigger对比,如果一样,则继续判断该action中的PropertyTriggers,遍历PropertyTriggers的map表查找当前action的PropertyTriggers值是否满足条件,如果在eventtrigger相同且PropertyTriggers满足条件的情况下,就把当前action push到current_executing_actions_队列中;

    接下来,从current_executing_actions_队列中取出第一个要执行的action,调用action->ExecuteOneCommand(current_command_); 执行该命令,如下代码

    Result<void> Command::InvokeFunc(Subcontext* subcontext) const {
        if (subcontext) {
            if (execute_in_subcontext_) {
                return subcontext->Execute(args_);
            }
    
            auto expanded_args = subcontext->ExpandArgs(args_);
            if (!expanded_args.ok()) {
                return expanded_args.error();
            }
            return RunBuiltinFunction(func_, *expanded_args, subcontext->context());
        }
    
        return RunBuiltinFunction(func_, args_, kInitContext);
    }

    该action下的所有命令被执行完成,;

    当一个 action 对象所有的 command 均执行完毕后,再执行下一个action。

    当一个 trigger 触发对应的所有 action 对象均执行完毕后(一个action有且仅有一个eventtrigger,但是一个eventtrigger可以对应多个action,他们可以由不同的属性),再执行下一个 trigger 对应 action。

    到此就是一个action被解析以及到被执行的整个过程!!因为service和action存在自有的特性,因此解析执行稍有不同,需要理解的可以自行分析;

    文章一开始的四个问题,前三个都在解析分析的过程中解开了;

    关于分区挂载的后续碰到再另行补充吧!

    展开全文
  • Android源码之init.rc文件详解

    千次阅读 2020-01-20 23:30:46
    一、引言 .rc文件是 android系统一个十分重要的文件。...Android init.rc文件由系统第一个启动的init程序解析。是启动系统服务使用的文件。 三、文件规则 类型 主要包含了四种类型的语句: 1、Acti...

    一、引言

    .rc文件是 android系统一个十分重要的文件。
    其是资源文件,包括比如对话框、菜单、图标、字符串等资源信息。
    使用.rc资源文件的目的是为了对程序中用到的大量的资源进行统一的管理。 本文来了解文件的规则。

    小广告

    中间打个小广告,是本人的一个小小副业。

    大家有需要各种品牌的鞋(耐克、阿迪、斐乐、vans、匡威等),都可以加下面这个微信号,性价比巨高,质量绝对可靠,自己穿的也都是这买的,到手不喜欢,不影响二次销售可直接退货,希望大家能多多支持(暂时不想买的也欢迎添加,首双优惠!),全国包邮!

    vx:cp_shop12138

    二、Android中init.rc文件简单介绍

    init.rc脚本是由Android中linux的第一个用户级进程init进行解析的。

    init.rc 文件并不是普通的配置文件,而是由一种被称为“Android初始化语言”(Android Init Language,这里简称为AIL)的脚本写成的文件。
    init.rc脚本包括了启动脚本文件,主要完成一些初级的初始化,文件系统初始化

    主要是:
    1)设置一些环境变量
    2)创建system、sdcard、data、cache等目录(见案例1)
    3)把一些文件系统mount到一些目录去,如,mount tmpfs tmpfs /sqlite_stmt_journals
    4)设置一些文件的用户群组、权限
    5)设置一些线程参数
    6)设置TCP缓存大小

    该文件在ROM中是只读的,即使有了root权限,可以修改该文件也没有。因为我们在根目录看到的文件只是内存文件的镜像。也就是说,android启动后,会将init.rc文件装载到内存。而修改init.rc文件的内容实际上只是修改内存中的init.rc文件的内容。一旦重启android,init.rc文件的内容又会恢复到最初的装载。想彻底修改init.rc文件内容的唯一方式是修改Android的ROM中的内核镜像(boot.img)。

    如果想要修改启动过程只需要修改init.c(system/core/init)或者init.rc里的内容即可.

    init.c与init.rc在源码中的位置分别位于如下:
    1 init.c : /system/core/init
    2 init.rc : /system/core/rootdir

    三、文件规则

    类型

    主要包含了四种类型的语句:
    1、Action
    2、Services
    Action和services显式声明了一个语句块

    3、Commands
    4、Options.
    commands和options属于最近声明的语句块。

    在第一个语句块之前 的commands和options会被忽略.

    基本规则

    1、在init.rc文件中一条语句通常是占据一行。
    2、单词之间是通过空格符来相隔的。
    3、如果需要在单词内使用空格,那么得使用转义字符""。
    4、末尾的反斜杠,是换行折叠符号,应该和下一行合并成一起来处。 理,与C语言中的含义是一致的。
    5、以#号开头的为注释。

    关键字

    关键字位于语句块的首部,决定了这个语句块的种类

    关键字含义
    Action动作
    trigger触发器。或者叫做触发条件.
    commands命令
    services服务
    1、触发器(trigger)

    在"动作"(action)里面的,on后面跟着的字符串是触发器(trigger),trigger是一个用于匹配某种事件类型的字符串,它将对应的Action的执行。
    触发器(trigger)有几种格式:

    1、最简单的一种是一个单纯的字符串。比如“on boot”。这种简单的格式可以使用命令"trigger"来触发。
    2、还有一种常见的格式是"on property : <属性>=<值>"。如果属性值在运行时设成了指定的值,则"块"(action)中的命令列表就会执行。

    常见的格式:

    格式含义
    on early-init在初始化早期阶段触发
    on init在初始化阶段触发
    on late-init在初始化晚期阶段触发
    on boot/charger当系统启动/充电时触发
    on property当属性值满足条件时触发
    2、动作(Action)

    动作表示了一组命令(commands)组成.动作包括一个触发器,决定了何时运行这个动作。
    注意:当触发器的条件满足时,这个动作会被增加到已被运行的队列尾。假设此动作在队列中已经存在,那么它将不会运行.

    on  <trigger>      ## 触发条件
        <command>      ##执行命令
        <command1>     ##可以执行多个命令
    

    示例:
    源码来自:/device/rockchip/rk322x/init.rc

    on property:vold.decrypt=trigger_encryption
        start surfaceflinger
        start encrypt
    
    3、commands(命令)

    command是action的命令列表中的命令,或者是service中的选项 onrestart 的参数命令.

    命令将在所属事件发生时被一个个地执行.

    常见命令:

    命令功能
    exec <path> [ ]*运行指定路径下的程序,并传递參数.
    export <name> <value>设置全局环境參数。此參数被设置后对全部进程都有效.
    ifup <interface>使指定的网络接口"上线",相当激活指定的网络接口
    hostname <name>设置主机名
    chdir <directory>改变工作文件夹.
    chmod <octal-mode> <path>改变指定文件的读取权限.
    chown <owner> <group> <path>改变指定文件的拥有都和组名的属性.
    chroot <directory>改变进行的根文件夹.
    class_start <serviceclass>启动指定类属的全部服务,假设服务已经启动,则不再反复启动.
    class_stop <serviceclass>停止指定类属的所胡服务.
    domainname <name>设置域名
    insmod <path>安装模块到指定路径.
    mkdir <path> [mode] [owner] [group]用指定參数创建一个文件夹,在默认情况下,创建的文件夹读取权限为755.username为root,组名为root.
    mount<type> <device> <dir> [ <mountoption> ]*类似于linux的mount指令
    setprop <name> <value>设置属性及相应的值.
    setrlimit <resource> <cur> <max>设置资源的rlimit(资源限制),不懂就百度一下rlimit
    start <service>假设指定的服务未启动,则启动它.
    stop <service>假设指定的服务当前正在执行。则停止它.
    symlink <target> <path>创建一个符号链接.
    sysclktz <mins_west_of_gmt>设置系统基准时间.
    write <path> <string>\ [ <string> ]*往指定的文件写字符串.

    4、服务(services)

    服务是指那些须要在系统初始化时就启动或退出时自己主动重新启动的程序.

    service <name><pathname> [ <argument> ]*
        <option>
        <option>
    

    解释一下各个参数:

    参数含义
    表示此服务的名称
    此服务所在路径因为是可执行文件,所以一定有存储路径。
    启动服务所带的参数
    对此服务的约束选项
    on property:sys.wifi.on=true
        start gpio_read 
    
    service gpio_read /system/bin/gpio_read.sh
        user root
        group root
        class main
        disabled
        oneshot
    
    

    设置一个gpio_read服务对应可执行文件 gpio_read.sh脚本 。设置运行的user 和group,设置为不自动启动disabled,设置为退出之后不重启oneshot。

    当属性 sys.wifi.on=true时,就会启动goio_read这个服务。

    选项(option)

    options是Service的修订项。它们决定一个服务何时以及如何运行.

    选项描述
    critical据设备相关的关键服务,如果在4分钟内,此服务重复启动了4次,那么设备将会重启进入还原模式。
    disabled服务不会自动运行,必须显式地通过服务器来启动。
    setenv设置环境变量
    socket [ [ ] ]在/dev/socket/下创建一个unix domain的socket,并传递创建的文件描述符fd给服务进程.其中type必须为dgram或stream,seqpacket.
    user在执行此服务之前先切换用户名。当前默认为root.
    group [ ]*类似于user,切换组名
    oneshot当此服务退出时不会自动重启.
    class给服务指定一个类属,这样方便操作多个服务同时启动或停止.默认情况下为default.
    onrestart当服务重启时执行一条指令,

    5、示例

    # not complete -- just providing some examples of usage  
    #  
    on boot  
       export PATH /sbin:/system/sbin:/system/bin  
       export LD_LIBRARY_PATH /system/lib  
      
       mkdir /dev  
       mkdir /proc  
       mkdir /sys  
      
       mount tmpfs tmpfs /dev  
       mkdir /dev/pts  
       mkdir /dev/socket  
       mount devpts devpts /dev/pts  
       mount proc proc /proc  
       mount sysfs sysfs /sys  
      
       write /proc/cpu/alignment 4  
      
       ifup lo  
      
       hostname localhost  
       domainname localhost  
      
       mount yaffs2 mtd@system /system  
       mount yaffs2 mtd@userdata /data  
      
       import /system/etc/init.conf  
      
       class_start default  
      
    service adbd /sbin/adbd  
       user adb  
       group adb  
      
    service usbd /system/bin/usbd -r  
       user usbd  
       group usbd  
       socket usbd 666  
      
    service zygote /system/bin/app_process -Xzygote /system/bin --zygote  
       socket zygote 666  
      
    service runtime /system/bin/runtime  
       user system  
       group system  
      
    on device-added-/dev/compass  
       start akmd  
      
    on device-removed-/dev/compass  
       stop akmd  
      
    service akmd /sbin/akmd  
       disabled  
       user akmd  
       group akmd  
    
    解析

    源码路径system/core/init/init.cpp中:

    parser.ParseConfig("/init.rc");
    

    1、

    on boot   
    

    系统启动时自动运行
    2、

       export PATH /sbin:/system/sbin:/system/bin  
       export LD_LIBRARY_PATH /system/lib  
       mkdir /dev  
       mkdir /proc  
       mkdir /sys  
       ......
    

    设置全局环境參数。此參数被设置后对全部进程都有效
    之后执行以下一系列动作:创建对目录,挂接文件系统等

    3、

    service adbd /sbin/adbd  
       user adb  
       group adb  
    

    执行adbd /sbin/adbd 设置执行的用户

    4、

    class_start default  
      
    service adbd /sbin/adbd  
       user adb  
       group adb
    

    给服务指定一个类属,这样方便操作多个服务同时启动或停止.默认情况下为default.
    class_start default:则是启动该类下的所有服务
    4、之后以此推类

    展开全文
  • init.rc介绍

    千次阅读 2019-06-14 12:28:48
    init.rc介绍 我先来做个名词解释,什么是init.rc,那就要从什么是init说起。init是由Android的Linux内核启动的第一个第一个进程,这个进程非常特殊,它的PID永远是1,并且这个进程是不会死亡的,如果它死亡,内核就...
  • Android init.rc文件详解

    千次阅读 2018-01-29 18:17:28
    一、文件简介init.rc:Android在启动过程中读取的启动脚本文件,主要完成一些初级的初始化,在/system/core/init/init.c中解析。rc 经常被用作程序之启动脚本的文件名。它是“run commands”(运行命令)的缩写。...
  • Android init.rc启动顺序

    2020-03-14 16:48:19
    kernel启动完成,最后启动init进程,init进程中解析init.rc. int main(int argc, char** argv) { open_console(); if (!strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); } if (!...
  • init.rc文件中执行shell命令

    千次阅读 2019-11-06 20:21:19
    device/fsl/imx8q/etc/reboot_adb device/fsl/imx8q/mek_8q/BoardConfig....device/fsl/imx8q/mek_8q/init_car.rc device/fsl/imx8q/sepolicy_car/file_contexts device/fsl/imx8q/sepolicy_car/reboot_adb.te syste...
  • Android init.rc 服务启动不成功

    千次阅读 2018-09-21 14:39:00
    Android init.rc 服务启动不成功 问题 在开发过程中发现一个问题,我们需要在开机的时候判断硬件版本号去启动服务, 服务的名字是ledservice和ledservice4,但是发现每次烧录完固件后,服务启动不 成功...
  • 关于安卓中的init.rc文件,官网已经说明地非常清楚。 官网资料: https://android.googlesource.com/platform/system/core/+/master/init/README.md添加链接描述 ...下面举一个使用的小例子,关于init.rc...
  • android的init.rc文件的语法
  • init.rc的解析是在parse_config(): [system/core/init/init_parser.c]中进行的。解析发生在init全过程中的哪个阶段,参看《Android init进程启动过程分析》。一、解析过程1. 扫描init.rc中的token 找到其中的 ...
  • init.rc:Android在启动过程中读取的启动脚本文件,主要完成一些初级的初始化,在/system/core/init/init.c中解析。 init.xx.rc:与具体CPU相关的启动脚本,比如对于飞思卡尔的CPU,名字为init.freescale.rc。在...
  • 在定制化开发中,由于系统给的文件节点不能满足需要,所以就要自己添加文件夹和节点,而系统启动过程中,大多数节点都是在init.rc中创建的 所以就在这里添加节点就可以了 接下来看下 system/core/init/init.cpp源码 ...
  • Android系统init进程启动及init.rc全解析

    万次阅读 多人点赞 2017-11-23 16:14:40
    system/core/init/init.c文件main函数中parse_config_file(init.rc)读取并解析init.rc文件内容。将service信息放置到system/core/init/init_parser.cpp的service_list中 system/core/init/init.c文
  • 前言:Android系统启动init进程会去解析/init.rc or init.platform.rc、/etc/init/*.rcrc档,以便启动一些进程和服务。这里主要看下启动这些进程或服务的属性触发条件。 背景:了解init.rc的语法格式,网上很多...
  • Android init.rc语法介绍

    千次阅读 2017-12-11 15:26:21
     Android init.rc文件由系统第一个启动的init程序解析,此文件由语句组成,主要包含了四种类型的语句:Action, Commands,Services, Options. 在init.rc 文件中一条语句通常是占据一行.单词之间是通过空格符来相隔的...
  • Android init.rc 添加自定义服务

    千次阅读 2019-12-16 10:20:35
    Android init.rc 添加自定义服务,运行系统bin文件一、按照rc语法格式添加新增service二、添加te文件三、rc语法链接四、avc权限添加 一、按照rc语法格式添加新增service rc文件中添加如下 # 最后两个必写,其他的...
  • init.rc深入学习

    千次阅读 2017-11-02 17:36:09
    语法部分 actions service options commandscommands initrc的变化 initcpp LOCAL_INIT_RC
  • Android init.rc on property

    千次阅读 2017-07-26 23:04:32
    init.rc 中,可以见到下面类似的用法,当一个属性值等于XX时,触发下面的事件,比如启动一个进程 on property:sys.init_log_level=*  loglevel ${sys.init_log_level} 那么它是如何实现的,启动时触发一次?...
  • init.rc文件中时常可以看到如下信息。 on property:init.svc.vendor.per_mgr=running start vendor.per_proxy 这里表明property的属性init.svc.vendor.per_mgr值等于running时。则启动vendor.per_proxy。 可以...
  • Init.rc妙用及语法说明 参考:system\core\init\readme.txt案例1 当开机启动完毕,写mpp2的寄存器,使其设置为PWM模式。 #add by eliot shao 2016.11.03 for mmp2-pwm function on property:sys.boot_completed=1 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 184,358
精华内容 73,743
关键字:

init.rc