init 订阅
init是Linux系统操作中不可缺少的程序之一。所谓的init进程,它是一个由内核启动的用户级进程。内核会在过去曾使用过init的几个地方查找它,它的正确位置(对Linux系统来说)是/sbin/init。如果内核找不到init,它就会试着运行/bin/sh,如果运行失败,系统的启动也会失败。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。所以init始终是第一个进程(其进程编号始终为1)。 展开全文
init是Linux系统操作中不可缺少的程序之一。所谓的init进程,它是一个由内核启动的用户级进程。内核会在过去曾使用过init的几个地方查找它,它的正确位置(对Linux系统来说)是/sbin/init。如果内核找不到init,它就会试着运行/bin/sh,如果运行失败,系统的启动也会失败。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。所以init始终是第一个进程(其进程编号始终为1)。
信息
外文名
init
属    于
Linux系统操作中不可缺少的程序
进    程
一个由内核启动的用户级进程
正确位置
是/sbin/init
init运行级别
那么,到底什么是运行级呢?简单的说,运行级就是操作系统当前正在运行的功能级别。这个级别从1到6 ,具有不同的功能。不同的运行级定义如下:(可以参考Red Hat Linux 里面的/etc/inittab)# 0 - 停机(千万不能把initdefault 设置为0 )# 1 - 单用户模式# 2 - 多用户,没有 NFS# 3 - 完全多用户模式(标准的运行级)# 4 - 没有用到# 5 - X11 (xwindow)# 6 - 重新启动 (千万不要把initdefault 设置为6 )这些级别在/etc/inittab 文件里指定。这个文件是init 程序寻找的主要文件,最先运行的服务是放在/etc/rc.d 目录下的文件。在大多数的Linux 发行版本中,启动脚本都是位于 /etc/rc.d/init.d中的。这些脚本被用ln 命令连接到 /etc/rc.d/rcn.d 目录。(这里的n 就是运行级0-6)
收起全文
精华内容
下载资源
问答
  • 安卓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存在自有的特性,因此解析执行稍有不同,需要理解的可以自行分析;

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

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

    展开全文
  • Python super().__init__()测试及理解

    万次阅读 多人点赞 2019-02-24 13:20:17
    Python3 super().__init__()含义 测试一、我们尝试下面代码,没有super(A, self).__init__()时调用A的父类Root的属性和方法(方法里不对Root数据进行二次操作) class Root(object): def __init__(self): self.x=...

    Python super().__init__()含义(单继承,即只有一个父类)

    测试一、我们尝试下面代码,没有super(A, self).__init__()时调用A的父类Root的属性方法(方法里不对Root数据进行二次操作)

    class Root(object):
        def __init__(self):
            self.x= '这是属性'
    
        def fun(self):
        	#print(self.x)
            print('这是方法')
            
    class A(Root):
        def __init__(self):
            print('实例化时执行')
    
    test = A()		#实例化类
    test.fun()	#调用方法
    test.x		#调用属性
    
    

    下面是结果:

    Traceback (most recent call last):
    实例化时执行
    这是方法
      File "/hom/PycharmProjects/untitled/super.py", line 17, in <module>
        test.x  # 调用属性
    AttributeError: 'A' object has no attribute 'x'
    

    可以看到此时父类的方法继承成功,可以使用,但是父类的属性却未继承,并不能用

    测试二、我们尝试下面代码,没有super(A,self).__init__()时调用A的父类Root的属性方法(方法里Root数据进行二次操作)

    class Root(object):
        def __init__(self):
            self.x= '这是属性'
    
        def fun(self):
        	print(self.x)
            print('这是方法')
            
    class A(Root):
        def __init__(self):
            print('实例化时执行')
    
    test = A()		#实例化类
    test.fun()	#调用方法
    test.x		#调用属性
    
    

    结果如下

    Traceback (most recent call last):
      File "/home/PycharmProjects/untitled/super.py", line 16, in <module>
        test.fun()  # 调用方法
      File "/home/PycharmProjects/untitled/super.py", line 6, in fun
        print(self.x)
    AttributeError: 'A' object has no attribute 'x'
    

    可以看到此时报错和测试一相似,果然,还是不能用父类的属性

    测试三、我们尝试下面代码,加入super(A, self).__init__()时调用A的父类Root的属性方法(方法里Root数据进行二次操作)

    class Root(object):
        def __init__(self):
            self.x = '这是属性'
    
        def fun(self):
            print(self.x)
            print('这是方法')
    
    
    class A(Root):
        def __init__(self):
            super(A,self).__init__()
            print('实例化时执行')
    
    
    test = A()  # 实例化类
    test.fun()  # 调用方法
    test.x  # 调用属性
    
    

    结果输出如下

    实例化时执行
    这是属性
    这是方法
    

    此时A已经成功继承了父类的属性,所以super().__init__()的作用也就显而易见了,就是执行父类的构造函数,使得我们能够调用父类的属性。

    上面是单继承情况,我们也会遇到多继承情况,用法类似,但是相比另一种Root.__init__(self),在继承时会跳过重复继承,节省了资源。

    还有很多关于super的用法可以参考
    super的使用

    展开全文
  • init进程详细分析--基于android 10

    千次阅读 2020-03-16 14:20:02
    init进程详细分析 概述 android设备上电,引导程序引导进入boot(通常是uboot),加载initramfs、kernel镜像,启动kernel后,进入用户态程序。第一个用户空间程序是init,PID固定是1.在android系统上,init的代码位于/...

    init进程详细分析

    概述

    android设备上电,引导程序引导进入boot(通常是uboot),加载initramfs、kernel镜像,启动kernel后,进入用户态程序。第一个用户空间程序是init,PID固定是1.在android系统上,init的代码位于/system/core/init下,基本功能有:

    • 管理设备
    • 解析并处理启动脚本init.rc
    • 实时维护这个init.rc中的服务

    image

    流程分析

    这些init.rc只是语法文件,并不是程序,真正的入口则是上面提到的system/core/init/init.cpp .

    对比老版本差异

    在Android Q中,对该机制做了一些改变 。

    单一的init.rc,被拆分,服务根据其二进制文件的位置(/system,/vendor,/odm)定义到对应分区的etc/init目录中,每个服务一个rc文件。与该服务相关的触发器、操作等也定义在同一rc文件中。

    /system/etc/init,包含系统核心服务的定义,如SurfaceFlinger、MediaServer、Logcatd等。
    /vendor/etc/init, SOC厂商针对SOC核心功能定义的一些服务。比如高通、MTK某一款SOC的相关的服务。
    /odm/etc/init,OEM/ODM厂商如小米、华为、OPP其产品所使用的外设以及差异化功能相关的服务。
    

    这样的目录结构拆分,也与Android产品的开发流程相吻合,减轻了维护的负担。下图为Android Q 中定义的所有服务。

    • k62v1_64_bsp$ find ./ -name "*.rc" :
    展开查看
    
    ```
    ./recovery/root/init.rc
    ./recovery/root/ueventd.rc
    ./recovery/root/init.recovery.mt6762.rc
    ./recovery/root/init.recovery.mt6765.rc
    ./vendor/ueventd.rc
    ./vendor/etc/init/init.wmt_drv.rc
    ./vendor/etc/init/init.volte_imsm_93.rc
    ./vendor/etc/init/mtk_agpsd_p.rc
    ./vendor/etc/init/bootperf.rc
    ./vendor/etc/init/audiocmdservice_atci.rc
    ./vendor/etc/init/android.hardware.graphics.composer@2.1-service.rc
    ./vendor/etc/init/vendor.mediatek.hardware.gpu@1.0-service.rc
    ./vendor/etc/init/init.volte_stack.rc
    ./vendor/etc/init/init_connectivity.rc
    ./vendor/etc/init/init.fmradio_drv.rc
    ./vendor/etc/init/netdagent.rc
    ./vendor/etc/init/atcid_eng.rc
    ./vendor/etc/init/init.vtservice_hidl.rc
    ./vendor/etc/init/mtkrild.rc
    ./vendor/etc/init/android.hardware.health@2.0-service.rc
    ./vendor/etc/init/init.wfca.rc
    ./vendor/etc/init/modemdbfilter_service.rc
    ./vendor/etc/init/init.bt_drv.rc
    ./vendor/etc/init/init.gps_drv.rc
    ./vendor/etc/init/aee_aedv64.rc
    ./vendor/etc/init/android.hardware.secure_element@1.0-service-mediatek.rc
    ./vendor/etc/init/android.hardware.drm@1.2-service.clearkey.rc
    ./vendor/etc/init/vendor.mediatek.hardware.log@1.0-service.rc
    ./vendor/etc/init/init.thermal_manager.rc
    ./vendor/etc/init/init.bip.rc
    ./vendor/etc/init/init.cccimdinit.rc
    ./vendor/etc/init/md_monitor.rc
    ./vendor/etc/init/muxreport.rc
    ./vendor/etc/init/android.hardware.drm@1.0-service.rc
    ./vendor/etc/init/init.wlan_drv.rc
    ./vendor/etc/init/android.hardware.drm@1.2-service.widevine.rc
    ./vendor/etc/init/wlan_assistant.rc
    ./vendor/etc/init/camerahalserver.rc
    ./vendor/etc/init/atci_service.rc
    ./vendor/etc/init/android.hardware.cas@1.1-service.rc
    ./vendor/etc/init/ipsec_mon.rc
    ./vendor/etc/init/android.hardware.thermal@1.0-service.rc
    ./vendor/etc/init/android.hardware.gatekeeper@1.0-service.rc
    ./vendor/etc/init/android.hardware.keymaster@4.0-service.rc
    ./vendor/etc/init/init.thermalloadalgod.rc
    ./vendor/etc/init/android.hardware.graphics.allocator@2.0-service.rc
    ./vendor/etc/init/vendor.mediatek.hardware.mtkpower@1.0-init.rc
    ./vendor/etc/init/lbs_hidl_service.rc
    ./vendor/etc/init/android.hardware.sensors@2.0-service-mediatek.rc
    ./vendor/etc/init/hostapd.android.rc
    ./vendor/etc/init/init.thermal.rc
    ./vendor/etc/init/fuelgauged_nvram_init.rc
    ./vendor/etc/init/em_hidl_eng.rc
    ./vendor/etc/init/vendor.mediatek.hardware.mtkpower@1.0-service.rc
    ./vendor/etc/init/init.cccirpcd.rc
    ./vendor/etc/init/init.volte_md_status.rc
    ./vendor/etc/init/android.hardware.light@2.0-service-mediatek.rc
    ./vendor/etc/init/aee_aedv.rc
    ./vendor/etc/init/init.cccifsd.rc
    ./vendor/etc/init/gsm0710muxd.rc
    ./vendor/etc/init/loghidlvendorservice.rc
    ./vendor/etc/init/fuelgauged_init.rc
    ./vendor/etc/init/android.hardware.wifi@1.0-service-lazy-mediatek.rc
    ./vendor/etc/init/android.hardware.bluetooth@1.0-service-mediatek.rc
    ./vendor/etc/init/ppl_agent.rc
    ./vendor/etc/init/hw/init.project.rc
    ./vendor/etc/init/hw/init.sensor_1_0.rc
    ./vendor/etc/init/hw/init.mt6765.rc
    ./vendor/etc/init/hw/meta_init.connectivity.rc
    ./vendor/etc/init/hw/meta_init.project.rc
    ./vendor/etc/init/hw/multi_init.rc
    ./vendor/etc/init/hw/factory_init.project.rc
    ./vendor/etc/init/hw/factory_init.rc
    ./vendor/etc/init/hw/init.mt6762.rc
    ./vendor/etc/init/hw/init.mt6765.usb.rc
    ./vendor/etc/init/hw/factory_init.connectivity.rc
    ./vendor/etc/init/hw/init.connectivity.rc
    ./vendor/etc/init/hw/init.aee.rc
    ./vendor/etc/init/hw/init.modem.rc
    ./vendor/etc/init/hw/init.ago.rc
    ./vendor/etc/init/hw/meta_init.modem.rc
    ./vendor/etc/init/hw/meta_init.rc
    ./vendor/etc/init/init.md_apps.rc
    ./vendor/etc/init/android.hardware.media.omx@1.0-service.rc
    ./vendor/etc/init/init.volte_ua.rc
    ./vendor/etc/init/nvram_daemon.rc
    ./vendor/etc/init/init.wod.rc
    ./vendor/etc/init/vendor.mediatek.hardware.nvram@1.1-sevice.rc
    ./vendor/etc/init/android.hardware.memtrack@1.0-service.rc
    ./vendor/etc/init/android.hardware.configstore@1.1-service.rc
    ./vendor/etc/init/android.hardware.usb@1.1-service-mediatek.rc
    ./vendor/etc/init/vendor.mediatek.hardware.mtkcodecservice@1.1-service.rc
    ./vendor/etc/init/init.volte_imcb.rc
    ./vendor/etc/init/android.hardware.audio@5.0-service-mediatek.rc
    ./vendor/etc/init/android.hardware.vibrator@1.0-service.rc
    ./vendor/etc/init/vendor.mediatek.hardware.mms@1.3-service.rc
    ./vendor/etc/init/vndservicemanager.rc
    ./vendor/etc/init/android.hardware.gnss@2.0-service-mediatek.rc
    ./vendor/etc/init/vendor.mediatek.hardware.pq@2.2-service.rc
    ./system/apex/com.android.media.swcodec/etc/init.rc
    ./system/etc/init/mdlogger.rc
    ./system/etc/init/atrace.rc
    ./system/etc/init/mediaextractor.rc
    ./system/etc/init/surfaceflinger.rc
    ./system/etc/init/ashmemd.rc
    ./system/etc/init/android.system.suspend@1.0-service.rc
    ./system/etc/init/uncrypt.rc
    ./system/etc/init/camerapostalgo.rc
    ./system/etc/init/bootlogoupdater.rc
    ./system/etc/init/art_apex_boot_integrity.rc
    ./system/etc/init/usbd.rc
    ./system/etc/init/modemdbfilter_client.rc
    ./system/etc/init/atci_service_sys.rc
    ./system/etc/init/drmserver.rc
    ./system/etc/init/recovery-persist.rc
    ./system/etc/init/aee_aed64.rc
    ./system/etc/init/keystore.rc
    ./system/etc/init/wait_for_keymaster.rc
    ./system/etc/init/mobile_log_d.rc
    ./system/etc/init/mediaserver.rc
    ./system/etc/init/hwservicemanager.rc
    ./system/etc/init/init-debug.rc
    ./system/etc/init/gpuservice.rc
    ./system/etc/init/logd.rc
    ./system/etc/init/incidentd.rc
    ./system/etc/init/servicemanager.rc
    ./system/etc/init/lpdumpd.rc
    ./system/etc/init/idmap2d.rc
    ./system/etc/init/bootstat.rc
    ./system/etc/init/blank_screen.rc
    ./system/etc/init/bootstat-debug.rc
    ./system/etc/init/storaged.rc
    ./system/etc/init/netd.rc
    ./system/etc/init/mediametrics.rc
    ./system/etc/init/mediadrmserver.rc
    ./system/etc/init/wificond.rc
    ./system/etc/init/vdc.rc
    ./system/etc/init/cameraserver.rc
    ./system/etc/init/gsid.rc
    ./system/etc/init/consyslogger.rc
    ./system/etc/init/logtagd.rc
    ./system/etc/init/malloc_debug_option.rc
    ./system/etc/init/bootanim.rc
    ./system/etc/init/logcatd.rc
    ./system/etc/init/batterywarning.rc
    ./system/etc/init/statsd.rc
    ./system/etc/init/perfetto.rc
    ./system/etc/init/init.connectivity.rc
    ./system/etc/init/flags_health_check.rc
    ./system/etc/init/mtpd.rc
    ./system/etc/init/init.thermald.rc
    ./system/etc/init/mdnsd.rc
    ./system/etc/init/duraspeed.rc
    ./system/etc/init/emdlogger3.rc
    ./system/etc/init/audioserver.rc
    ./system/etc/init/terserver.rc
    ./system/etc/init/heapprofd.rc
    ./system/etc/init/netdiag.rc
    ./system/etc/init/bpfloader.rc
    ./system/etc/init/init.vtservice.rc
    ./system/etc/init/hw/init.aee.rc
    ./system/etc/init/hw/vendor_init_as_system.rc
    ./system/etc/init/emdlogger5.rc
    ./system/etc/init/iorapd.rc
    ./system/etc/init/wifi-events.rc
    ./system/etc/init/tombstoned.rc
    ./system/etc/init/kpoc_charger.rc
    ./system/etc/init/apexd.rc
    ./system/etc/init/traceur.rc
    ./system/etc/init/android.hidl.allocator@1.0-service.rc
    ./system/etc/init/gatekeeperd.rc
    ./system/etc/init/atrace_userdebug.rc
    ./system/etc/init/lmkd.rc
    ./system/etc/init/rss_hwm_reset.rc
    ./system/etc/init/installd.rc
    ./system/etc/init/racoon.rc
    ./system/etc/init/emdlogger2.rc
    ./system/etc/init/emdlogger1.rc
    ./system/etc/init/em_svr.rc
    ./system/etc/init/dumpstate.rc
    ./system/etc/init/aee_aed.rc
    ./system/etc/init/vold.rc
    ./root/init.rc
    ./root/init.zygote32.rc
    ./root/init.environ.rc
    ./root/ueventd.rc
    ./root/init.zygote64_32.rc
    ./root/init.preload.rc
    ./root/init.usb.configfs.rc
    ./root/init.usb.rc
    ```
    

    init.rc定位

    • init的源码位于system/core/init包下,我们先从入口类main.cpp来看
    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);   //ueventd.cpp入口函数,初始化uevent
        }
    
        if (argc > 1) {
            if (!strcmp(argv[1], "subcontext")) {
                android::base::InitLogging(argv, &android::base::KernelLogger);
                const BuiltinFunctionMap function_map;
    
                return SubcontextMain(argc, argv, &function_map);
            }
    
            if (!strcmp(argv[1], "selinux_setup")) {
                // This function initializes SELinux then execs init to run in the init SELinux context.
                return SetupSelinux(argv); //对SELinux进行初始化,并通过execs的系统调用开启init进程
            }
    
            if (!strcmp(argv[1], "second_stage")) {
                return SecondStageMain(argc, argv);  //第二阶段 init.cpp入口函数
            }
        }
    
        return FirstStageMain(argc, argv);   //第一阶段
    }
    

    复制代码可以看到main.cpp的函数跟之前版本有了很大的区别,拿Android9.0的源码androidxref.com/9.0.0_r3/xr…来说,Android10中并不只是调用init::main,而是把部分流程性的判断放到的mian.cpp中来做,所以这里如果按照书上或者文章中所说的,直接去找init.cpp中的main函数,其实是找不到入口的。

    当入口函数Init.cpp启动加载第一个init.rc如下:

    /system/core/rootdir/init.rc
    /bootable/recovery/etc/init.rc
    

    从目录上大致可以猜测,这两个init.rc使用场景不一样,一个是刷机用到的,也就是进入recorvery模式,一个是正常启动用到的;我们这里重点分析的是正常启动用的,也是init.c关联的那个;

    • device/mediatek/mt6765/device.mk
    7:  PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.vendor.rc=/vendor/etc/init/hw/
    
    919: PRODUCT_COPY_FILES += device/mediatek/mt6765/init.recovery.mt6765.rc:recovery-vendor/init.recovery.mt6765.rc
    
    • /system/core/rootdir/init.rc
    ···
    import /vendor/etc/init/hw/init.${ro.hardware}.rc
    ···
    
    • /device/mediatek/mt6765/init.mt6765.rc
    ···
    import /vendor/etc/init/hw/init.project.rc
    ···
    
    • device/mediateksample/k62v1_64_bsp/device.mk
    16:PRODUCT_COPY_FILES += $(LOCAL_PATH)/init.project.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw/init.project.rc
    

    根据以上内容,我们可以判断我们OEM/ODM厂商主要修改文件是device/mediateksample/k62v1_64_bsp/init.project.rc,而在recovery模式下主要修改init.recovery.mt6765.rc文件。

    init.rc修改规则

    rc规则主要包含了四种类型的语句:

    1. Action
    2. Commands
    3. Services
    4. Options.

    以下做具体详解:

    动作(Action)

    动作表示了一组命令(commands)组成.动作包括一个触发器,决定了何时运行这个动作。

    注意:当触发器的条件满足时,这个动作会被增加到已被运行的队列尾。假设此动作在队列中已经存在,那么它将不会运行.

    一个动作所包括的命令将被依次运行。

    on  <trigger>      ## 触发条件
        <command>      ##执行命令
        <command1>     ##可以执行多个命令
    
    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当属性值满足条件时触发

    commands(命令)

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

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

    常见命令:

    命令描写叙述
    exec [ ]* 运行指定路径下的程序,并传递參数.
    export 设置全局环境參数。此參数被设置后对全部进程都有效.
    ifup 使指定的网络接口"上线",相当激活指定的网络接口
    import 导入一个额外的init配置文件.
    hostname 设置主机名
    chdir 改变工作文件夹.
    chmod 改变指定文件的读取权限.
    chown 改变指定文件的拥有都和组名的属性.
    chroot 改变进行的根文件夹.
    class_start <serviceclass启动指定类属的全部服务,假设服务已经启动,则不再反复启动.
    class_stop 停止指定类属的所胡服务.
    domainname 设置域名
    insmod 安装模块到指定路径.
    mkdir [mode] [owner] [group] 用指定參数创建一个文件夹,在默认情况下,创建的文件夹读取权限为755.username为root,组名为root.
    mount [ ]* 类似于linux的mount指令setkey TBD(To Be Determined), 待定.
    setprop 设置属性及相应的值.
    setrlimit 设置资源的rlimit(资源限制),不懂就百度一下rlimit
    start 假设指定的服务未启动,则启动它.
    stop 假设指定的服务当前正在执行。则停止它.
    symlink 创建一个符号链接.
    sysclktz <mins_west_of_gmt>设置系统基准时间.
    trigger Trigger an event. Used to queue an action from another action. 这名话没有理解,望高手指点.
    write [ ]* 往指定的文件写字符串.

    服务(services)

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

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

    解释一下各个参数:

    参数含义
    表示此服务的名称
    此服务所在路径因为是可执行文件,所以一定有存储路径。
    启动服务所带的参数
    对此服务的约束选项

    选项(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当服务重启时执行一条指令,

    使用例子:

    service bootanim /system/bin/bootanimation
        class core  //给服务指定一个类属,这样方便操作多个服务同时启动或停止
        user graphics //在执行此服务之前先切换用户名
        group graphics audio
        disabled  //服务不会自动运行
        oneshot  //当此服务退出时不会自动重启
    

    启动顺序

    下面为各个section的执行顺序,英文编号的section是系统内建的(写死在init.cpp中的命令)

    1) early-init
    
        a) wait_for_coldboot_done
    
        b) property_init
    
        c) keychord_int
    
        d) console_init
    
    2) init
    
    3) early-fs (sys.boot_from_charger_mode=1)
    
    4) fs
    5) post-fs
    
    6) late-fs
    
    7) post-fs-data
    
    8) load_persist_props_action
    
    9) zygote-start
    
    10) firmware_mounts_complete
    
    11) early-boot
    
    12) boot
    
    13) service
    

    所有的action运行于service之前。

    展开全文
  • Cloud-init的认识和应用

    千次阅读 2020-05-12 15:06:59
    Cloud-init是什么 Cloud-init是开源的云初始化程序,能够对新创建弹性云服务器中指定的自定义信息(主机名、密钥和用户数据等)进行初始化配置。通过Cloud-init进行弹性云服务器的初始化配置,将对您使用弹性云...

    Cloud-init是什么

    Cloud-init是开源的云初始化程序,能够对新创建弹性云服务器中指定的自定义信息(主机名、密钥和用户数据等)进行初始化配置。通过Cloud-init进行弹性云服务器的初始化配置,将对您使用弹性云服务器、镜像服务和弹性伸缩产生影响。简单地讲,cloud-init是一个Linux虚拟机的初始化工具,被广泛应用在AWS和OpenStack等云平台中,用于在新建的虚拟机中进行时间设置、密码设置、扩展分区、安装软件包等初始化设置。

    官方讨论:http://cloudinit.readthedocs.io/en/latest/topics/examples.html

    对镜像服务的影响

    为了保证使用私有镜像新创建的弹性云服务器可以自定义配置,您需要在创建私有镜像前先安装Cloud-init/Cloudbase-init。

    如果是Windows操作系统,需下载并安装Cloudbase-init。

    如果是Linux操作系统,需下载并安装Cloud-init。

    在镜像上安装Cloud-init/Cloudbase-init后,即可在创建弹性云服务器时,按照用户的需要自动设置弹性云服务器的初始属性。

    对弹性云服务器的影响

    在创建弹性云服务器时,如果选择的镜像支持Cloud-init特性,此时,您可以通过系统提供的“用户数据注入”功能,注入初始化自定义信息(例如为弹性云服务器设置登录密码),完成弹性云服务器的初始化配置。

    支持Cloud-init特性后ZQ,弹性云服务器的登录方式会产生影响。

    对于运行中的的弹性云服务器,支持Cloud-init特性后,用户可以通过查询、使用元数据,对正在运行的弹性云服务器进行配置和管理。

    对弹性伸缩的影响

    创建伸缩配置时,您可以使用“用户数据注入”功能,指定弹性云服务器的初始化自定义信息。如果伸缩组使用了该伸缩配置,则伸缩组新创建的弹性云服务器会自动完成初始化配置。

    对于已有的伸缩配置,如果其私有镜像没有安装Cloud-init/Cloudbase-init,则使用该伸缩配置的伸缩组创建的弹性云服务器在登录时会受到影响。

    使用须知

    使用Cloud-init特性时,需开启弹性云服务器所在VPC中子网的DHCP。

    使用Cloud-init特性时,安全组出方向规则需满足如下要求:

    协议:TCP

    端口范围:80

    远端地址:169.254.0.0/16

    说明:

    如果您使用的是默认安全组出方向规则,则已经包括了如上要求,可以正常访问元数据。默认安全组出方向规则为:

    协议:ANY

    端口范围:ANY

    远端地址:0.0.0.0/0

    cloud-init简介及组件说明

    介绍:
        cloud-init是专为云环境中虚拟机的初始化而开发的工具,它从各种数据源读取相关数据并据此对虚拟机进行配置。
        向一台数据服务器获取元数据(meta data)和用户数据(user data),前者是指VM的必要信息,
        如主机名、网络地址等;后者是系统或用户需要的数据和文件,如用户组信息、启动脚本等。
        当cloud-init获取这些信息后,开始使用一些模块对数据进行处理,如新建用户、启动脚本等。常见的配置包括:设定虚拟机的hostname、hosts文件、设定用户名密码、更新apt -get的本地缓存、调整文件系统的大小(注意不是调整分区的大小)等。
    工作原理:
        首先,数据服务器开启HTTP服务,cloud-init会向数据服务器发送请求,确认数据源模块,依次获取版本、数据类型和具体数据内容信息。
        
      功能:
        用户可配置性
            可以通过用户数据配置Cloud-init的行为。
            用户数据可以给用户在实例启动时输出
            例如,这可以通过--user-data或--user-data-file参数到ec2-run-instances
            
        特征
        设置默认域
        设置一个实例的主机名
        生成实例SSH秘钥
        将SSH密钥添加到用户的.ssh / authorized_keys,以便他们可以登录
        设置临时挂载点
        配置网络设备

    可用性:
        它目前安装在Ubuntu Cloud Images中,还有在EC2,Azure,GCE和许多其他云端上提供的官方Ubuntu映像。
        支持诸多linux发行版本,ubuntu、fedora、debian、rhel、centos等等

    Gzip压缩:
        发现gzip压缩的内容将被解压缩。 然后将使用未压缩的数据,如同未压缩的数据一样。 
        这通常是有用的,因为用户数据被限制在〜16384 [1]个字节。

    用户数据:
        配置文件(Cloud Config Data),类型为Content-Type: text/cloud-config,系统配置文件,如管理用户等,与/etc/cloud下的cloud.cfg最后合并配置项
        启动任务(Upstart Job),类型为Content-Type: text/upstart-job,建立Upstart的服务
        用户数据脚本(User-Data Script),类型为Content-Type: text/x-shellscript,用户自定义的脚本,在启动时执行
        包含文件(Include File),类型为Content-Type: text/x-include-url,该文件内容是一个链接,这个链接的内容是一个文件, (Cloud Boothook),类型为Content-Type: text/cloud-boothook
        压缩内容( Gzip Compressed Content),
        处理句柄(Part Handler),类型为Content-Type: text/part-handler,内容为python脚本,根据用户数据文件的类型做相应的处理
        多部分存档(Mime Multi Part archive),当客户端需要下载多个上述用户数据文件时,可用Mime编码为Mime Multi Part archive一次下载

    目录结构:

    cloud-init 是 linux 的一个工具,当系统启动时,cloud-init 可从 nova metadata 服务或者 config drive 中获取 metadata,完成包括但不限于下面的定制化工作:

    • 设置默认语言环境
    • 设置实例主机名
    • 添加 ssh keys到 .ssh/authorized_keys
    • 设置用户密码
    • 配置网络安装软件包


    启动阶段:
        
        Generator
        Local
        Network
        Config
        Final

    为了实现 instance 定制工作,cloud-init 会按 4 个阶段执行任务:

    1. local
    2. init
    3. config
    4. final

    cloud-init 安装时会将这 4 个阶段执行的任务以服务的形式注册到系统中,比如在 systemd 的环境下,我们能够看到这4个阶段分别对应的服务:

    1. local - cloud-init-local.service
    2. init - cloud-init.service
    3. config - cloud-config.service
    4. final - cloud-final.service

    在这里插入图片描述

    当在systemd下引导时,将运行一个生成器,以确定cloud-init.target是否应包含在引导目标中。
    默认情况下,此生成器将启用cloud-init。 它不会启用cloud-init,如果:
        
        Generator:
            当在systemd下引导时,将运行一个生成器,以确定cloud-init.target是否应包含在引导目标中。 
            默认情况下,此生成器将启用cloud-init。 它不会启用cloud-init,如果:A file exists: /etc/cloud/cloud-init.disabled
             /proc/cmdline contains cloud-init=disabled
        
        local:
            cloud-init-local.service
            找到本地的数据源
            对系统应用及网络进行配置
            网络配置:
                数据源
                网卡dhcp
                禁用网络
        
        Network:
            cloud-init.service
            http
            解压gzip
            格式化磁盘、分区
            bootcmd
        
        Config:
            cloud-config.service
            配置模块modules
        Final:
            cloud-final.service
            启动最后阶段rc.local
            包的安装
            配置管理插件、chef、puppet
            用户脚本,runcmd

    数据源:
        数据源是cloud-init的配置数据源,通常来自用户(也称为用户数据)或来自创建配置驱动器(也称为元数据)的堆栈。
        型的用户数据将包括文件,yaml和shell脚本,而典型的元数据将包括服务器名称,实例ID,
        显示名称和其他云具体细节。 
        由于有多种方式来提供这些数据(每个云解决方案似乎都喜欢自己的方式)内部创建了一个数据源抽象类,
        以允许单一方式访问不同的云系统方法,以通过典型的子类使用提供此数据。
        
        支持的方式:
            Alt Cloud:
                RHEVm
                vSphere
            Azure
            CloudSigma
            CloudStack
            Config Drive
            Digital Ocean
            Amazon EC2
            MAAS
            NoCloud
            OpenNebula
            OVF
            SmartOS Datasource
            OpenStack:
                metadata_urls http://169.254.169.254返回200ok
                max_wait default 1
                timeout default 10
                retries default 5重试次数
            #cloud-config
                datasource:
                    OpenStack:
                    metadata_urls: ["http://169.254.169.254"]
                    max_wait: -1
                    timeout: 10
                    retries: 5
    Logging:
        支持本地和远程日志记录是通过 python 的内置-在日志记录配置并通过云 init rsyslog 模块可配置、
        默认cloud-init将其输出配置从/etc/cloud/cloud.cfg.d/05_logging.cfg加载
        默认配置将stdout和stderr从所有cloud-init阶段引导到/var/log/cloud-init-output.log

    Modules:
        Apt Configure配置ubuntu 源列表
        Bootcmd:在引导过程的早期运行命令,用于在引导过程中不能完成的
        Byobu:该模块控制是否启用或禁用系统范围以及默认系统用户
        CA Certs:添加CA证书
        Chef:安装chef client
        Debug:帮助调试内部数据结构
        Disable EC2 Metadata:默认禁用,该模块通过拒绝发送到169.254.169.254的路由,从而禁用ec2数据源。
        Disk Setup:配置分区和文件系统
        Emit Upstart:发出启动配置,不需要配置
        Fan:配置ubuntu网络
        Final Message:当cloud-init完成时输出最终消息
            version: cloud-init version版本
            timestamp: time at cloud-init finish 完成耗时
            datasource: cloud-init data source源数据
            uptime: system uptime系统运行时间
        Foo:示例显示模块结构,不做任何事情    
        Growpart:调整分区大小以填充可用磁盘空间
            growpart:
                mode: auto
                devices: ["/"]
                ignore_growroot_disabled: false
        Grub Dpkg:配置哪个设备用作grub安装的目标,默认情况下,此模块应正常工作无须配置。
        Keys to Console:控制哪些ssh密钥可以写入控制台
            出于安全考虑,可能不希望将ssh指纹和密钥写入控制台。 
            为了避免将ssh键的类型指纹写入控制台,可以使用ssh_fp_console_blacklist配置密钥。 
            默认情况下,所有类型的键都将其指纹写入控制台。 
            为了避免将密钥类型的密钥写入控制台,可以使用ssh_key_console_blacklist配置密钥。
            默认情况下,ssh-dss键不会写入控制台
        Landscape:
        Locale:配置系统区域设置并系统应用
        LXD:
        Mcollective:
        Migrator:该模块处理将旧版本的cloud-init数据移动到较新版本
        Mounts:配置挂载点和交换文件
        NTP:处理ntp配置
            ntp:
        pools:
            - 0.company.pool.ntp.org
            - 1.company.pool.ntp.org
            - ntp.myorg.org
        servers:
            - my.ntp.server.local
            - ntp.ubuntu.com
            - 192.168.23.2
        Package Update Upgrade Install:
            更新,升级和安装软件包
        此模块允许在引导期间更新,升级或安装软件包
         如果要安装任何软件包或执行升级,那么软件包缓存将首先更新。 
         如果软件包安装或升级需要重新启动,则如果指定了package_reboot_if_required,则可以执行重新启动。 
         可以提供要安装的软件包的列表。 
         列表中的每个条目可以是包名称或具有两个条目的列表,第一个是包名称,第二个是要安装的特定包版本。
        Phone Home:
            引导完成后,该模块可用于将数据发布到远程主机, 可以发布所有数据或发布的密钥列表。
        Power State Change:
            改变电源状态
            所有配置模块都运行后,该模块处理关机/重新启动。
        Puppet:
            安装、配置puppet
        Resizefs:调;整文件系统的大小以使用分区上的所有可用空间。
        Resolv Conf:
            manage_resolv_conf: <true/false>
        resolv_conf:
            nameservers: ['8.8.4.4', '8.8.8.8']
            searchdomains:
                - foo.example.com
                - bar.example.com
            domain: example.com
            options:
                rotate: <true/false>
                timeout: 1
            Debian / Ubuntu默认情况下会使用resovlconf,同样的RedHat也会使用sysconfig
        RedHat Subscription:
            通过用户名和密码或激活和组织注册一个RedHat系统
        Rsyslog:
            此模块使用rsyslog配置远程系统日志记录
        Runcmd:
            在类似级别的rc.local上运行任意命令,并输出到控制台
            所有的命令必须是正确的yaml,因此您必须引用yaml所符合的任何字符
            runcmd:
        - [ ls, -l, / ]
        - [ sh, -xc, "echo $(date) ': hello world!'" ]
        - [ sh, -c, echo "=========hello world'=========" ]
        - ls -l /root
        - [ wget, "http://example.org", -O, /tmp/index.html ]
        如果项目是一个列表,它将被正确执行,就好像传递给execve()(以第一个arg为命令)。 
        如果项目是一个字符串,它将被写入一个文件并使用sh进行解释
        Salt Minion:配置公钥和私钥
        Scripts Per Boot:运行启动脚本
            数据源中的scripts / per-boot目录中的任何脚本都将在系统引导时运行。
        Scripts Per Instance:实例运行脚本,系统引导时运行
        Scripts Per Once:运行一次脚本
        Scripts User:运行用户脚本
            该模块运行所有用户脚本。 
            用户脚本未在数据源中的scripts目录中指定,而是存在于实例配置中的脚本目录中。
        Scripts Vendor:数据源中脚本/供应商目录中的任何脚本将在首次引导新实例时运行。
        Seed Random:生成随机秘钥
        Set Hostname:设置主机名和fqdn
        Set Passwords:设置系统密码,启用或禁用ssh密码认证。
            chpasswd配置密钥接受一个包含两个密钥中的一个的字典,即过期或列表。 
            如果expire被指定并设置为false,那么将使用password全局配置密钥作为所有用户帐户的密码。 
            如果指定了过期密钥,并设置为true,则用户密码将过期,从而防止使用默认系统密码。
            ssh_pwauth: <yes/no/unchanged>

            password: password1
            chpasswd:
            expire: <true/false>

            chpasswd:
            list: |
                user1:password1
                user2:RANDOM
                user3:password3
                user4:R

            ##
            # or as yaml list
            ##
            chpasswd:
                list:
                - user1:password1
                - user2:RANDOM
                - user3:password3
                - user4:R
                - user4:$6$rL..$ej...
        SSH:该模块处理ssh和ssh密钥的大部分配置
        SSH Authkey Fingerprints:为每个用户写入授权密钥的指纹记录日志
        SSH Import Id:此模块通过公钥钥匙服务器(通常是启动板或github)使用ssh-import-id导入ssh密钥
        Timezone:设置系统时区
            timezone: <timezone>
        Update Etc Hosts:更新/etc/hosts
            该模块将根据config中指定的主机名/ fqdn更新/ etc / hosts的内容。 
            / etc / hosts的管理使用manage_etc_hosts进行控制。 
            如果设置为false,则cloud-init将不会管理/ etc / hosts
            如果设置为true或template,cloud-init将使用位于/etc/cloud/templates/hosts.tmpl中的模板生成/ etc / hosts。 
            如果manage_etc_hosts设置为localhost,那么cloud-init将不会完全重写/ etc / hosts,而是确保/ etc / hosts中存在具有ip 127.0.1.1的fqdn的条目
            manage_etc_hosts: <true/"template"/false/"localhost">
            fqdn: <fqdn>
            hostname: <fqdn/hostname>
        Update Hostname:该模块将更新系统主机名和fqdn。
            preserve_hostname: <true/false>
            fqdn: <fqdn>
            hostname: <fqdn/hostname>
        Users and Groups:配置用户和组
                groups:
        - <group>: [<user>, <user>]
        - <group>

        users:
            - default
            - name: <username>
            expiredate: <date>
            gecos: <comment>
            groups: <additional groups>
            homedir: <home directory>
            inactive: <true/false>
            lock_passwd: <true/false>
            no-create-home: <true/false>
            no-log-init: <true/false>
            no-user-group: <true/false>
            passwd: <password>
            primary-group: <primary group>
            selinux-user: <selinux username>
            shell: <shell path>
            snapuser: <email>
            ssh-authorized-keys:
                - <key>
                - <key>
            ssh-import-id: <id>
            sudo: <sudo config>
            system: <true/false>
            uid: <user id>
        Write Files:
            将任意内容写入文件,可选择设置权限。 内容可以用纯文本或二进制格式指定。 
            可以指定使用base64或二进制gzip数据编码的数据,并在写入之前对其进行解码。
                write_files:
            - encoding: b64
            content: CiMgVGhpcyBmaWxlIGNvbnRyb2xzIHRoZSBzdGF0ZSBvZiBTRUxpbnV4...
            owner: root:root
            path: /etc/sysconfig/selinux
            permissions: '0644'
            - content: |
            # My new /etc/sysconfig/samba file

            SMDBOPTIONS="-D"
            path: /etc/sysconfig/samba
            - content: !!binary |
            f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAwARAAAAAAABAAAAAAAAAAJAVAAAAAA
            AEAAHgAdAAYAAAAFAAAAQAAAAAAAAABAAEAAAAAAAEAAQAAAAAAAwAEAAAAAAA
            AAAAAAAAAwAAAAQAAAAAAgAAAAAAAAACQAAAAAAAAAJAAAAAAAAcAAAAAAAAAB
            ...
            path: /bin/arch
            permissions: '0555'
        Yum Add Repo:
            将yum存储库配置添加到/etc/yum.repos.d。
            yum_repos:
                <repo-name>:
                    baseurl: <repo url>
                    name: <repo name>
                    enabled: <true/false>

    在本地KVM中使用cloud-init

        本文主要研究在本地KVM虚拟机,也就是没有云平台的情况下,如何使用cloud-init进行初始化工作。示例虚拟机的操作系统是centos7.1。

    安装

    在虚拟机内安装cloud-init:

    yum install cloud-init -y

     

    安装完后,配置文件在/etc/cloud目录,主配置文件为/etc/cloud/cloud.cfg

    DataSource

    要让cloud-init能够顺利完成初始化工作,必须把一些数据传给cloud-init,例如让cloud-init设置root密码,必须要告诉cloud-init密码是什么。Cloud-init支持很多种数据来源,常见的有metadata service、config drive和nocloud等。

    l  metadata service提供一个可以获取数据的url,例如OpenStack中的nova-api-metadat提供的http://169.254.169.254,虚拟机开机后cloud-init在完成网络配置后,会向这个url发起请求。因此这种模式需要先配置好网络;

    l  Config drive 把数据封装进一个iso9660(也支持vfat,但是不常见)文件系统的镜像中,然后把这个镜像以光驱(ide接口)的形式挂载到虚拟机中,虚拟机开机后cloud-init会自动去该镜像中获取数据。文件结构一般如下:

    openstack/

      -   2012-08-10/ or latest/

          - meta_data.json

          - user_data (not mandatory)

      -   content/

          - 0000 (referenced content files)

          - 0001

          - ....

    ec2

      -   latest/

          - meta-data.json (not mandatory)

     

    l  Nocloud 这种模式与config drive类似,只是文件结构不同,一般由user-data和meta-data两个文本文件构成。并且镜像以普通磁盘(virtio接口)的方式挂载。

    本文主要试验nocloud模式。

    封装数据

    制作镜像需要cloud-localsd命令,默认没有安装,安装命令如下:

    yum install cloud-utils -y

    编写user-data:

    cat << EOF > my-user-data

    #cloud-config

    chpasswd:

    list: |

    root:123456

    expire: false

    ssh_pwauth: true

    EOF

    cloud-init也支持多种数据格式,我们使用的是cloud-config格式,这种格式必须是以#cloud-config开头的yaml格式。上面这段的意思是把root的密码设置为“123456”,然后配置ssh允许密码登录。

    制作img:

    cloud-localds -m local my-seed.img   my-user-data

    -m指定的cloud-init的工作模式,local的意思是不需要依赖网络,我们没有使用metadata service,所以不需要网络。

    挂载数据

    编辑虚拟机的libvirt xml配置文件,挂载镜像:

    <disk type='file' device='disk'>

          <driver name='qemu' type='raw' cache='none' io='native'/>

          <source file='/path/to/my-seed.img'/>

          <target dev='vdb' bus='virtio'/>

          <readonly/>

    </disk>

    修改cloud-init配置

    在/etc/cloud/cloud.cfg最后一行添加以下内容,这句的意思是让cloud-init接受NoCloud来源的数据:

    datasource_list: ["NoCloud"]

    重启虚拟机

    在虚拟机内执行rm -rf /var/lib/cloud,不删除这个目录,cloud-init不会执行。

    硬重启虚拟机

    virsh destroy vm

    virsh start vm

    虚拟机开机后,会发现root密码变成了“123456”

    查看数据

    在虚拟机内mount /dev/vdb /mnt,可以查看数据的内容。

    ls /mnt

    meta-data user-data

    cat user-data

    #cloud-config

    chpasswd:

    list: |

    root:123456

    expire: false

    ssh_pwauth: true

    总结

    本文测试了在无云平台管理的本地KVM虚拟机上使用cloud-init修改root密码的功能。cloud-init还有很多功能和技术细节,后续会继续研究。

    更多应用参考

    1、cloud-init的使用(cloud-init 的模块众多,功能很全,是实现 instance 定制的神器) -  https://www.jianshu.com/p/f4fa583f022a

    2、proxmox里使用cloud-init和一些笔记 - 云途主机 https://www.waimaospace.com/reed/414.html

    展开全文
  • Android 10.0系统启动之init进程-[Android取经之路]

    万次阅读 多人点赞 2019-12-15 16:12:11
    摘要:init进程是linux系统中用户空间的第一个进程,进程号为1.当bootloader启动后,启动kernel,kernel启动完后,在用户空间启动init进程,再通过init进程,来读取init.rc中的相关配置,从而来启动其他相关进程以及...
  • rosdep init/update 解决方法

    千次阅读 多人点赞 2021-03-14 18:22:32
    rosdep init/update 解决方法 最近在国内上github越来越难,我自己的校园网曾经还是很好使的,但是最近也上不去了,加上不想花钱在ubuntu上买代理,所以在很多需要上git的场景很头疼。包括今天写的这个ros 安装过程...
  • Android 9.0 init 启动流程

    千次阅读 2019-11-18 18:40:00
    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK")); // Set libavb version for Framework-only OTA match in Treble build. const char* avb_version = getenv("INIT_AVB_VERSION"); if...
  • init命令详解

    千次阅读 2018-11-03 14:35:49
    init是Linux系统操作中不可缺少的程序之一。所谓的init进程,它是一个由内核启动的用户级进程。内核会在过去曾使用过init的几个地方查找它,它的正确位置(对Linux系统来说)是/sbin/init。如果内核找不到init,它就...
  • 在bean的xml定义中指定init-method属性。 2)注解配置 在bean的class定义中添加@PostConstruct注解。 例子: xml如下配置: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http:...
  • linux 中module_init()加载顺序

    千次阅读 2018-12-24 16:18:50
    目录 ...kernel-3.18\include\linux\init.h中   Linux内核为不同驱动的加载顺序对应不同的优先级,定义了一些宏: #define pure_initcall(fn) __define_initcall("0",fn,1) ...
  • 安装Cloud-Init工具

    千次阅读 2019-05-24 20:18:52
    为了保证使用私有镜像创建的新云服务器可以自定义配置(例如修改云服务器密码),请在创建私有镜像前安装Cloud-Init工具。 • Linux操作系统安装Cloud-Init工具时需要从官网下载并安装,因此,需要提前给云服务器...
  • 嵌入式linux init系统(busybox init

    千次阅读 2016-05-28 15:53:50
    (一)System V init 首先我们来讨论PC上运行的Linux系统,它启动后从BIOS开始,进入bootloader,有bootloader加载内核,进入内核初始化。内核启动的最后一步就是创建一个pid为1的init进程。这是系统的第一个进程,...
  • K8S中Pod的生命周期与init container初始化容器详解
  • 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文
  • 文章目录cloud-init安装方式1. 下载cloud-init2. 安装相应的包3. 配置Cloud-Init工具4. 上传镜像到openstackOpenstack的config-drive机制 在云平台中,创建云主机的时候希望能够对主机进行一些初始化操作,如配置ip...
  • npm init

    千次阅读 2020-06-29 12:18:28
    1、为什么要使用npm init初始化项目 在node开发中使用npm init会生成一个pakeage.json文件,这个文件主要是用来记录这个项目的详细信息的,它会将我们在项目开发中所要用到的包,以及项目的详细信息等记录在这个项目...
  • 另外,需要提醒大家注意的一点是,阅读本章需要有一定的 C/C++基础,在这个流程中会涉及到很多重要的知识点,这个系列我们就来一一讲解它们,这一篇我们就来学习init进程,本系列文章都将基于Android8.0源码来分析...
  • 1. InitializingBean、initMethod和@PostConstruct的作用 2. initMethod和InitializingBean 2.1 从initMethod说起 2.2 从一个栗子来看initMethod和InitializingBean 2.3 探秘initMethod和InitializingBean在spring...
  • linux启动第一个应用程序init linux的运行顺序为uboot传递参数到内核,内核的第一个c编写的函数为start_kernel(),start_kernel来启动内核,最后到到rest_init()函数处完成内核启动过程。 rest_init()中启动第一个...
  • linux 驱动module_init()本质

    千次阅读 2019-06-17 15:15:33
    linux 驱动module_init()本质--->不同驱动加载顺序对应不同的优先级 阶段一: kernel-3.18\include\linux\init.h中 Linux内核为不同驱动的加载顺序对应不同的优先级,定义了一些宏: #define pure_...
  • Linux init详解

    千次阅读 2019-05-30 20:25:22
    一、init模块 一般来说,Linux程序只能用另一个Linux程序启动。例如,登录Linux终端程序Mingetty。   但终端程序又由谁启动呢?在计算机上启动Linux时,内核装入并启动init程序。然后init程序装载硬盘和启动终端...
  • Android系统启动流程(一)解析init进程启动过程

    万次阅读 多人点赞 2017-02-07 11:29:48
    作为“Android框架层”这个大系列中的第一个系列,我们首先要了解的是Android系统启动流程,在这个流程中会涉及到很多重要的知识点,这个系列我们就来一一讲解它们,这一篇我们就来学习init进程。
  • 快速理解initContainer概念、用法、使用场景一、理解 Init 容器二、Init 容器能做什么【使用场景】三、话不多说,来实操 Init 容器1.用docker启用个nginx将war包放在上面2.k8s中创建initContainers和containers四、...
  • module_init机制的理解

    千次阅读 2019-01-23 10:53:02
     所以这里的意思就是:定义一个名为 __initcall_hello_init6 的函数指针变量,并初始化为 hello_init(指向hello_init);并且该函数指针变量存放于 .initcall6.init 代码段中。  接下来,我们通过查看链接脚本...
  • Android P (9.0) 之Init进程源码分析

    千次阅读 2018-12-03 17:19:28
    众所周知,init进程是Android系统的第一个用户进程,Android启动流程大致如下: 今天我们来分析一下用户空间的第一个进程init进程。 init进程主要提供以下几个功能: 挂载文件系统、生成部分设备节点、创建目录 ...
  • Linux init进程详解

    千次阅读 2018-04-05 13:14:32
    一、什么是INIT: init是Linux系统操作中不可缺少的程序之一。 所谓的init进程,它是一个由内核启动的用户级进程。 内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就...
  • 相信大家在很多场合特别是写神经网络的代码的时候都看到过下面的这种代码: ... def __init__(self): super(Net, self).__init__() # 输入图像channel:1;输出channel:6;5x5卷积核 self.conv1 = nn.C
  • python中super().__init__()

    万次阅读 多人点赞 2020-03-01 21:55:12
    super().__init__() 1、子类构造函数调用super().__init__()1.1、第一个直接继承父类,可以调用name1.2、第二个继承自父类,覆盖初始化化def init,增加属性age,不能调用name属性1.3、第三个继承自父类,覆盖...
  • linux kernel_init

    千次阅读 2017-12-10 23:45:32
    前言:   内核在启动用户空间程序时会创建两个线程,kthread() 和 kernel_init()线程,在前一篇介绍了kthread()线程 点击打开链接,本文不在赘述,这里主要是对kernel_init...路径:linux-3.10.x\init\main.c ...
  • 安装Cloudbase-Init工具

    千次阅读 2019-06-02 11:39:32
    为了保证使用私有镜像创建的新云服务器可以自定义配置(例如修改云服务器密码),建议您在创建私有镜像前安装Cloudbase-init工具。 不安装Cloudbase-init工具,将无法对云服务器进行自定义配置,只能使用镜像原有...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,288,555
精华内容 1,315,422
关键字:

init