android init进程启动流程

news2024/11/23 22:52:20


 

Android系统完整的启动流程

android 系统架构图

init进程的启动流程

init进程启动服务的顺序

bool Service::Start() {
    // Starting a service removes it from the disabled or reset state and
    // immediately takes it out of the restarting state if it was in there.
    flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));

    // Running processes require no additional work --- if they're in the
    // process of exiting, we've ensured that they will immediately restart
    // on exit, unless they are ONESHOT.
    if (flags_ & SVC_RUNNING) {
        return false;
    }

    bool needs_console = (flags_ & SVC_CONSOLE);
    if (needs_console) {
        if (console_.empty()) {
            console_ = default_console;
        }

        // Make sure that open call succeeds to ensure a console driver is
        // properly registered for the device node
        int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
        if (console_fd < 0) {
            PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
            flags_ |= SVC_DISABLED;
            return false;
        }
        close(console_fd);
    }

    struct stat sb;
    if (stat(args_[0].c_str(), &sb) == -1) {
        PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
        flags_ |= SVC_DISABLED;
        return false;
    }

    std::string scon;
    if (!seclabel_.empty()) {
        scon = seclabel_;
    } else {
        scon = ComputeContextFromExecutable(name_, args_[0]);
        if (scon == "") {
            return false;
        }
    }

    LOG(INFO) << "starting service '" << name_ << "'...";

    pid_t pid = -1;
    if (namespace_flags_) {
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
        pid = fork();
    }

    if (pid == 0) {
        umask(077);

        if (namespace_flags_ & CLONE_NEWPID) {
            // This will fork again to run an init process inside the PID
            // namespace.
            SetUpPidNamespace(name_);
        }

        for (const auto& ei : envvars_) {
            add_environment(ei.name.c_str(), ei.value.c_str());
        }

        std::for_each(descriptors_.begin(), descriptors_.end(),
                      std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));

        // See if there were "writepid" instructions to write to files under /dev/cpuset/.
        auto cpuset_predicate = [](const std::string& path) {
            return StartsWith(path, "/dev/cpuset/");
        };
        auto iter = std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate);
        if (iter == writepid_files_.end()) {
            // There were no "writepid" instructions for cpusets, check if the system default
            // cpuset is specified to be used for the process.
            std::string default_cpuset = GetProperty("ro.cpuset.default", "");
            if (!default_cpuset.empty()) {
                // Make sure the cpuset name starts and ends with '/'.
                // A single '/' means the 'root' cpuset.
                if (default_cpuset.front() != '/') {
                    default_cpuset.insert(0, 1, '/');
                }
                if (default_cpuset.back() != '/') {
                    default_cpuset.push_back('/');
                }
                writepid_files_.push_back(
                    StringPrintf("/dev/cpuset%stasks", default_cpuset.c_str()));
            }
        }
        std::string pid_str = std::to_string(getpid());
        for (const auto& file : writepid_files_) {
            if (!WriteStringToFile(pid_str, file)) {
                PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
            }
        }

        if (ioprio_class_ != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
                PLOG(ERROR) << "failed to set pid " << getpid()
                            << " ioprio=" << ioprio_class_ << "," << ioprio_pri_;
            }
        }

        if (needs_console) {
            setsid();
            OpenConsole();
        } else {
            ZapStdio();
        }

        // As requested, set our gid, supplemental gids, uid, context, and
        // priority. Aborts on failure.
        SetProcessAttributes();

        if (!ExpandArgsAndExecve(args_)) {
            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
        }

        _exit(127);
    }

这段代码最后调用_exit(127)的原因:

当一个进程调用fork()创建一个子进程时,子进程会继承父进程的内存映像,包括代码段、数据段、堆栈等。子进程会在fork()调用的位置开始执行,并且会复制父进程的文件描述符。在fork()之后,子进程通常会调用execve()来加载一个新的程序映像,取代原来的父进程映像。如果execve()执行成功,子进程会开始执行新程序的代码,而原来的父进程则继续执行下去。

在这段代码中,当子进程执行if (!ExpandArgsAndExecve(args_))时,它试图调用execve()加载一个新程序,如果加载失败(例如,找不到要执行的程序),那么子进程将无法继续执行。这时候,子进程就没有必要再继续执行下去,因为它无法完成它的任务。所以,为了避免子进程继续运行下去造成不必要的资源浪费,程序员选择在这种情况下使用_exit(127)来终止子进程。

为什么是_exit(127)呢?这是一个惯例,Unix系统的shell约定,当一个命令找不到时,会返回状态码127。这种约定让父进程能够根据子进程的返回状态码来判断子进程的执行情况。所以,在这里使用_exit(127)是为了向父进程传达“找不到要执行的程序”的信息。

如果调用execve()成功了,意味着新程序已经被加载并且开始执行了,那么当前进程的代码段、数据段等都已经被替换成了新程序的内容,所以当前进程不再执行原来的代码,而是转而执行新程序的代码。因此,_exit(127)之后的代码将不会被执行到,包括_exit(127)本身。

