当前位置:网站首页>im即时通讯开发:进程被杀底层原理、APP应对被杀技巧
im即时通讯开发:进程被杀底层原理、APP应对被杀技巧
2022-06-10 12:59:00 【wecloud1314】
本文的技术原理讲解透彻、系统源码分享到位、样例代码也很有参考意义,希望能对有同样兴趣爱好的Android开发者、IM开发者、推送系统开发者等,带来对于Android进程保活技术的深入理解。
一直以来,App 进程保活都是各大厂商,特别是头部应用开发商永恒的追求。毕竟App 进程死了,就什么也干不了了。一旦 App 进程死亡,那就再也无法在用户的手机上开展任何业务,所有的商业模型在用户侧都没有立足之地了。

早期的 Android 系统不完善,导致 App 侧有很多空子可以钻,因此它们有着有着各种各样的姿势进行保活。
譬如说在 Android 5.0 以前,App 内部通过 native 方式 fork 出来的进程是不受系统管控的,系统在杀 App 进程的时候,只会去杀 App 启动的 Java 进程。因此诞生了一大批“毒瘤”,他们通过 fork native 进程,在 App 的 Java 进程被杀死的时候通过 am命令拉起自己从而实现永生。
那时候的 Android 可谓是魑魅横行,群魔乱舞,系统根本管不住应用,因此长期以来被人诟病耗电、卡顿。
同时,系统的软弱导致了 Xposed 框架、阻止运行、绿色守护、黑域、冰箱等一系列管制系统后台进程的框架和 App 出现。
不过,随着 Android 系统的发展,这一切都在往好的方向演变。
Android 5.0 以上,系统杀进程以 uid 为标识,通过杀死整个进程组来杀进程,因此 native 进程也躲不过系统的法眼。
Android 6.0 引入了待机模式(doze),一旦用户拔下设备的电源插头,并在屏幕关闭后的一段时间内使其保持不活动状态,设备会进入低电耗模式,在该模式下设备会尝试让系统保持休眠状态。
Android 7.0 加强了之前鸡肋的待机模式(不再要求设备静止状态),同时对开启了 Project Svelte。Project Svelte 是专门用来优化 Android 系统后台的项目,在 Android 7.0 上直接移除了一些隐式广播,App 无法再通过监听这些广播拉起自己。
Android 8.0 进一步加强了应用后台执行限制:一旦应用进入已缓存状态时,如果没有活动的组件,系统将解除应用具有的所有唤醒锁。另外,系统会限制未在前台运行的应用的某些行为,比如说应用的后台服务的访问受到限制,也无法使用 Mainifest 注册大部分隐式广播。
Android 9.0 进一步改进了省电模式的功能并加入了应用待机分组,长时间不用的 App 会被打入冷宫。另外,系统监测到应用消耗过多资源时,系统会通知并询问用户是否需要限制该应用的后台活动。
然而,道高一尺,魔高一丈。系统在不断演进,保活方法也在不断发展。
大约在 4 年前出现过一个 MarsDaemon,这个库通过双进程守护的方式实现保活,一时间风头无两。不过好景不长,进入 Android 8.0 时代之后,这个库就逐渐消亡。

