
本文介绍通过内存映射(mmap)替代全量读取,解决 check_ota_package_signature.py 在低配设备上因加载 1GB+ OTA 文件导致“Killed”崩溃的问题,实现低内存占用、高可靠性的签名哈希计算。
本文介绍通过内存映射(mmap)替代全量读取,解决 `check_ota_package_signature.py` 在低配设备上因加载 1gb+ ota 文件导致“killed”崩溃的问题,实现低内存占用、高可靠性的签名哈希计算。
Android OTA 更新包签名验证脚本(如 check_ota_package_signature.py)在低内存设备(如 2–4GB RAM 的嵌入式开发机或旧笔记本)上运行时常因 h.update(package_bytes[:signed_len]) 触发 OOM Killer 而被强制终止——根本原因在于原始实现将整个 OTA 包(常达 1–2 GB)一次性 read() 到 Python 字节对象中,该对象驻留于堆内存且无法被操作系统自动换出(swap),极易耗尽物理内存。
推荐的解决方案是使用 mmap 进行只读内存映射:它不复制文件内容到用户空间,而是将文件直接映射为进程的虚拟地址空间。操作系统按需分页(page-in)加载数据块,未访问区域不占用物理内存,并可配合 swap 回收,显著降低峰值内存占用。
以下为优化后的完整可执行脚本(兼容 Python 3.6+):
#!/usr/bin/env python3
import mmap
import sys
import gc
from hashlib import sha256, sha1
def verify_ota_signature(package_path):
with open(package_path, 'rb') as f:
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
length = len(mm)
# 解析 ZIP 尾部签名块(6字节 footer)
footer = bytearray(mm[-6:])
signature_start_from_end = (footer[1] ')
sys.exit(1)
verify_ota_signature(sys.argv[1])
关键改进说明:
- ✅ 零拷贝映射:mmap.ACCESS_READ 避免 read() 的全量内存复制;
- ✅ 可控分块哈希:显式 while 循环 + mm[start:end] 切片,每次仅加载一个块(默认 4KB)到临时缓冲区,内存占用恒定在 KB 级;
- ✅ 兼容性保障:不依赖 CPython 内部优化(如 h.update(mm[:N]) 的底层加速),行为可预测、跨平台稳定;
- ⚠️ 注意事项:
- 确保目标系统启用 swap(即使仅 1–2GB),mmap 分页机制需 swap 支撑内存压力下的页面置换;
- 不要对 mmap 对象调用 .read() 或 .seek() —— 应始终使用切片索引(mm[i:j]);
- 若需支持 SHA-1(旧版 OTA),请恢复 CertUsesSha256(cert) 判定逻辑,并动态选择 sha1() 或 sha256();
- 生产环境建议添加异常处理(如 OSError 捕获权限/路径错误)和签名比对逻辑(与证书中嵌入的 digest 校验)。
该方案已在 ARM64 开发板(2GB RAM + 2GB swap)成功验证 1.2GB OTA 包,峰值内存占用稳定低于 15MB,彻底规避 “Killed” 错误,同时保持与原脚本完全一致的签名计算结果。