在这段代码中,_exit(127)被视为一种防御措施,用于处理execve()调用失败的情况。一旦execve()成功执行,子进程就不会继续执行后续的代码,而是会执行新程序的代码

举例:

#include <iostream>
#include <unistd.h>

int main() {
    pid_t pid = fork();

    if (pid == -1) {
        // 错误处理
        std::cerr << "fork() failed" << std::endl;
        return 1;
    } else if (pid == 0) {
        // 在子进程中
        std::cout << "Child process: fork() returned " << pid << std::endl;
    } else {
        // 在父进程中
        std::cout << "Parent process: fork() returned " << pid << std::endl;
    }

    return 0;
}

执行以后输出结果:

默认情况下fork以后,

在Linux中,默认情况下,父进程不会一直等待子进程执行完毕才结束自己。父进程和子进程是并发执行的,父进程会继续执行自己的任务,不会阻塞等待子进程的执行。

当父进程结束时,它的子进程可能还在运行,这时子进程的状态会被转移给 init 进程(进程号为1),这样子进程就不会成为孤儿进程。

但是,如果父进程希望等待子进程结束,可以使用 wait()waitpid() 系统调用来实现。这些调用可以使父进程阻塞,直到它的一个或多个子进程结束。

所以,要使父进程在子进程执行完毕后再结束自己,需要显式地调用 wait()waitpid() 函数。

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    std::cout << "Parent process: Starting..." << std::endl;

    pid_t pid = fork();

    if (pid == -1) {
        std::cerr << "fork() failed" << std::endl;
        return 1;
    } else if (pid == 0) {
        // 子进程
        std::cout << "Child process: Starting..." << std::endl;
        sleep(3); // 模拟子进程执行一些任务
        std::cout << "Child process: Finished" << std::endl;
        return 0;
    } else {
        // 父进程
        std::cout << "Parent process: Waiting for child process to finish..." << std::endl;
        int status;
        waitpid(pid, &status, 0); // 等待子进程结束
        if (WIFEXITED(status)) {
            std::cout << "Parent process: Child process exited with status: " << WEXITSTATUS(status) << std::endl;
        } else {
            std::cerr << "Parent process: Child process terminated abnormally" << std::endl;
        }
        std::cout << "Parent process: Finished" << std::endl;
    }

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1640360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Leetcode每日一题】 动态规划 - 简单多状态 dp 问题 - 删除并获得点数(难度⭐⭐)(70)

1. 题目解析 题目链接&#xff1a;740. 删除并获得点数 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 问题分析 本题是「打家劫舍」问题的变种&#xff0c;但核心逻辑依然保持一致。题目要求从给定的数组nums中选择…

【 书生·浦语大模型实战营】作业(七):大模型实战评测

【 书生浦语大模型实战营】作业&#xff08;七&#xff09;&#xff1a;大模型实战评测 &#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系的学…

「2024年」前端开发常用工具函数总结 TypeScript

前言 在前端开发中&#xff0c;工具函数是提高代码复用率、保持代码整洁和增加开发效率的关键。使用 TypeScript 编写工具函数不仅可以帮助开发者捕捉到更多的类型错误&#xff0c;还可以提供更清晰的代码注释和更智能的代码补全。下面是一些在 TypeScript 中常用的前端开发工…

【iOS】方法交换(Method Swizzling)

文章目录 前言一、原理与注意用法注意要点Method Swizzing涉及的相关API 二、应用场景与实践1.统计VC加载次数并打印2.防止UI控件短时间多次激活事件3.防崩溃处理&#xff1a;数组越界问题4.防KVO崩溃 总结 前言 上文讲到了iOS的消息发送机制&#xff0c;在消息机制中我们了解…

【打工日常】云原生之使用docker部署Web在线流程图软件

一、drawio介绍 1.drawio简介 draw.io是一款免费、开源、高质量的WEB在线流程图软件&#xff0c;无需注册登录&#xff0c;支持多种图表类型和元素&#xff0c;可在线编辑和导出。它是一个可配置的图表/白板可视化应用程序。该应用程序的设计主要是按原样使用。draw.io不适合作…

AI诗歌创作

诗歌作为一种文学形式&#xff0c;能够通过优美的语言和深刻的意境表达情感和思想&#xff0c;触动人心&#xff0c;引发共鸣。然而&#xff0c;如今随着生活节奏的加快和人们对实用性的追求&#xff0c;写诗这一传统艺术渐渐被人们所忽略。幸运的是&#xff0c;随着人工智能技…

【C++】双指针算法:四数之和

1.题目 2.算法思路 这道题目十分困难&#xff0c;在leetcode上的通过率只有36%&#xff0c;大家要做好心理准备。 在做个题目前强烈建议大家先看看我的上一篇博客&#xff1a;有效三角形个数&#xff0c;看完之后再去leetcode上写一写三数之和&#xff0c;搞懂那两个题目之后…

基于STM32的最小系统电路设计(STM32F103C8T6为例)

