这两天火星了一件事, Android 不喜欢 fork()/exec()

198 天前
 acess

Android 从一开始就不喜欢 Linux native executables ,认为这玩意游离于 Android framework 管理之外,属于灰色地带,不被鼓励,甚至默认这类进程随时可以被系统杀掉。

这件事最让我感觉难受的是,Google 是太懒了么?好像从一开始,甚至一直到今天,都仍然没有在官方文档里写明这件事。

时间线

早在 2013 年,stackoverflow 上就有相关问答,虽然很显然这并不是官方口径:

https://stackoverflow.com/questions/16179062/using-exec-with-ndk

呃虽然不是 Google 官方下场答的,但这个答主的理解被 Google 官方认可了。在 Android 10 执行 R^X 安全政策,禁止从可写的应用数据目录执行原生代码时(但打包在 APK 内并声明会解压到只读位置则仍然允许执行),Google 方面就在解释时引用了上面这个回答:

https://issuetracker.google.com/issues/128554619#comment4

然后 Android 12 时代 Google 同样是在未文档的情况下引入了一个“幽灵进程杀手”机制,连带 W^X 在一起简直是想要 Termux 死……于是 Termux 开发者替 Google 整理了一份文档:

https://github.com/agnostic-apollo/Android-Docs/blob/master/en/docs/apps/processes/phantom-cached-and-empty-processes.md

而且他还跑去 AOSP 官方 issue tracker 据理力争了一把……

值得庆幸的是 Google 还是听进去了一点,在 Android 14 引入了一个开发者选项,可以彻底把幽灵进程杀手关掉:

https://issuetracker.google.com/issues/205156966#comment120

6792 次点击
所在节点    Android
13 条回复
acess
198 天前
这件事其实还让我想起来很多年前 @madeye 接受的一个 PR ,在 shadowsocks-android 里引入了进程被杀自动重启的 GuardedProcessPool 机制:

https://github.com/shadowsocks/shadowsocks-android/pull/594

那个时候好像认为这属于 vendor-specific bug (?明明是故意的)

现在看其实 ss-android 一直以来都在用幽灵进程支持自己的核心功能……(天
acess
198 天前
(啊啦坏了,一处 W^X 我写错成 R^X 了,捂脸)
codehz
198 天前
还记得之前有个保活技巧就是开一大堆进程互相检测,只要进程生成的比杀的快,(要非常多,不然跑不过),就可以躲过“强制停止”按钮和自动终止后台执行的机制,因为先前版本的强制停止只是循环杀进程 5 秒,之后就放过了
benhaz
198 天前
@codehz 估计这电量也消耗的巨快(捂脸
acess
198 天前
@codehz https://weishu.me/2020/01/16/a-keep-alive-method-on-android/

重点貌似不是进程多让系统杀不过来,而是……

杀一个进程的时候另一个能立即感知(通过文件锁实现);

然后第二个重点是在 5ms 内完成重启。

非常令人意外地,因为系统每执行一轮追杀就要歇息 5ms ,所以另一个进程居然还享有 5ms 时间能够做出反应。只要它在这 5ms 之内能通知 binder 完成重启复活,然后因为系统只重复追杀 40 次(所以也不是“持续杀 5 秒”而是只有 200ms ),40 次追杀都逃过了系统就不再继续追杀了,等于放过。
seers
198 天前
native 实在是黑科技太多了,pdd 已经玩的出神入化
tool2dx
198 天前
我这里 adb+root 出来的 exec 进程,可以 24 小时运行,并不会被杀掉。

当然你用 ndk 的 exec 肯定不行,哪怕 root 也不行。包括 termux+sudo 也不行。
kkocdko
198 天前
感谢楼主的整理,之前看过 termux 开发者的抱怨,之后旧手机就停在 android 10 继续用了,没想到事情这么复杂。
guo4224
197 天前
谷歌提供了新的 syscall 吗?
zhenjiachen
197 天前
我去知道,我用 rust 开发了安卓的 sdk ,刚好里面有调用命令行的代码,那这样不会也被杀进程吧?
acess
197 天前
@tool2dx 诶我这边感觉,之前看过 weishu 的博客讲解,现代 root 方案本来就是类似 ssh 到本机那样的“远程 root”,而并不是 app 自己的进程 fork 出来的


@kkocdko 啊呀别感谢了,今天发现很多地方我都疏漏了,比如 Android12 之后,Android12L 其实就已经引入了一个设置参数来完全关闭幽灵进程杀手,只不过是这个参数不在开发者设置 UI 里,只能通过命令行设置,Android14 只是在开发者设置 UI 里新增了设置项(而且和之前的命令行设置不完全一样,命令行设置可以永久保存,开发者设置这边则是如果关闭了开发者设置就会重置)。再比如 Android5 以前其实只要 fork 出去,force-stop 甚至完全不会杀到你,是 Android5 才开始管到 fork 出去的原生进程,等等……

@zhenjiachen termux 开发者提到过,杀的时候看 app 进程的 oom_adj ,还看进程自己什么时候启动的,启动早运行久的优先杀。而且杀进程动作触发也不是非常频繁。所以按我理解的话……如果只是短暂启动执行一个命令马上就退出那被杀几率应该非常小,但也不好说,因为系统默认全局所有 app 加起来只允许 32 个幽灵进程存在,如果别的 app 在搞事,正巧在你执行命令的时候开了一堆幽灵进程出来跟你抢,这个几率也不严格是 0 吧。
acess
197 天前
@codehz 说实话经你这么说我昨天也感觉有点不明白了,因为回头看 weishu 的讲解里很强调 5ms ,但好像……这个 5ms 是对[同一个进程组]反复追杀时的歇息间隔,而如果我没理解错,被杀掉的这一组已经没办法做出什么反应了,是监视着它们的,受到文件锁关联的[另一组进程]立即可以感知到兄弟阵亡并马上反应……那么,还没被追杀波及到的[另一组进程]反应时间还是 5ms 吗?想到这里我就感觉有点奇怪,但我也太懒了所以没有继续深究(逃
tool2dx
197 天前
@acess 我这里是安卓 12 ,试过很多方法保活都没用。唯一有效的,是父进程 pid 为 0 的情况,系统就会放你一马。

这情况 termux 做不到。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1048280

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX