IPC 选型
说到 IPC,首要的问题就是架构选型,不同的架构效果大相径庭。
CS 架构 vs 去中心化架构
Android 平台第一个想到的就是 ContentProvider:一个单独进程管理数据,数据同步不易出错,简单好用易上手。然而它的问题也很明显,就是一个字慢:启动慢,访问也慢。这个可以说是 Android 下基于 Binder 的 CS 架构组件的通用痛点。至于其他的 CS 架构,例如经典的 socket、PIPE、message queue,因为要至少 2 次的内存拷贝,就更加慢了。
MMKV 追求的是极致的访问速度,我们要尽可能地避免进程间通信,CS 架构是不可取的。再考虑到 MMKV 底层使用 mmap 实现,采用去中心化的架构是很自然的选择。我们只需要将文件 mmap 到每个访问进程的内存空间,加上合适的进程锁,再处理好数据的同步,就能够实现多进程并发访问。
挑选进程锁
然而去中心化的架构实现起来并不简单,Android 是个阉割版的 Linux,IPC 组件的支持比较残缺。例如,说到进程锁第一个想到的就是 pthread 库的 pthread_mutex,创建于共享内存的 pthread_mutex 是可以用作进程锁的,然而 Android 版的 pthread_mutex 并不保证robust,亦即对 pthread_mutex 加了锁的进程被 kill,系统不会进行清理工作,这个锁会一直存在下去,那么其他等锁的进程就会永远饿死。其他的 IPC 组件,例如信号量、条件变量,也有同样问题,Android 为了能够尽快关闭进程,真是无所不用其极。
找了一圈,能够保证 robust 的,只有已打开的文件描述符,以及基于文件描述符的文件锁和 Binder 组件的死亡通知(是的,Binder 也是依赖这个清理机制运作,打开的文件是 /dev/binder)。
我们有两个选择:
- 文件锁,优点是天然 robust,缺点是不支持递归加锁,也不支持读写锁升级/降级,需要自行实现。
- pthread_mutex,优点是 pthread 库支持递归加锁,也支持读写锁升级/降级,缺点是不 robust,需要自行清理。
关于 mutex 清理,有个可能的方案是基于 Binder 死亡通知进行清理:A、B进程相互注册对方的死亡通知,在对方死亡的时候进行清理。但有个比较棘手的场景:只有 A 进程存在,那么他的死亡通知就没人处理,留下一个永远加锁的

本文深入探讨了MMKV在Android上的多进程设计,包括IPC选型,如CS架构与去中心化架构的优缺点,以及挑选进程锁的考量。文章详细阐述了使用文件锁作为进程锁的原因和限制,并介绍了多进程实现细节,如状态同步(写指针增长、内存重整、内存增长)的处理方法。同时,文章提出了递归锁和锁升级/降级的解决方案,以应对文件锁的局限性。
最低0.47元/天 解锁文章
3389

被折叠的 条评论
为什么被折叠?



