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 nodeint 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.mzph.cn/news/831361.shtml

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

相关文章

vue快速入门(五十一)历史模式

注释很详细&#xff0c;直接上代码 上一篇 新增内容 历史模式配置方法 默认哈希模式&#xff0c;历史模式与哈希模式在表层的区别是是否有/#/ 其他差异暂不深究 源码 //导入所需模块 import Vue from "vue"; import VueRouter from "vue-router"; import m…

Hive 表定义主键约束

文章目录 1.建表语句2.主键约束3.主键约束的意义参考文献 1.建表语句 先看一下官方给的完整的见表语句&#xff1a; CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name -- (Note: TEMPORARY available in Hive 0.14.0 and later)[(col_name data…

全新TOF感知RGBD相机 | 高帧率+AI,探索3D感知新境界

海康机器人在近期的机器视觉新品发布会上推出的全新TOF感知RGBD相机,无疑是对当前机器视觉技术的一次革新。这款相机不仅融合了高帧率、轻松集成、体积小巧以及供电稳定等诸多优点,更重要的是,它将AI与3D感知技术完美结合,通过高帧率+AI算法,实现了对不同场景的快速捕捉与…

电脑重装系统ip地址会变吗

在数字化世界中&#xff0c;IP地址就像是每个计算机设备的“门牌号”&#xff0c;它帮助我们在互联网上进行定位和通信。然而&#xff0c;当我们的电脑系统进行重新安装时&#xff0c;许多用户会担心其IP地址是否会发生改变。虎观代理小二将带您深入探讨电脑重装系统后IP地址的…

Android Studio报错:Constant expression required

【出现的问题】&#xff1a; 使用JDK17以上版本&#xff0c;switch语句报错&#xff1a;Constant expression required 【解决方法】&#xff1a; 在gradle.properties配置文件下添加代码&#xff1a; android.nonFinalResIdsfalse 如图&#xff1a; 接着再点击右上角的Sync…

详解 Go 程序的启动流程,你知道 g0,m0 是什么吗?

自古应用程序均从 Hello World 开始&#xff0c;你我所写的 Go 语言亦然&#xff1a; import "fmt"func main() {fmt.Println("hello world.") }这段程序的输出结果为 hello world.&#xff0c;就是这么的简单又直接。但这时候又不禁思考了起来&#xff0…

Android Studio实现简单的自定义钟表

项目目录 一、项目概述二、开发环境三、详细设计3.1、尺寸设置3.2、绘制表盘和指针3.3、动态效果 四、运行演示五、总结展望六、源码获取 一、项目概述 在安卓开发中&#xff0c;当系统自带的View已经无法满足项目需求时&#xff0c;就要自定义View。在Android中是没有与钟表有…

chrome extension插件替换网络请求中的useragent

感觉Chrome商店中的插件不能很好的实现自己想要的效果,那么就来自己动手吧。 本文以百度为例: 一般来说网页请求如下: 当前使用的useragent是User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safar…

android studio项目实战——备忘录(附源码)

成果展示&#xff1a; 1.前期准备 &#xff08;1&#xff09;在配置文件中添加权限及启动页面顺序 ①展开工程&#xff0c;打开app下方的AndroidManifest.xml,添加权限&#xff0c;如下&#xff1a; <uses-permission android:name"android.permission.CAMERA"…

【Proteus】LED呼吸灯 直流电机调速

1.LED呼吸灯 #include <REGX51.H> sbit LEDP2^0; void delay(unsigned int t) {while(t--); } void main() {unsigned char time,i;while(1){for(time0;time<100;time){for(i0;i<20;i){LED0;delay(time);LED1;delay(100-time);}}for(time100;time>0;time--){fo…

软件工程全过程性文档(软件全套文档整理)

软件项目相关全套精华资料包获取方式①&#xff1a;进主页。 获取方式②&#xff1a;本文末个人名片直接获取。 在软件开发的全过程中&#xff0c;文档是记录项目进展、决策、设计和测试结果的重要工具。以下是一个简要的软件全过程性文档梳理清单&#xff1a; 需求分析阶段…

基于 Spring Boot 博客系统开发(五)

基于 Spring Boot 博客系统开发&#xff08;五&#xff09; 本系统是简易的个人博客系统开发&#xff0c;为了更加熟练地掌握 SprIng Boot 框架及相关技术的使用。&#x1f33f;&#x1f33f;&#x1f33f; 基于 Spring Boot 博客系统开发&#xff08;四&#xff09;&#x1f…

go-mysql-transfer 同步数据到es

同步数据需要注意的事项 前提条件 1 要同步的mysql 表必须包含主键 2 mysql binlog 必须是row 模式 3 不支持程序运行过程中修改表结构 4 要赋予连接mysql 账号的权限 reload, replication super 权限 如果是root 权限则不需要 安装 go-mysql-transfer ​ git clone…

每日OJ题_DFS爆搜深搜回溯剪枝⑧_力扣980. 不同路径 III

目录 力扣980. 不同路径 III 解析代码 力扣980. 不同路径 III 980. 不同路径 III 难度 困难 在二维网格 grid 上&#xff0c;有 4 种类型的方格&#xff1a; 1 表示起始方格。且只有一个起始方格。2 表示结束方格&#xff0c;且只有一个结束方格。0 表示我们可以走过的空…

React 第十五章 Ref

React ref 是 React 中一个用于访问组件中 DOM 元素或者类实例的方式。它允许我们直接操作 DOM&#xff0c;而不需要通过 state 或 props 来更新组件。 过时 API&#xff1a;String 类型的 Refs 在最最早期的时候&#xff0c;React 中 Ref 的用法非常简单&#xff0c;类似于 …

Docker consul 的容器服务更新与发现

目录 一. consul 的相关知识 1 什么是注册与发现 2. 什么是 consul 3. zookeeper 和 consul 的区别 二. consul 部署 1. consul 服务器 2. registrator 服务器 三. consul-template 1. consul-template 的作用 2. consul-template 的具体部署运用 2.1 准备 templa…

Deep Learning Part Five RNNLM的学习和评价-24.4.30

准备好RNNLM所需要的层&#xff0c;我们现在来实现RNNLM&#xff0c;并对其进行训练&#xff0c;然后再评价一下它的结果的。 5.5.1 RNNLM的实现 这里我们将RNNLM使用的网络实现为SimpleRnnlm类&#xff0c;其层结构如下&#xff1a; 如图 5-30 所示&#xff0c;SimpleRnnlm …

设计模式: 工厂模式

工厂模式&#xff08;Factory Pattern&#xff09;是 Java 中最常用的设计模式之一&#xff0c;这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 工厂模式提供了一种创建对象的方式&#xff0c;而无需指定要创建的具体类。 工厂模式属于创建型…

我的毕业实习经历

我的毕业实习经历 前言求职之路成为社畜重获自由结语 前言 这篇博客原本我想以实习生找工作踩坑指南&#xff1a;我的毕业实习经历为文章标题的&#xff0c;原因是跟我前面发布的一篇博客《实习生找工作踩坑指南&#xff1a;租房篇》做一个呼应收尾&#xff0c;奈何标题略显臃肿…

免费分享一套SpringBoot+Vue在线考试系统(优质版),帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue在线考试系统(优质版)&#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue在线考试系统(优质版) Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue在线考试系统(优质版) Java毕…