低端设备运行Android OTA签名校验脚本被终止,求分块读取优化方案
问题描述
在低端计算机上运行Android的check_ota_package_signature.py脚本校验OTA更新包签名时,程序意外终止。从运行日志判断,是一次性读取约1.1GB的文件导致内存耗尽。
原代码中引发问题的片段:
with open(package, 'rb') as package_file: package_bytes = package_file.read() length = len(package_bytes) footer = bytearray(package_bytes[-6:]) signature_start_from_end = (footer[1] << 8) + footer[0] signature_start = length - signature_start_from_end comment_len = (footer[5] << 8) + footer[4] signed_len = length - comment_len - 2 comment_len = (footer[5] << 8) + footer[4] signed_len = length - comment_len - 2 print('Package length: %d' % (length,)) print('Comment length: %d' % (comment_len,)) print('Signed data length: %d' % (signed_len,)) print('Signature start: %d' % (signature_start,)) use_sha256 = CertUsesSha256(cert) print('Use SHA-256: %s' % (use_sha256,)) h = sha256() if use_sha256 else sha1() h.update(package_bytes[:signed_len]) package_digest = h.hexdigest().lower()
程序运行日志:
Package length: 1182903653 Comment length: 1983 Signed data length: 1182901668 Signature start: 1182901688 Use SHA-256: True Killed
问题根源在于package_file.read()一次性将整个1.1GB文件加载到内存,后续h.update(package_bytes[:signed_len])又复制了近1.1GB的内存块,直接耗尽低端机器的内存。
解决方案
通过分块读取文件计算哈希的方式解决内存不足问题,修改后的代码如下:
# 仅读取文件末尾6字节获取footer和文件总长度,避免加载整个文件 with open(package, 'rb') as package_file: package_file.seek(-6, 2) # 定位到文件末尾前6字节 footer = bytearray(package_file.read(6)) package_file.seek(0, 2) # 定位到文件末尾获取总长度 length = package_file.tell() signature_start_from_end = (footer[1] << 8) + footer[0] signature_start = length - signature_start_from_end comment_len = (footer[5] << 8) + footer[4] signed_len = length - comment_len - 2 print('Package length: %d' % (length,)) print('Comment length: %d' % (comment_len,)) print('Signed data length: %d' % (signed_len,)) print('Signature start: %d' % (signature_start,)) use_sha256 = CertUsesSha256(cert) print('Use SHA-256: %s' % (use_sha256,)) h = sha256() if use_sha256 else sha1() # 分块读取文件,每次读取64KB(可根据机器内存调整大小) block_size = 64 * 1024 with open(package, 'rb') as package_file: remaining_bytes = signed_len while remaining_bytes > 0: read_size = min(block_size, remaining_bytes) block = package_file.read(read_size) if not block: break # 可在此处添加文件读取异常处理 h.update(block) remaining_bytes -= read_size package_digest = h.hexdigest().lower()
关键修改说明
- 避免一次性加载整个文件:通过
seek定位到文件末尾,仅读取6字节的footer数据,同时获取文件总长度,内存占用极低。 - 分块计算哈希:每次读取固定大小的块(示例为64KB),逐步更新哈希对象,内存占用仅为单个块的大小,完全适配低端机器的内存限制。
- 移除冗余内存复制:不再将整个文件内容存入
package_bytes变量,也避免了package_bytes[:signed_len]的大内存切片操作。
内容的提问来源于stack exchange,提问作者Alexander
相关产品推荐
相关产品推荐