一般来说,Android 进程保活分为两个方面:
1)保持进程不被系统杀死;
2)进程被系统杀死之后,可以重新复活。
随着 Android 系统变得越来越完善,单单通过自己拉活自己逐渐变得不可能了。
因此,后面的所谓「保活」基本上是两条路:
1)提升自己进程的优先级,让系统不要轻易弄死自己;
2)App 之间互相结盟,一个兄弟死了其他兄弟把它拉起来。
当然,还有一种终极方法,那就是跟各大系统厂商建立 PY 关系,把自己加入系统内存清理的白名单——比如说国民应用微信。当然这条路一般人是没有资格走的。
知己知彼,百战不殆。既然我们想要保活,那么首先得知道我们是怎么死的。
一般来说,系统杀进程有两种方法,这两个方法都通过 ActivityManagerService 提供:
1)killBackgroundProcesses;
2)forceStopPackage。
在原生系统上,很多时候杀进程是通过第一种方式,除非用户主动在 App 的设置界面点击「强制停止」。
不过国内各厂商以及一加三星等 ROM 现在一般使用第二种方法。因为第一种方法太过温柔,根本治不住想要搞事情的应用。第二种方法就比较强力了,一般来说被 force-stop 之后,App 就只能乖乖等死了。
因此,要实现保活,我们就得知道 force-stop 到底是如何运作的。
既然如此,我们就跟踪一下系统的 forceStopPackage 这个方法的执行流程。
那么,如何实现逃脱被杀呢?我们看这个关键的 5ms。
假设:App 进程在被杀掉之后,能够以足够快的速度(5ms 内)启动一堆新的进程,那么系统在一次循环杀掉老的所有进程之后,sleep 5ms 之后又会遇到一堆新的进程。
如此循环 40 次,只要我们每次都能够拉起新的进程,那我们的 App 就能逃过系统的追杀,实现永生。
是的:炼狱般的 200ms,只要我们熬过 200ms 就能渡劫成功,得道飞升。不知道大家有没有玩过打地鼠这个游戏,整个过程非常类似,按下去一个又冒出一个,只要每次都能足够快地冒出来,我们就赢了。即时通讯开发
现在问题的关键就在于:如何在 5ms 内启动一堆新的进程?
再回过头来看原来的保活方式:它们拉起进程最开始通过am命令,这个命令实际上是一个 java 程序,它会经历启动一个进程然后启动一个 ART 虚拟机,接着获取 ams 的 binder 代理,然后与 ams 进行 binder 同步通信。这个过程实在是太慢了,在这与死神赛跑的 5ms 里,它的速度的确是不敢恭维。
后来:MarsDaemon 提出了一种新的方式,它用 binder 引用直接给 ams 发送 Parcel,这个过程相比 am命令快了很多,从而大大提高了成功率。
其实这里还有改进的空间,毕竟这里还是在 Java 层调用,Java 语言在这种实时性要求极高的场合有一个非常令人诟病的特性:垃圾回收(GC)。
虽然我们在这 5ms 内直接碰上 gc 引发停顿的可能性非常小,但是由于 GC 的存在,ART 中的 Java 代码存在非常多的 checkpoint。想象一下你现在是一个信使有重要军情要报告,但是在路上却碰到很多关隘,而且很可能被勒令暂时停止一下,这种情况是不可接受的。
因此,最好的方法是通过 native code 给 ams 发送 binder 调用。当然,如果再底层一点,我们甚至可以通过 ioctl 直接给 binder 驱动发送数据进而完成调用,但是这种方法的兼容性比较差,没有用 native 方式省心。
通过在 native 层给 ams 发送 binder 消息拉起进程,我们算是解决了「快速拉起进程」这个问题。但是这个还是不够。
还是回到打地鼠这个游戏,假设你摁下一个地鼠,会冒起一个新的地鼠,那么你每次都能摁下去最后获取胜利的概率还是比较高的;但如果你每次摁下一个地鼠,其他所有地鼠都能冒出来呢?这个难度系数可是要高多了。如果我们的进程能够在任意一个进程死亡之后,都能让把其他所有进程全部拉起,这样系统就很难杀死我们了。
新的黑科技保活中通过 2 个机制来保证进程之间的互相拉起:
1)2 个进程通过互相监听文件锁的方式,来感知彼此的死亡;
2)通过 fork 产生子进程,fork 的进程同属一个进程组,一个被杀之后会触发另外一个进程被杀,从而被文件锁感知。
具体来说:
1)创建 2 个进程 p1、p2,这两个进程通过文件锁互相关联,一个被杀之后拉起另外一个;
2)同时 p1 经过 2 次 fork 产生孤儿进程 c1,p2 经过 2 次 fork 产生孤儿进程 c2,c1 和 c2 之间建立文件锁关联。
这样假设 p1 被杀,那么 p2 会立马感知到,然后 p1 和 c1 同属一个进程组,p1 被杀会触发 c1 被杀,c1 死后 c2 立马感受到从而拉起 p1,因此这四个进程三三之间形成了铁三角,从而保证了存活率。
分析到这里,这种方案的大致原理我们已经清晰了。基于以上原理,我写了一个简单的验证性代码(代码在下方)有兴趣的可以看一下。
边栏推荐
- Mysql database (26): View
- Lecture on linear dynamic programming
- Google proposed the super pre training model coca, and the accuracy of fine-tuning top-1 on Imagenet reached 91%! SOTA on multiple downstream tasks!
- Cvpr2022|aconvnetforthe2020s & how to design neural network Summary
- CL210OpenStack操作的故障排除--常见核心问题的故障排除
- Apple邮箱配置QQ邮箱,163邮箱,edu邮箱,gmail邮箱,获取gmail日历
- On distributed transaction
- "Forget to learn again" shell Basics - 29. Awk built-in variables
- The deep neural network classifies nearly 2billion images per second, and the new brain like optical classifier chip is on nature
- Start with interpreting the code automatically generated by BDC, and explain the program components of sapgui
猜你喜欢

從解讀 BDC 自動生成的代碼談起,講解 SAPGUI 的程序組成部分
10 competitive airpods Pro products worth your choice

JS global timer case

13、 System call and shell (freesanding shell, terminal and job control)

Copying and deleting files
![[FAQ] summary of common problems and solutions during the use of rest API interface of sports health service](/img/73/c6c4c0d92e5adb2e831ea4a0290ee9.jpg)
[FAQ] summary of common problems and solutions during the use of rest API interface of sports health service

将anaconda的bin目录加入PATH
![buuctf [Jupyter]notebook-rce](/img/fc/9c2047bdadb606b555e48f553fb1dd.png)
buuctf [Jupyter]notebook-rce

How about the one-stop machine learning opening platform mlflow?

The essence of linear algebra 6 inverse matrix, column space and zero space
随机推荐
[raise bar C #] how to call the base of the interface
【无标题】音频蓝牙语音芯片,WT2605C-32N实时录音上传技术方案介绍
Handling work ticket system
数码管驱动芯片+语音芯片的应用场景介绍,WT588E02B-24SS
Apple邮箱配置QQ邮箱,163邮箱,edu邮箱,gmail邮箱,获取gmail日历
'getWidth()' is deprecated,'getHeight()' is deprecated
Example of full page sliding screen at mobile terminal (sliding the whole screen up and down) (sorting)
Neuron Newsletter 2022 - 05 | ajout de 2 entraînements Sud et 1 Application Nord, mise en œuvre de l'extension personnalisée par Modbus TCP
JS global timer case
QA of some high frequency problems in oauth2 learning
【抬杠C#】如何实现接口的base调用
Comprehensive training of large projects
[summary] individual competition supplement POJ - 3041 asteroids & codeforces - 173b chamber of Secrets
Z-Wave ecosystem status report in 2022
[flinlk] dynamic Kerberos authentication in Flink pit
世贸组织MC12重启 议程重点关注全球经济复苏
CF894C Marco and GCD Sequence
[FAQ] summary of common problems and solutions during the use of rest API interface of sports health service
Nanomq newsletter 2022-05 | release of V0.8.0, new webhook extension interface and connection authentication API
学籍管理系统