Android启动流程(五)——init进程对子进程的监控
init进程会读取rc文件,然后孵化很多其他系统服务进程,为防止子进程死亡后称为僵尸进程,init需要监测子进程是否死亡,如果死亡,则清除子进程资源,并重新拉起进程。
system/core/init/init.cpp
InstallSignalFdHandler(&epoll);
init进程第二阶段启动的时候,创建了epoll,并将子进程相关处理注册到epoll中
static void InstallSignalFdHandler(Epoll* epoll) {// Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving// SIGCHLD when a child process stops or continues (b/77867680#comment9).const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };sigaction(SIGCHLD, &act, nullptr);sigset_t mask;sigemptyset(&mask);sigaddset(&mask, SIGCHLD);if (!IsRebootCapable()) {// If init does not have the CAP_SYS_BOOT capability, it is running in a container.// In that case, receiving SIGTERM will cause the system to shut down.sigaddset(&mask, SIGTERM);}if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {PLOG(FATAL) << "failed to block signals";}// Register a handler to unblock signals in the child processes.const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);if (result != 0) {LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);}signal_fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);if (signal_fd == -1) {PLOG(FATAL) << "failed to create signalfd";}constexpr int flags = EPOLLIN | EPOLLPRI;auto handler = std::bind(HandleSignalFd, false);if (auto result = epoll->RegisterHandler(signal_fd, handler, flags); !result.ok()) {LOG(FATAL) << result.error();}
}
使用epoll注册监听信号量fd,子进程死亡会发送信号给init进程,这里就会调用epoll中注册的回调函数HandleSignalFd
static void HandleSignalFd(bool one_off) {signalfd_siginfo siginfo;auto started = std::chrono::steady_clock::now();do {ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));if (bytes_read < 0 && errno == EAGAIN) {auto now = std::chrono::steady_clock::now();std::chrono::duration<double> waited = now - started;if (waited >= kDiagnosticTimeout) {LOG(ERROR) << "epoll() woke us up, but we waited with no SIGCHLD!";started = now;}std::this_thread::sleep_for(100ms);continue;}if (bytes_read != sizeof(siginfo)) {PLOG(ERROR) << "Failed to read siginfo from signal_fd";return;}break;} while (!one_off);switch (siginfo.ssi_signo) {case SIGCHLD:ReapAnyOutstandingChildren();break;case SIGTERM:HandleSigtermSignal(siginfo);break;default:PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;break;}
}
对于SIGCHLD信号,调用ReapAnyOutstandingChildren
system/core/init/sigchld_handler.cpp
void ReapAnyOutstandingChildren() {while (ReapOneProcess() != 0) {}}
static pid_t ReapOneProcess() {siginfo_t siginfo = {};// This returns a zombie pid or informs us that there are no zombies left to be reaped.// It does NOT reap the pid; that is done below.if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {PLOG(ERROR) << "waitid failed";return 0;}auto pid = siginfo.si_pid;if (pid == 0) return 0;// At this point we know we have a zombie pid, so we use this scopeguard to reap the pid// whenever the function returns from this point forward.// We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we// want the pid to remain valid throughout that (and potentially future) usages.auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });std::string name;std::string wait_string;Service* service = nullptr;if (SubcontextChildReap(pid)) {name = "Subcontext";} else {//查找死亡进程pidservice = ServiceList::GetInstance().FindService(pid, &Service::pid);if (service) {name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);if (service->flags() & SVC_EXEC) {auto exec_duration = boot_clock::now() - service->time_started();auto exec_duration_ms =std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);} else if (service->flags() & SVC_ONESHOT) {auto exec_duration = boot_clock::now() - service->time_started();auto exec_duration_ms =std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();wait_string = StringPrintf(" oneshot service took %f seconds in background",exec_duration_ms / 1000.0f);}} else {name = StringPrintf("Untracked pid %d", pid);}}if (siginfo.si_code == CLD_EXITED) {LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;} else {LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;}if (!service) {LOG(INFO) << name << " did not have an associated service entry and will not be reaped";return pid;}//杀死进程,清除资源,并设置重启标志service->Reap(siginfo);if (service->flags() & SVC_TEMPORARY) {ServiceList::GetInstance().RemoveService(*service);}return pid;
}
调用Reap函数清理子进程
system/core/init/service.cpp
void Service::Reap(const siginfo_t& siginfo) {//杀死进程if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {KillProcessGroup(SIGKILL, false);} else {// Legacy behavior from ~2007 until Android R: this else branch did not exist and we did not// kill the process group in this case.if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {// The new behavior in Android R is to kill these process groups in all cases. The// 'true' parameter instructions KillProcessGroup() to report a warning message where it// detects a difference in behavior has occurred.KillProcessGroup(SIGKILL, true);}}// Remove any socket resources we may have created.//清除socket节点for (const auto& socket : sockets_) {if (socket.persist) {continue;}auto path = ANDROID_SOCKET_DIR "/" + socket.name;unlink(path.c_str());}for (const auto& f : reap_callbacks_) {f(siginfo);}if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";trigger_shutdown(*on_failure_reboot_target_);}if (flags_ & SVC_EXEC) UnSetExec();if (name_ == "zygote" || name_ == "zygote64") {//如果是zygote,则清除所有服务进程removeAllEmptyProcessGroups();}if (flags_ & SVC_TEMPORARY) return;pid_ = 0;flags_ &= (~SVC_RUNNING);start_order_ = 0;// Oneshot processes go into the disabled state on exit,// except when manually restarted.if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART) && !(flags_ & SVC_RESET)) {flags_ |= SVC_DISABLED;}// Disabled and reset processes do not get restarted automatically.if (flags_ & (SVC_DISABLED | SVC_RESET)) {NotifyStateChange("stopped");return;}#if INIT_FULL_SOURCESstatic bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
#elsestatic bool is_apex_updatable = false;
#endifconst bool is_process_updatable = !use_bootstrap_ns_ && is_apex_updatable;// If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,// reboot into bootloader or set crashing propertyboot_clock::time_point now = boot_clock::now();if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {bool boot_completed = GetBoolProperty("sys.boot_completed", false);if (now < time_crashed_ + fatal_crash_window_ || !boot_completed) {if (++crash_count_ > 4) {auto exit_reason = boot_completed ?"in " + std::to_string(fatal_crash_window_.count()) + " minutes" :"before boot completed";if (flags_ & SVC_CRITICAL) {if (!GetBoolProperty("init.svc_debug.no_fatal." + name_, false)) {// Aborts into `fatal_reboot_target_'.SetFatalRebootTarget(fatal_reboot_target_);LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "<< exit_reason;}} else {LOG(ERROR) << "process with updatable components '" << name_<< "' exited 4 times " << exit_reason;// Notifies update_verifier and apexdSetProperty("sys.init.updatable_crashing_process_name", name_);SetProperty("sys.init.updatable_crashing", "1");}}} else {time_crashed_ = now;crash_count_ = 1;}}flags_ &= (~SVC_RESTART);//设置重启标志位flags_ |= SVC_RESTARTING;// Execute all onrestart commands for this service.//启动所有rc中标志位onrestart的服务onrestart_.ExecuteAllCommands();NotifyStateChange("restarting");return;
}
这里清除了进程的资源,并设置该进程标志为SVC_RESTARTING,并启动rc中定义的onrestart相关服务
while (true) {...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;}}...}
init进程的死循环中HandleProcessActions会处理服务重启
static std::optional<boot_clock::time_point> HandleProcessActions() {std::optional<boot_clock::time_point> next_process_action_time;//遍历所有的servicefor (const auto& s : ServiceList::GetInstance()) {if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {auto timeout_time = s->time_started() + *s->timeout_period();if (boot_clock::now() > timeout_time) {s->Timeout();} else {if (!next_process_action_time || timeout_time < *next_process_action_time) {next_process_action_time = timeout_time;}}}//如果没有标志为SVC_RESTARTING,则跳过if (!(s->flags() & SVC_RESTARTING)) continue;auto restart_time = s->time_started() + s->restart_period();if (boot_clock::now() > restart_time) {//调用Start重启服务if (auto result = s->Start(); !result.ok()) {LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();}} else {if (!next_process_action_time || restart_time < *next_process_action_time) {next_process_action_time = restart_time;}}}return next_process_action_time;
}
调用Start函数执行
Result<void> Service::Start() {...pid_t pid = -1;if (namespaces_.flags) {pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);} else {//新建一个进程pid = fork();}if (pid == 0) {//子进程中执行,运行子进程服务umask(077);RunService(override_mount_namespace, descriptors, std::move(pipefd));_exit(127);}if (pid < 0) {pid_ = 0;return ErrnoError() << "Failed to fork";}...
}
fork子进程,并调用RunService执行service
void Service::RunService(const std::optional<MountNamespace>& override_mount_namespace,const std::vector<Descriptor>& descriptors,std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd) {...if (!ExpandArgsAndExecv(args_, sigstop_)) {PLOG(ERROR) << "cannot execv('" << args_[0]<< "'). See the 'Debugging init' section of init's README.md for tips";}
}
调用ExpandArgsAndExecv执行
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {std::vector<std::string> expanded_args;std::vector<char*> c_strings;expanded_args.resize(args.size());c_strings.push_back(const_cast<char*>(args[0].data()));for (std::size_t i = 1; i < args.size(); ++i) {auto expanded_arg = ExpandProps(args[i]);if (!expanded_arg.ok()) {LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();}expanded_args[i] = *expanded_arg;c_strings.push_back(expanded_args[i].data());}c_strings.push_back(nullptr);if (sigstop) {kill(getpid(), SIGSTOP);}return execv(c_strings[0], c_strings.data()) == 0;
}
调用execv执行可执行文件
相关文章:
Android启动流程(五)——init进程对子进程的监控
init进程会读取rc文件,然后孵化很多其他系统服务进程,为防止子进程死亡后称为僵尸进程,init需要监测子进程是否死亡,如果死亡,则清除子进程资源,并重新拉起进程。 system/core/init/init.cpp InstallSigna…...

