SquashFS 是 Linux 的只读压缩文件系统。该文件系统设计为只读,因此适合在系统分区上使用。很多 Android 设备都可以通过对其系统分区使用此文件系统来获益;例如,以下设备:
- 存储容量小的设备,例如 Android Watch。
- 闪存缓慢的设备(压缩可减少块 I/O 的数量)。
遗憾的是,SquashFS 的性能落后于 ext4。
优化
为提高 SquashFS 的性能,已经实现下列优化。
减少内存使用量和 memcpy 调用次数
读取块(默认为 128K)时,SquashFS 会尝试抓取包含此块的所有页面。
如果某个页面是最新页面或已被锁定,则 SquashFS 会转而分配一个完整块,提交读取请求,然后将其内容复制到这些页面。
这种方法效果极其低效;一段时间后,页面缓存可能包含最新页面,即使相邻页面并非最新页面也是如此。
代码现在能够处理有孔(即缺少页面)的块。这通过以下方式来提高性能:
异步读取
SquashFS 仍使用已弃用的 ll_rw_block()
函数。使用这种方法存在两个问题:
- 顾名思义,该函数会等待读取完成之后再返回结果。这是多余的,因为
.readpage()
已在页面锁上等待。此外,我们需要一个异步机制来高效实现 .readpages()
。 - 合并读取请求完全取决于 I/O 调度程序。
ll_rw_block()
只会为每个缓冲区创建一个请求。在应该合并哪些信息方面,SquashFS 包含的信息比 I/O 调度程序多。此外,合并请求意味着我们对 I/O 调度程序的依赖会有所减少。
因此,ll_rw_block()
函数已被 submit_bio()
替换。
Readpages(预先抓取)
SquashFS 不会实现 .readpages()
,因此内核会反复调用 .readpage()
。
由于我们的读取请求是异步的,因此内核可以使用其异步预读机制真正预先抓取页面。
优化未压缩块的读取操作
Android 之类的现代系统包含大量经过压缩的文件。因此,映像包含大量无法压缩的块。
SquashFS 使用相同的逻辑处理压缩和未压缩的块:当 SquashFS 需要读取一个页面时,它实际上读取的是一个完整块(默认为 128k)。 虽然对于压缩块,这是必需的;但对于未压缩的块,这只是在浪费资源。
SquashFS 现在只读取预读算法建议的内容,而不会读取一个完整块。
这极大地提高了随机读取的性能。
代码
AOSP 中提供 SquashFS 代码优化:
从 Android 5.0 开始,要使 Android 网络堆栈在 Linux 内核中正常运行,开发者需要集成近期才提交给上游或尚未提交给上游的多个补丁程序。手动验证需要的内核功能或跟踪缺失的补丁程序并不容易,因此,Android 团队打算共享他们使用的测试方案,以确保内核按预期运行。
为何要运行测试?
运行这些测试的原因主要有 3 个:
- 设备上使用的 Linux 内核的确切版本通常是特定于设备的,如果不运行测试,就很难了解某一版本的内核运行是否正常。
- 将内核补丁程序向前移植和向后移植到不同的内核版本或不同的设备树,可能会导致出现一些细微的问题;如果不运行测试,则很难发现这种问题。例如,在开发过程中,某些设备的初始版本具有从 Android-3.4 向前移植(而非从 Android-3.10 中挑选)的 UID 路由补丁程序,因而导致行为不正常。
- 新的网络功能可能需要借助新的内核功能或内核错误修复来实现。
如果测试没有通过,则设备的网络堆栈会运行异常,从而导致出现用户可见的连接错误,例如 WLAN 网络断开连接。设备还可能会无法通过 Android 兼容性测试套件 (CTS) 测试。
使用测试
测试会使用 User-Mode Linux 来启动内核,如同 Linux 主机上的一个进程。请参阅构建编译环境,查看合适的操作系统版本。单元测试框架会使用适当的磁盘映像启动内核,并从主机文件系统运行测试。测试使用 Python 2.x 进行编写,并使用 TAP 接口来测试内核行为和套接字 API。
针对 ARCH=um 编译内核
要运行测试,则必须针对 ARCH=um SUBARCH=x86_64
编译内核。这是一个受支持的基础架构上游,位于通用 Android 内核树(例如 android-3.10
、android-3.18
)中。但是,有时设备内核不会在这种模式下进行编译,因为设备树会在通用文件中包含特定于设备或特定于硬件的代码(例如 sys/exit.c
)。
在很多情况下,确保特定于硬件的代码位于 #ifdef
之后就足够了。通常,这应该是配置选项中的 #ifdef
,用于控制与代码相关的特定功能。如果没有这样的配置选项,则将特定于硬件的代码放在 #ifndef CONFIG_UML
块中。
一般来说,这项修复应该由内核树提供者(例如,芯片组供应商或 SoC 供应商)负责。我们正在与原始设备制造商 (OEM) 和供应商合作,确保当前和未来的内核将针对 ARCH=um SUBARCH=x86_64
进行编译,而无需进行任何更改。
运行测试
测试位于 kernel/tests/net/test
下。建议您从 AOSP master 运行测试,因为它们是最新的;在某些情况下,指定的 Android 版本正常运行所必需的内核功能尚未在给定版本中进行全面测试。有关如何运行测试的信息,请参阅内核网络测试自述文件。总而言之,从您的内核树顶部运行:
ANDROID_TREE/kernel/tests/net/test/run_net_test.sh all_tests.sh
通过测试
内核网络测试 Python 源文件包含注释,这些注释会指定通过测试所必需的已知内核提交。在常见内核树中,至少 AOSP 中的 kernel/common
项目中的 android-3.10
和 android-3.18
分支应该通过测试。因此,在由 3.10 或 3.18 派生的内核树上通过测试在很大程度上而言就是从这些树中挑选补丁程序。
做出贡献
报告问题
请使用组件网络标签在 Android 问题跟踪器中报告内核网络测试的任何问题。
记录提交并添加测试
请如上文所述报告问题;如果可能,请在发生以下情况时上传更改以修复问题:
- 测试没有在通用内核树上通过
- 您发现在源代码注释中没有提及某项必要的提交
- 需要进行重大更改才能在上游内核通过测试
- 您认为测试是多余指定的,或者未来的内核测试会失败
- 您希望添加更多测试或扩大现有测试的覆盖面。