android init进程启动流程


 

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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每天五分钟深度学习框架pytorch:如何创建多维Tensor张量元素?

本文重点 上节课程我们学习了如何创建Tensor标量,我们使用torch.tensor。本节课程我们学习如何创建Tensor向量,我们即可以使用torch.Tensor又可以使用torch.tensor,下面我们看一下二者的共同点和不同点。 Tensor张量 tensor张量是一个多维数组,零维就是一个点(就是上一…

llama-factory/peft微调千问1.5-7b-chat

目标 使用COIG-CQIA数据集和通用sft数据集对qwen1.5-7b-chat进行sft微调,使用公开dpo数据集进行dpo对齐。学习千问的长度外推方法。 一、训练配置 使用Lora方式, 将lora改为full即可使用全量微调。 具体的参数在 该框架将各个参数、训练配置都封装好了,直接使用脚本,将数…

毫米波雷达多人呼吸心跳检测MATLAB仿真

本文基于TI的IWR1642毫米波雷达 2T4R MIMO阵列&#xff0c;通过实际采集数据算法仿真&#xff0c;实现多人呼吸心跳检测。 文章末尾给出了本文的仿真代码。 主要内容包含&#xff1a; &#xff08;1&#xff09;雷达参数设定 &#xff08;2&#xff09;ADC数据导入 &#xff08…

Windows Server 安全策略配置

前言 Windows Server是由微软开发的一种操作系统&#xff0c;主要用于在企业或机构的服务器上运行。它提供了一系列的功能和工具&#xff0c;旨在提高服务器的性能、可靠性、安全性和管理性。 特点 强大的性能&#xff1a;Windows Server具有高度优化的内核和资源管理&#x…

【MySQL | 第十篇】重新认识MySQL索引匹配过程

文章目录 10.重新认识MySQL索引匹配过程10.1匹配规则10.2举例&#xff1a;联合索引遇到范围查询&#xff08;>、<、between、like&#xff09;10.2.1例子一&#xff1a;>10.2.2例子二&#xff1a;>10.2.3例子三&#xff1a;between10.2.4例子四&#xff1a;like 10…

SQL数据库

一.什么是数据库 数据库&#xff1a;存储数据的仓库&#xff0c;数据是有组织的进行存储。&#xff08;database 简称DB&#xff09; 数据库管理系统&#xff1a;管理数据库的大型软禁&#xff08;DataBase Management System 简称DBMS&#xff09; SQL&#xff1a;操作关系…

Deep Learning Part Seven基于RNN生成文本--24.5.2

不存在什么完美的文章&#xff0c;就好像没有完美的绝望。 ——村上春树《且听风吟》 本章所学的内容 0.引子 本章主要利用LSTM实现几个有趣的应用&#xff1a; 先剧透一下&#xff1a;是AI聊天软件&#xff08;现在做的ChatGPT&#xff08;聊天神器&#xff0c;水论文高手…

Windows Server安装DHCP和DNS

前言 本期将教大家如何在Windows server上部署DHCP服务和DNS服务&#xff0c;用于模拟给内网主机分配IP地址。虽然用于演示的系统比较老&#xff0c;如果在新版本如Windows server2016、19、22上部署&#xff0c;操作基本一致。在此之前先给大家科普一波理论&#xff0c;需略过…

【docker 】push 镜像到私服

查看镜像 docker images把这个hello-world 推送到私服 docker push hello-world:latest 报错了。不能推送。需要标记镜像 标记Docker镜像 docker tag hello-world:latest 192.168.2.1:5000/hello-world:latest 将Docker镜像推送到私服 docker push 192.168.2.1:5000/hello…

Django数据库创建存储及管理

一、什么是ORM Django的ORM(Object-Relational Mapping)是Django框架中一个非常重要的组件。ORM可以让开发者以面向对象的方式操作数据库,而不需要直接编写SQL语句。 具体来说,Django ORM提供了以下功能: 模型定义:开发者可以在Django应用中定义Python类来表示数据库表,这些…

基于寄存器的STM32操作流程

寄存器点灯 寄存器操作STM32的好处是不需要依靠外部文件&#xff0c;自由度更高&#xff0c;更为底层&#xff0c;但也更加繁杂。 通过寄存器点灯&#xff0c;需要按照电路结构与手册配置寄存器&#xff1a; 电路结构如下&#xff1a;可知需配置的GPIO为GPIOB5与GPIOE5。 在…

Docker构建LNMP部署WordPress

前言 使用容器化技术如 Docker 可以极大地简化应用程序的部署和管理过程&#xff0c;本文将介绍如何利用 Docker 构建 LNMP 环境&#xff0c;并通过部署 WordPress 来展示这一过程。 目录 一、环境准备 1. 项目需求 2. 安装包下载 3. 服务器环境 4. 规划工作目录 5. 创…

excel怎么删除条件格式规则但保留格式?

这个问题的意思就是要将设置的条件格式&#xff0c;转换成单元格格式。除了使用VBA代码将格式转换外&#xff0c;还可以用excel自己的功能来完成这个任务。 一、将条件格式“留下来” 1.设置条件格式 选中数据&#xff0c;点击开始选项卡&#xff0c;设置条件格式&#xff0…

2024年 Java 面试八股文——SpringMVC篇

目录 1.简单介绍下你对springMVC的理解? 2.说一说SpringMVC的重要组件及其作用 3.SpringMVC的工作原理或流程 4.SpringMVC的优点 5.SpringMVC常用注解 6.SpringMVC和struts2的区别 7.怎么实现SpringMVC拦截器 8.SpringMvc的控制器是不是单例模式&#xff1f;如果是&am…

XUbuntu24.04之更换国内高速源(二百二十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

无人机+飞行汽车:低空经济新引擎,有望爆发式增长

无人机和飞行汽车作为低空经济的新引擎&#xff0c;正在引领一场全新的交通革命。随着技术的不断进步和政策的支持&#xff0c;低空经济有望成为未来经济发展的重要领域&#xff0c;实现爆发式增长。 首先&#xff0c;无人机和飞行汽车具有独特的优势和应用场景。无人机可以在…

confluence 设置https代理

使用nginx反待confluence并开启https后&#xff0c;登录confluence会一直提示&#xff1a;scheme、proxyName、proxyPort设置错误。 解决办法&#xff1a; find / -name server.xmlvi /opt/atlassian/confluence/conf/server.xml HTTP反代配置 HTTPS反代配置

ue引擎游戏开发笔记(28)——实现第三人称越肩视角

1.需求分析 实现一个第三人称越肩视角 2.操作实现 1.思路&#xff1a;建立一个弹簧臂和摄像机&#xff0c;调整两者位置达到越肩效果。 2.直接在蓝图操作&#xff1a;添加摄像机和弹簧臂&#xff1a; 3.对弹簧臂勾选使用pawn控制旋转&#xff0c;并适当调整摄像机和弹簧臂位置…

[NSSCTF]prize_p1

前言 之前做了p5 才知道还有p1到p4 遂来做一下 顺便复习一下反序列化 prize_p1 <META http-equiv"Content-Type" content"text/html; charsetutf-8" /><?phphighlight_file(__FILE__);class getflag{function __destruct(){echo getenv(&qu…

Vue 组件的三大组成部分

Vue 组件通常由三大组成部分构成&#xff1a;模板&#xff08;Template&#xff09;、脚本&#xff08;Script&#xff09;、样式&#xff08;Style&#xff09; 模板部分是组件的 HTML 结构&#xff0c;它定义了组件的外观和布局。Vue 使用基于 HTML 的模板语法来声明组件的模…