Python每日一练:蚂蚁家族(详解集合法)
文章目录 前言一、题目二、代码分析总结 前言 这题挺有意思,感觉评简单难度有点低了,如果正经用无向图来做,代码还是有点长的。首先得建立节点,估计除第一个和最后一个每个节点都是一条线连进,一条线连出的。就可以这…...

图神经网络:在KarateClub数据集上动手实现图神经网络
文章说明: 1)参考资料:PYG官方文档。超链。 2)博主水平不高,如有错误还望批评指正。 3)我在百度网盘上传了这篇文章的jupyter notebook。超链。提取码8888。 文章目录 文献阅读:代码实操: 文献阅读: 参考文…...
ArduPilot之开源代码调试技巧
ArduPilot之开源代码调试技巧 1. 源由2. ArduPilot Code Debugging Part13. ArduPilot Code Debugging Part24. 持续更新中。。。5. 参考资料 1. 源由 对于如何调试和验证ArduPilot,对于新手来说,有的时候反而是入门的一个门槛。 其实这个并不难&#…...

Linux网络基础-2
在之前的网络基础博客中,我们对网络的基本概念进行了一个简单的介绍,那么接下来的网络内容中,我们将对网络通信中的典型协议进行详细解释。 我们根据网络协议中的分层来对典型协议进行注意介绍,不过对于物理层的传输我们不做考究…...
软件测试报告模板
目录 2 1 概述... 3 1.1 测试目的... 3 1.2 测试策略... 3 1.3 测试方法... 3 1.4 计划验收标准... 3 1.5 测试用例... 4...

