blob: fc8dab4aac107a8575b9c0e4d234f008956aa0f6 [file] [log] [blame] [edit]
<html devsite><head>
<title>在内核级别优化 SquashFS</title>
<meta name="project_path" value="/_project.yaml"/>
<meta name="book_path" value="/_book.yaml"/>
</head>
<body>
<!--
Copyright 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<p>
SquashFS 是 Linux 的只读压缩文件系统。该文件系统设计为只读,因此适合在系统分区上使用。很多 Android 设备都可以通过对其系统分区使用此文件系统来获益;例如,以下设备:
</p><ul>
<li>存储容量小的设备,例如 Android Watch。
</li><li>闪存缓慢的设备(压缩可减少块 I/O 的数量)。</li></ul>
<p>
遗憾的是,SquashFS 的性能落后于 ext4。
</p>
<h2 id="optimizations">优化</h2>
<p>
为提高 SquashFS 的性能,已经实现下列优化。
</p>
<h3 id="reduce-the-memory-usage-and-memcpy">减少内存使用量和 memcpy 调用次数</h3>
<p>
读取块(默认为 128K)时,SquashFS 会尝试抓取包含此块的所有页面。
</p>
<p>
如果某个页面是最新页面或已被锁定,则 SquashFS 会转而分配一个完整块,提交读取请求,然后将其内容复制到这些页面。
</p>
<p>
这种方法效果极其低效;一段时间后,页面缓存可能包含最新页面,即使相邻页面并非最新页面也是如此。
</p>
<p>
代码现在能够处理有孔(即缺少页面)的块。这通过以下方式来提高性能:
</p><ul>
<li>减少 <code>memcpy</code> 调用次数
</li><li>减少内存分配</li></ul>
<h3 id="asynchronous-reads">异步读取</h3>
<p>
SquashFS 仍使用已弃用的 <code>ll_rw_block()</code> 函数。使用这种方法存在两个问题:
</p><ul>
<li>顾名思义,该函数会等待读取完成之后再返回结果。这是多余的,因为 <code>.readpage()</code> 已在页面锁上等待。此外,我们需要一个异步机制来高效实现 <code>.readpages()</code>
</li><li>合并读取请求完全取决于 I/O 调度程序。
<code>ll_rw_block()</code> 只会为每个缓冲区创建一个请求。在应该合并哪些信息方面,SquashFS 包含的信息比 I/O 调度程序多。此外,合并请求意味着我们对 I/O 调度程序的依赖会有所减少。</li></ul>
<p>
因此,<code>ll_rw_block()</code> 函数已被 <code>submit_bio()</code> 替换。
</p>
<h3 id="readpages-prefetching">Readpages(预先抓取)</h3>
<p>
SquashFS 不会实现 <code>.readpages()</code>,因此内核会反复调用 <code>.readpage()</code>
</p>
<p>
由于我们的读取请求是异步的,因此内核可以使用其异步预读机制真正预先抓取页面。
</p>
<h3 id="optimize-reading-uncompressed-blocks">优化未压缩块的读取操作</h3>
<p>
Android 之类的现代系统包含大量经过压缩的文件。因此,映像包含大量无法压缩的块。
</p>
<p>
SquashFS 使用相同的逻辑处理压缩和未压缩的块:当 SquashFS 需要读取一个页面时,它实际上读取的是一个完整块(默认为 128k)。
虽然对于压缩块,这是必需的;但对于未压缩的块,这只是在浪费资源。
</p>
<p>
SquashFS 现在只读取预读算法建议的内容,而不会读取一个完整块。
</p>
<p>
这极大地提高了随机读取的性能。
</p>
<h2 id="code">代码</h2>
<p>
AOSP 中提供 SquashFS 代码优化:
</p><ul>
<li><a href="https://android-review.googlesource.com/#/q/topic:squashfs">https://android-review.googlesource.com/#/q/topic:squashfs</a></li></ul>
</body></html>