Mac程序的热修复实现思路
Mac程序的热修复实现思路
前言
Mac的热修复,相比iOS来说,从设计上思路要开阔很多。iOS的热修复需要借助runtime,让原生逻辑从原先的oc进入js环境执行,然后热修复时下载js脚本替换原来的代码。而且还被苹果商店给禁止了。
Mac的热修复功能,可以从进程模块替换的角度考虑。一个Mac程序一般都会有多个进程构成,可能有负责网络的独立进程,负责UI的xxx.app,有严格权限控制的独立XPC进程,负责保活的辅助进程等。
所以热修复的思路和iOS不同,Mac一般是替换模块的可执行程序,其中会包括安装进程的提权操作,通用热修复数据下载和执行等。本文主要会介绍这两个方面的设计思路。
安装进程的提权
这里详细解释一下什么是提权。一般我们的热修复任务下载到本地之后,需要有人去执行它,执行它的角色就是安装进程了,安装进程是我们自己设计的,主要是配合热修复安装包的逻辑来工作,把安装包中的更新模块安装到事先指定的位置。
提权的意思,就是让安装进程从普通用户权限提升到root权限,因为一些模块需要放到系统预先设定的目录,比如内核模块,这些目录都是需要root权限的,而且让系统加载内核扩展也需要root权限,所以我们需要提权操作。
提权的实现思路
进程提权在桌面程序是一个比较常见的需求,苹果也有一套官方推荐的方案。这里介绍的就是苹果推荐的方案。
思路不复杂,简单说,要实现用户无需输入密码即可让程序运行在root权限下的方法,就是借助launchd,首次安装软件的时候在launchd的/Library/LaunchDaemons
目录下写入开机自启动的plist文件,plist文件中指定的程序就会以root权限启动了。具体可以看我之前写的一篇文章从内核探究Mac OS X和iOS App 进程的创建原理
- 我们需要在plist文件里面指定一个我们自己写的提权进程,比如叫
Helper
,随后用户开机的时候launchd就会以root权限运行Helper
了。 - 接着Helper可以监听自定义端口或者注册xpc服务,这样一来我们的其他进程就能通过
Helper
进程进行提权了。 - 具体
Helper
代码怎么写,就看自己的业务需求了,简单点,就是注册xpc服务,然后我们的热修复安装进程,需要提权时就通过xpc告知Helper
进程。 Helper
进程把原来的热修复安装进程杀了再重新以子进程的形式启动热修复安装进程,这样热修复安装进程就是root权限了,这样热修复进程就用原来的用户权限提升到root权限。
上面就是Mac开发中提权标准做法。
通用热修复任务下载以及执行的实现思路
安装包的内容设计
热修复安装包一般是一个压缩包,压缩包中有相关的shell脚本以及对应需要升级的进程可执行文件,实际热修复安装进程的工作,只是直接执行解压并执行压缩包中的shell脚本。
安装包的下载
考虑到后台可能会每次下发多个热修复安装包,其中又会包含一些需要重启电脑的升级,从代码的角度上讲,要判断出安装包是否已经被成功安装而且成功重启是比较困难的,一般都要借助文件系统,比如在本地存放一个什么标记之类。
另一个问题就是,多个热修复安装包可能会修复同一个模块,比如一开始A安装包修复了网络模块v1,接着又来了一个B安装包修复网络模块v2,这个时候你可能就会想着是不是需要跳过A安装包直接安装B安装包呢?如果再混合上重启类安装,实际场景远比这个复杂,另外还有可能是热修复过后电脑还没重启,用户又自己手动安装了程序,这样会导致版本号错乱的问题,所以如果把全部场景都考虑上去的话,代码逻辑就会变得非常复杂。
下图就是我一开始把重启类任务限制成最多1个的流程图,逻辑较多。
上面的设计有一个问题,程序会每安装一个重启类任务后就重启电脑一次,如果同时有多个重启类任务时就要重启多次,体验不是很好。而且总体看逻辑是比较复杂的。
后来觉得,直接有多少任务就安装多少任务,无需再区分是否重启,所这样实现逻辑也会简单明了很多,而功能体验上也会更好。以上面的是否存在重启类任务的分支就始终返回否了。
优化后的流程图我就不画了,就是把上面是否存在重启类任务的是
分支去掉即可。
总结
以上就是Mac热修复的设计思路了,主要是在进程的角度处理问题,讲述了提权,任务下载和执行的具体设计思路。可以看出来,Mac开发的思路和iOS还是有很大区别的,不过本质也是一样的。