前言&#xff1a;本篇博客为嵌入式硬件领域的文章&#xff0c;对 STM32 的最小系统电路设计进行教学。本篇博客以嘉立创 EDA&#xff08;标准版&#xff09;进行绘制 STM32F103C8T6 的最小系统电路 PCB 板&#xff0c;STM32 的最小系统通常包括&#xff1a;微控制器、时钟电路、…

[Power Automate] 关联Teams,发起通知并进行审批

1、 workflow 创建一条teams chat,定义teams members 和 chat title 2、添加通知内容到刚刚创建的chat 里 3、发起teams 审批流程&#xff08;通过这个action&#xff0c;进行teams内部的审批&#xff09; 4、审批结果判断&#xff0c;在这个基础上进行审批之后的操作&#xff…

Mamba3D革新3D点云分析:超越Transformer,提升本地特征提取效率与性能!

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; Mamba3D革新3D点云分析&#xff1a;超越Transformer&#xff0c;提升本地特征提取效率与性能&#xff01; 引言&#xff1a;3D点云分析的重要性与挑战 3D点云…

vscode设置免密登录远程服务器

文章目录 1. 问题描述2. 解决方案3. 原理 1. 问题描述 当我们使用vscode的ssh连接远程服务器后&#xff0c;过一段时间后&#xff0c;总是要求登录服务器的密码。 这就导致一个麻烦就是: 无论是在公司还是在学校&#xff0c;密码往往不是自己设置的&#xff0c;所以记忆起来就…

SQL事前巡检插件

背景: 事故频发 •在工作过程中每年都会看到SQL问题引发的线上问题&#xff0c;一条有问题的SQL足以拖垮整个数据库 不易发觉 •对于SQL性能问题测试在预发环境不易发现&#xff08;数据量小&#xff09; •SAAS系统隔离字段在SQL条件中遗漏&#xff0c;造成越权风险 •业…

navicat连接MySQL时1045报错

当登录MySQL数据库出现 Error 1045 错误时&#xff0c;表明你输入的用户名或密码错误被拒绝访问了&#xff1b;MySQL服务搭建后&#xff0c;默认root用户是不能进行远程访问连接的。 无法连接的可能有&#xff1a; 数据库没开启——开启数据库 用户名或密码错误——更改密码 连…

【探索】文字游侠AI新时代,每天5分钟自动化创作图文月入1万+,十分适合新手小白,附上渠道和教程(全面)

在这个信息爆炸的时代&#xff0c;内容创作者面临着空前的竞争。为了在今日头条这样的平台上脱颖而出并获取稳定收入&#xff0c;他们需要找到更高效、更创新的方法。而今&#xff0c;一款全新的AI工具正引领着一场革命&#xff0c;彻底改变了内容创作的生态。 自从GPT问世以来…

PZK via OWF

参考文献&#xff1a; [SMP88] Santis A, Micali S, Persiano G. Non-Interactive Zero-Knowledge with Preprocessing[C]//Advances in Cryptology—CRYPTO’88.[LS90] Lapidot D, Shamir A. Publicly verifiable non-interactive zero-knowledge proofs[C]//Advances in Cry…

C++ stack和queue的使用方法与模拟实现

文章目录 一、 stack的使用方法二、 queue的使用方法三、 容器适配器四、 stack的模拟实现五、 queue的模拟实现 一、 stack的使用方法 stack介绍文档 stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的…

8 聚类算法

目录 0 背景 1 Kmeans 1.1 聚类数量k的确定 2 DBSCAN 2.1 三个点 2.2 算法流程 3 层次聚类 3.1 过程 4 基于分布的聚类:高斯混合模型 0 背景 聚类算法是一种无监督学习技术&#xff0c;用于将数据集中的数据点划分为不同的组或簇&#xff0c;使得同一组内的数据点彼此相…

线性卷积和圆周卷积

文章目录 【 1. 线性卷积 】1.1 图解分析1.2 矩阵相乘实现线性卷积1.3 圆周卷积实现线性卷积1.4 实例:线性卷积的两种实现方法【 2. 圆周卷积 】2.1 图解分析2.2 矩阵相乘实现圆周卷积2.3 频域点乘实现圆周卷积2.4 实例:圆周卷积两种实现方法【 3. 线性卷积和圆周卷积的等价 …

算法导论 总结索引 | 第三部分 第十二章:二叉搜索树

1、搜索树数据结构 支持 许多动态集合操作&#xff0c;包括 SEARCH、MINIMUM、MAXIMUM、PREDECESSOR、SUCCESSOR、INSERT 和 DELETE 等。使用搜索树 既可以作为一个字典 又可以作为一个优先队列 2、二叉搜索树上的基本操作 所花费的时间 与这棵树的高度成正比。对于有n个结点的…

发卡盗u源码系统搭建ZHU

2024最新UI发卡盗U/支持多语言/更新UI界面/支持多个主流钱包去除后门板&#xff0c;最好是部署智能合约后用合约地址来授权包含转账支付页面盗U授权源码。 完美提U&#xff0c;教程包含如何提u 。功能完美。 1.Php静态 2.目录puicta 3.扩sal 4.ssl不强https