记一次azkaban调度异常处理
一、背景 预发布环境使用的数据库性能比较低,根据业务测试的需求,需要将数据库更换成 稳定高性能的数据库。更换业务数据库后azkaban定时任务失败 二、数据库服务信息 说明:该部分使用代号来代替,非真实信息 该数据库存储了azka…...
开发一个vue自定义指令的npm库-系列三:使用rollup打包npm库并发布
配置 rollup 使用rollup将 TypeScript 代码转换为 JavaScript,然后进行压缩和输出到目标文件。 项目根目录新建rollup.config.js import typescript from "rollup/plugin-typescript"; import terser from "rollup/plugin-terser"; import de…...

C嘎嘎的运算符重载基础教程以及遵守规则【文末赠书三本】
博主名字:阿玥的小东东 大家一起共进步! 目录 基础概念 优先级和结合性 不会改变用法 在全局范围内重载运算符 小结 本期送书:盼了一年的Core Java最新版卷Ⅱ,终于上市了 基础概念 运算符重载是通过函数重载实现的…...

【MCAL_UART】-1.2-图文详解RS232,RS485和MODBUS的关系
目录 1 UART,RS232和RS485通信拓扑 2 什么是RS232 2.1 RS232标准的演变 2.2 RS232标准讲了哪些 2.2.1 RS232通信的电平 2.2.2 RS232通信的带宽 2.2.3 RS232通信距离 2.2.4 RS232通信的机械接口 3 什么是RS485 3.1 RS485标准的演变 3.2 RS485标准讲了哪些…...
设计模式详解(二)——单例模式
单例模式简介 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,创建型模式是一类最常用的设计模式,在软件开发中应用非常广泛,它提供了一种创建对象的最佳方式。 单例模…...

