ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

南京大学 操作系统 (JYY) 学习笔记:火星上的 Bug 与 Android 移动操作系统的黑客法则

南京大学 操作系统 (JYY) 学习笔记:火星上的 Bug 与 Android 移动操作系统的黑客法则 写在前面这是本系列的第二十八篇。虚拟化和容器技术使我们可以管理成千上万台计算机为小到一个班集体、大到全世界的人提供各种云端服务。而连接每一个人的不仅是云端的超级分布式应用还有万千的终端物理设备。虽然通用的 Linux、Windows 操作系统的 API 完全可以胜任管理这些设备但面向特定领域的操作系统和数据中心里的巨兽相比又有着极其独特的设计哲学。本讲内容嵌入式实时和移动操作系统的设计与实现原理。每个人都可以是生活的黑客嵌入式操作系统 (Embedded OS)我们之前学的都是通用操作系统这里介绍一下特定领域的操作系统。嵌入式的操作系统通常更关注并发特性与绝对的可靠性。为了达到这个目的它们甚至愿意放弃一些诸如高吞吐量 I/O 读写效率之类的通用性能。什么是嵌入式系统“Embedded”: 计算机系统是“嵌入”到特定物理设备中的。人造卫星、工业控制 (工控)、家用电器、医疗器械、穿戴设备……核心要求体积小、功耗低、可靠性极高。操作系统通常更简单(因为领域限定)当然经过高度裁剪的 Linux 也是可以跑在嵌入式设备上的。例子PLC (Programmable Logic Controller) 和工控自动逻辑控制设备支撑了现代工业的自动化流水线。典型设计Event-based (事件驱动) 与 Real-time (实时)通常有严格的实时 (real-time) 保障必须在严格的时间限制内对外部事件作出可预测的响应。晚了一毫秒可能就是车毁人亡。例子无论今天的新能源汽车车机屏幕多么花哨跑着 Android 甚至玩 3A 游戏最终负责底盘实时控制的仍然是底层的 MCU 和硬实时操作系统。(还记得导致至少 6 人死亡的 Therac-25 医疗事故并发 Bug 吗这就是缺乏硬实时安全保障的代价)例子FreeRTOSA FreeRTOS application will start up and execute just like a non-RTOS application untilvTaskStartScheduler()is called.BaseType_txTaskCreate(TaskFunction_t pvTaskCode,// 函数指针constchar*constpcName,// 名称constconfigSTACK_DEPTH_TYPE uxStackDepth,// 堆栈深度void*pvParameters,// 参数UBaseType_t uxPriority,// 任务优先级(核心)TaskHandle_t*pxCreatedTask);// 返回句柄这个 API 和普通的thread.h里的spawn差不多。Task 通常是一个死循环。操作系统按绝对的优先级唤醒阻塞的 Tasks高优先级无条件抢占低优先级。虽然它没有绝对的 Hard real-time (硬实时)但严格的优先级机制能实现极强的控制力。优先级带来的致命麻烦Priority Inversiondefjyy():set_priority(1)# 优先级最低mutex_lock(restroom_lock)# 拿到了厕所的互斥锁def马院长():set_priority(10)# 优先级中等long_work()# 执行极其耗时的无锁计算任务def校长():set_priority(100)# 优先级极高mutex_lock(restroom_lock)# 想要进厕所被锁住了只能等待如果只有一个处理器会发生什么灾难jyy(低优先级) 拿到了锁。校长(高优先级) 想要锁被阻塞只能等jyy用完。此时马院长(中优先级) 突然来了因为他的优先级比jyy高他直接抢占了 CPU开始执行漫长的long_work()结果jyy被赶下处理器无法释放锁 $ \rightarrow $校长作为最高优先级被迫一直等待中优先级的马院长执行完毕这就是著名的“优先级反转 (Priority Inversion)”。这个“事故”曾经在火星上真实发生过Sojourner “探路者” (PathFinder)1997 年 7 月 4 日登陆火星。Lander (登陆舱):IBM Rad6000 SC (20 MIPS), 128 MiB RAM, 6 MiB EEPROM。Rover (火星车):Intel 80C85 (0.1 MIPS), 512K RAM, 176K Flash。运行着VxWorks“实时” 任务操作系统。任务优先级分配ASI/MET task: 大气成分监测 (低优先级)bc_dist task: 分发任务 (中优先级)bc_sched task: 总线调度 (高优先级)着陆后开始出现系统疯狂重启。原因正是优先级反转触发了硬件的看门狗 (Watchdog) 定时器系统认为严重故障被迫硬重启。(低优先级)select$ \rightarrow $pipeIoctl$ \rightarrow $selNodeAdd$ \rightarrow $mutex_lock(高优先级)pipeWrite$ \rightarrow $mutex_lock最终 JPL 工程师通过远程发送补丁开启了互斥锁的“优先级继承 (Priority Inheritance)”特性低优先级拿到锁后临时提升到等待该锁的最高优先级才挽救了这台价值上亿美元的火星车。移动操作系统 (Android 演进史)人手一个的“手机”曾经的手机可不是今天的样子我上大学的时候在用什么手机Nokia 5310 XpressMusic (2007): 320 x 240 屏幕; 200 万像素摄像头; 30 MB 存储这在当时已经算是非常精细了。运行着 Symbian S40 系统相比于“功能机”已经是巨大的进步了。Symbian 和 J2ME 的时代没变的是大学上课和现在一样无聊。The K Virtual Machine (KVM) J2ME。当时的高端玩家都在折腾如何用极小的 KVM 在几十 KB 内存里跑 Java 游戏。那个男人改变了世界2007 年乔布斯发布了初代 iPhone。那个男人遇到了对手Google 2005 年秘密收购了 Android Inc.初创团队包含一些极其硬核的 BeOS 操作系统的成员例如牛逼的 Binder IPC 机制就源自 BeOS。Android 的兴起高瞻远瞩的豪赌Android 架构Linux Full JVM Framework API 开放平台早期的 Dalvik VM 虚拟机 $ \rightarrow $ 后来演化成了 Android Runtime (ART)。那么代价呢卡到爆啊第一代安卓机 HTC G1 使用的是 Qualcomm MSM7201 处理器。32-bit ARMv6 528MHz (单核 CPU, TSMC 90nm工艺)。顺序八级流水线 (IPC 1)拖着沉重的 Java 虚拟机连滑动地图都会卡成 PPT。但这其实是一个高瞻远瞩的决定他们赌摩尔定律会生效2008 年同年Intel 桌面端已经发布了恐怖的 i7-965 (4C8T, 6 发射)。移动端算力大爆炸只是时间问题。对手们的下场Symbian暴毙于 C。C 代码谁写谁知道指针飞舞满地 Bug。Windows Phone暴毙于封闭和缓慢的反应。(其实也许只要熬到了移动网页时代大家就都能活下来了)。Android 选择了 Java。2005 年的互联网时代Java 已经涌现出极其强大的生命力和生态。借着海量 Java 开发者的东风虽然早期卡顿但它硬生生熬死了 Symbian 和 Windows Phone。最终形成了如今 Android 和 iPhone 两强争霸的格局。One More Comment (大模型时代的隐喻)初期的“低性能”总会解决的。算法 (软件) 会进步硬件也会进步摩尔定律还没死呢。在今天的大语言模型 (LLM) 时代这同样成立今天 600B 庞大模型才能做到的智能通过算法演进明天 30B 的小模型也能做到。一定存在一个临界点能实现端侧“实时推理无处不在”。Android Apps 的真面目首先它是一个 Java 程序 (Kotlin)。其次它有独特的生命周期运行模型。Android Platform APIsandroid.view.View: “the basic building block for user interface components” (UI 组件的基石)android.webkit.WebView: 嵌入应用的网页引擎android.hardware.camera2: 相机控制android.database.sqlite: 原生内置的数据库四大组件Activity (活动), Service (后台服务), ContentProvider (数据提供), BroadcastReceiver (广播接收)。Platform API 之下一个“微内核”架构核心黑科技 1Binder IPCRemote Procedure Call (RPC) 远程过程调用remote.transact()在性能优化和易用之间的极致权衡相比于 UNIX 传统的管道 / 套接字 (Socket)它们太“底层”了手工管理序列化极其痛苦。Binder 基于共享内存实现 (Linux Kernelbinderdriver)。实现了绝无仅有的“Copy once” (只需一次内存拷贝)性能碾压传统 IPC。自带服务端线程池和高效的注册机制。核心黑科技 2Activity Stack (活动栈)当你在微信里点击链接拉起知乎看完后按返回键知乎会销毁并优雅地返回到微信界面。这种行为在用户“切换应用”后仍能完美保持这与传统桌面操作系统的窗口管理器Alt Tab逻辑有着本质的体验区别极大地适应了移动端的小屏操作流。每个人都可以是生活的黑客 (Hacking Android )Android: 不只是应用生态更是极客生态UNIX 哲学POSIX system calls $ \rightarrow $ libc $ \rightarrow $ coreutils $ \rightarrow $ 应用程序世界幕后是gcc,gdb,vscode。Android 也为我们留下了极其丰富的后门开发者工具Android Debug Bridge (adb):神器中的神器adb push/pull/install管理文件和应用。adb shell获取底层终端 (比如screencap /sdcard/screen.png瞬间截图)。adb forward,logcat抓取日志。我们可以做什么花活1. scrcpy利用 ADB 在设备上捕获高帧率视频流 (通常有硬件加速)通过 USB 极低延迟投屏到电脑 Host 端进行控制。2. UI Automation (Monkey 测试)无限猴子定理给 10,000 只猴子和打字机有一只能盲打出 C 程序其他只能打出 Perl 乱码。让“电子猴子”去疯狂随机点击、滑动屏幕用来测试我们的应用会不会 Crash这就是高强度的自动化鲁棒性测试。甚至可以“任意改变” App 的行为 (Hook 注入)LSPosed: 强大的 Xposed 框架分支可以在运行时动态劫持和修改任意 Java 方法的返回值比如拦截广告、伪造定位、微信自动抢红包。现在甚至有VirtualXposed等技术利用双开容器的原理连 Root 权限都不需要就能实现 Hook。极客实战保活与反杀 (Diehard Apps)如何彻底杀死一个 Android 进程读读内核源码ActivityManagerService.javaAndroid 为了安全给每一个 App 都分配了一个独立的沙箱uid。遍历进程表找到属于这个uid的所有进程组。执行Process.KillProcessGroup。为了防止那些生命力顽强的流氓 App系统会间隔 5ms连续发送 40 次 SIGKILL 信号确保彻底斩草除根科普kill -9** 代表了什么**普通的kill默认发送SIGTERM信号编号 15进程可以捕获它并在退出前优雅地保存数据。而kill -9发送的是SIGKILL信号。这是一个底层的强杀命令进程根本无法捕获或忽略连喊救命的机会都没有直接被内核干掉。黑客的反制利用数据竞争进行进程保活既然系统要杀我那我就利用并发漏洞让子进程故意成为“孤儿进程”脱离原本的进程组从而在组强杀中幸存逃避SIGKILL信号。利用文件锁互相监控。一旦主进程被杀死孤儿进程立刻在极短的时间窗口内重新拉起另一个主进程于是手机里出现了连系统强制停止都杀不死的僵尸 App。“Diehard apps” (EuroSys’19 顶级学术会议论文探讨了这种奇观)。在 Android 的浪潮里折腾作为系统程序员我们也想过做一些极其有趣的事。甚至把它做成了学术论文发表 (Jigsaw paper)。在 LLM 大模型时代基于 UI 的自动化和语义识别这些 Hack 行为会变得更容易做成、更好玩。很多时候我们都被学术界 “Publish or perish (要么发表要么出局)” 的枷锁束缚了但只要跳出框架每个人都可以是生活中的极客与黑客总结Take-away messages:在每一个细分的计算领域操作系统的底层架构师都会做出一些面向该领域的特殊设计妥协例如嵌入式领域的绝对实时性控制 (real-timeliness)或是 Android 面向庞大应用生态而建立的 JVM Binder 运行模型。但剥开层层迷雾回到应用视角去俯瞰操作系统它本质上永远是一组封装好的底层 API。这组 API 屏蔽了枯燥的硬件细节使得无数开发者能够愉快、高效地构建出改变世界的应用。是的大道至简操作系统的魔力就是这么简单。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表