为什么hooks不能在循环、条件或嵌套函数中调用
hooks不能在循环、条件或嵌套函数中调用 为什么? 带着疑问一起去看源码吧~ function App() {const [num, setNum] useState(0);const [count, setCount] useState(0);const handleClick () > {setNum(num > num 1)setCount(2)}return <p …...

互联网赚钱项目有哪些?目前最火的互联网项目
互联网是一个神奇的行业,大门不出二门不迈,一根网线一台电脑,甚至一台手机就可以赚钱。它给我们创造了前所未有的商业机会,让成千上万有梦想,敢想敢干的人通过互联网获得了巨大的成功!正因为如此࿰…...

队列、栈专题
队列、栈专题 LeetCode 20. 有效的括号解题思路代码实现 LeetCode 921. 使括号有效的最少添加解题思路代码实现 LeetCode 1541. 平衡括号字符串的最少插入次数解题思路代码实现 总结 不要纠结,干就完事了,熟练度很重要!!ÿ…...

TensorFlow vs PyTorch:哪一个更适合您的深度学习项目?
在深度学习领域中,TensorFlow 和 PyTorch 都是非常流行的框架。这两个框架都提供了用于开发神经网络模型的工具和库,但它们在设计和实现上有很大的差异。在本文中,我们将比较 TensorFlow 和 PyTorch,并讨论哪个框架更适合您的深度…...
大项目环境配置
目录 Linux的龙蜥8是什么? OpenGL是什么? 能讲讲qt是什么吗? 我可以把qt技术理解为c工程师的前端开发手段吗? 我其实一直有些不懂大家所说的这个开发框架啥的,这个该如何理解呢 那现在在我看来,框架意…...
Elasticsearch——》正则regexp
推荐链接: 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…...

五面阿里Java岗,从小公司到阿里的面经总结
面试 笔试常见的问题 面试常见的问题下面给的面试题基本都有。 1 手写代码:手写代码一般会考单例、排序、线程、消费者生产者 排序。 2 写SQL很常考察group by、内连接和外连接 2.面试1-5面总结 1)让你自我介绍 2)做两道算法…...

redis(7)
全局ID生成器: 全局ID生成器,是一种在分布式系统下用来生成全局唯一ID的工具,一般要满足以下特性 唯一性高可用(随时访问随时生成)递增性安全性(不能具有规律性)高性能(生成ID的速度快) 为了增加ID的安全性,我们不会使用redis自增的数值&am…...
互联网从业者高频单词 300个
测试 (Test) 软件 (Software) 用例 (Test Case) 缺陷 (Defect) 提交 (Submit) 回归测试 (Regression Testing) 验收测试 (Acceptance Testing) 单元测试 (Unit Testing) 集成测试 (Integration Testing) 性能测试 (Performance Testing) 负载测试 (load Testing) 压…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...