0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

稀疏镜像在OpenHarmony上的应用

OpenAtom OpenHarmony 来源:未知 2023-02-09 17:40 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

开源项目 OpenHarmony是每个人的 OpenHarmony b486eddc-a85d-11ed-bfe3-dac502259ad0.jpg

陈甲印

鸿湖万联高级技术专家

鸿湖万联产品推荐官

一、稀疏镜像升级背景

常用系统镜像格式为原始镜像,即RAW格式。镜像体积比较大,在烧录固件或者升级固件时比较耗时,而且在移动设备升级过程时比较耗费流量。为此,将原始镜像用稀疏描述,可以大大地缩减镜像体积,省时省流量。

二、稀疏镜像原理

1、稀疏镜像的概念 原始镜像:即raw image,完整的ext4分区镜像,包含很多全零的无效填充区 稀疏镜像:即sparse image,将raw ext4进行稀疏描述,因此尺寸比较小,制作目录有多少文件就计算多少,没有全零填充2、稀疏镜像格式 b4ab0d66-a85d-11ed-bfe3-dac502259ad0.png   稀疏镜像数据格式:首先是sparse_header占用28byte,然后是12byte的chunk_header,同样这chunk_header的类型决定了后面跟着的数据,如果读到数据是0xCAC1意味着后面是本身的raw_data,如果是0xCAC3,则后面num为0,接着再0xCAC2意味着后面填充4byte的内容。  

三、实现稀疏镜像升级方案

版本基线:

OpenAtom OpenHarmony(以下简称“OpenHarmony”) 3.1 Release

代码路径:

https://gitee.com/openharmony/docs/blob/master/zh-cn/release-notes/OpenHarmony-v3.1-release.md

1、稀疏镜像烧录(1)生成稀疏格式镜像有2种方法可以生成稀疏镜像:1)修改文件build/ohos_var.gni中,sparse_image=true b4c588c6-a85d-11ed-bfe3-dac502259ad0.png  2)编译命令增加--sparse-image字段,如./build.sh --product-name=xxx --sparse-image(2)增加稀疏格式转换工具在目录build/ohos/images/mkimage中增加文件img2simg,该工具用于编译完成后将raw镜像转换为sparse格式,并设置权限为777。(3)编译后的镜像对比 b4f0c1e4-a85d-11ed-bfe3-dac502259ad0.png  编译出的镜像格式为sparse格式,镜像大小相比raw格式明显变小。(4)烧录稀疏镜像烧录稀疏镜像方法和烧录原始镜像方法一致。稀疏镜像本身是不能直接挂载的,在烧录过程中通过uboot将稀疏格式镜像还原为原始镜像,然后写到磁盘中,系统启动后可挂载对应的镜像。 2、稀疏镜像升级升级包采用稀疏镜像制作。(1)修改升级包制作工具官方升级包工具不支持生成稀疏镜像的升级包,修改升级包工具,生成稀疏格式的升级包。.aseupdatepackaging_toolsimage_class.py b514972c-a85d-11ed-bfe3-dac502259ad0.png  按照上图所示注释代码(2)生成稀疏镜像升级包和全量镜像升级包制作方法一致。

参考:

https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-ota-guide.md#%E6%A0%87%E5%87%86%E7%B3%BB%E7%BB%9F%E5%8D%87%E7%BA%A7%E5%8C%85%E5%88%B6%E4%BD%9C

(3)适配updater组件中稀疏镜像功能●增加写稀疏镜像分支.aseupdateupdaterservicesapplypatchdata_writer.cpp写数据函数CreateDataWriter增加写稀疏镜像分支
case WRITE_SPARSE:
{
    std::unique_ptr writer(std::make_unique(partitionName));
    return std::move(writer);
}
●增加稀疏镜像类声明.aseupdateupdaterservicesapplypatch aw_writer.h增加稀疏镜像类声明及相关变量定义
typedefstructsparse_header{
 uint32_tmagic;  /* 0xed26ff3a */
 uint16_tmajor_version;/* (0x1) - reject images with higher major versions */
 uint16_tminor_version;/* (0x0) - allow images with higer minor versions */
 uint16_tfile_hdr_sz; /* 28 bytes for first revision of the file format */
 uint16_tchunk_hdr_sz; /* 12 bytes for first revision of the file format */
 uint32_tblk_sz;  /* block size in bytes, must be a multiple of 4 (4096) */
 uint32_ttotal_blks;/* total blocks in the non-sparse output image */
 uint32_ttotal_chunks; /* total chunks in the sparse input image */
 uint32_timage_checksum;/* CRC32 checksum of the original data, counting "don't care" */
        /* as 0. Standard 802.3 polynomial, use a Public Domain */
        /* table implementation */
} sparse_header_t;

#defineSPARSE_HEADER_MAGIC 0xed26ff3a

#defineCHUNK_TYPE_RAW   0xCAC1
#defineCHUNK_TYPE_FILL   0xCAC2
#defineCHUNK_TYPE_DONT_CARE  0xCAC3
#defineCHUNK_TYPE_CRC32  0xCAC4

typedefstructchunk_header{
 uint16_tchunk_type;/* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
 uint16_treserved1;
 uint32_tchunk_sz; /* in blocks in output image */
 uint32_ttotal_sz; /* in bytes of chunk input file including chunk header and data */
} chunk_header_t;

classSparseWriter: publicDataWriter{
public:
  virtualboolWrite(constuint8_t*addr, size_tlen, WriteModemode, conststd::string&partitionName);

  explicitSparseWriter(conststd::stringpartitionName) : offset_(0), fd_(-1), partitionName_(partitionName) {}

  virtual~SparseWriter()
  {
    offset_= 0;
    if(fd_> 0) {
      fsync(fd_);
      close(fd_);
    }
    fd_= -1;
  }
private:
  intWriteInternal(intfd, constuint8_t*data, size_tlen, conststd::string&partitionName);

  SparseWriter(constSparseWriter&) = delete;

  constSparseWriter&operator=(constSparseWriter&) = delete;
  off64_toffset_;
  intfd_;
  std::string partitionName_;
};
●增加稀疏镜像类实现.aseupdateupdaterservicesapplypatch aw_writer.cpp增加稀疏镜像类实现及相关变量定义,原有代码不变
bool SparseWriter::Write(const uint8_t *addr, size_t len, WriteMode mode, const std::string &partitionName)
{
  if (addr == nullptr) {
    LOG(ERROR) << "SparseWriter: invalid address.";
    return false;
  }
  if (len == 0) {
    LOG(INFO) << "SparseWriter: write length is 0, skip.";
    return false;
  }
  if (fd_ < 0) {
    fd_ = OpenPartition(partitionName_);
    if (fd_ < 0) {
      return false;
    }
  }

  UPDATER_CHECK_ONLY_RETURN(WriteInternal(fd_, addr, len, partitionName_) >= 0, return false);
  return true;
}


int SparseWriter::WriteInternal(int fd, const uint8_t *data, size_t len, const std::string &partitionName)
{
  uint32_t written = 0;
  sparse_header_t *sparse_header;
  chunk_header_t *chunk_header;
  unsigned int chunk;
  void *membuf = NULL;
  uint32_t *fill_buf = NULL;
  uint32_t fill_val;
  uint32_t bytes_written = 0;
  uint32_t total_bytes = 0;
  uint32_t blk = 0;
  uint32_t chunk_data_sz = 0;
  uint32_t blkcnt = 0;
  uint32_t blks = 0;
  uint32_t total_blocks = 0;
  uint32_t addr_offset = 0;
  uint32_t fill_buf_num_blks = 0;


  uint32_t block_size = 4096;
  uint32_t block_count = 524288;
  uint32_t i;
  uint32_t j;
  int ret = lseek64(fd, offset_, SEEK_SET);
  UPDATER_FILE_CHECK(ret != -1, "RawWriter: failed to seek file to " << offset_, return -1);
  fill_buf_num_blks = CONFIG_FASTBOOT_FLASH_FILLBUF_SIZE / block_size;
  LOG(INFO) << "WriteInternal offset_ " << offset_;
  /* Read and skip over sparse image header */
  sparse_header = (sparse_header_t *)data;
  data += sparse_header->file_hdr_sz;
  if (sparse_header->file_hdr_sz > sizeof(sparse_header_t)) {
    /*
    * Skip the remaining bytes in a header that is longer than
    * we expected.
    */
    data += (sparse_header->file_hdr_sz - sizeof(sparse_header_t));
  }
  LOG(INFO) << "=== Sparse Image Header ===";
  LOG(INFO) << "magic: "  << sparse_header->magic;
  LOG(INFO) << "major_version: " << sparse_header->major_version;
  LOG(INFO) << "minor_version: " << sparse_header->minor_version;
  LOG(INFO) << "file_hdr_sz: " << sparse_header->file_hdr_sz;
  LOG(INFO) << "chunk_hdr_sz: " << sparse_header->chunk_hdr_sz;
  LOG(INFO) << "blk_sz: " << sparse_header->blk_sz;
  LOG(INFO) << "total_blks: " << sparse_header->total_blks;
  LOG(INFO) << "total_chunks: " << sparse_header->total_chunks;


  LOG(INFO) << "Flashing Sparse Image";
  blk = 0;
  for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) {
    /* Read and skip over chunk header */
    chunk_header = (chunk_header_t *)data;
    data += sizeof(chunk_header_t);
    if (chunk_header->chunk_type != CHUNK_TYPE_RAW) 
    {
      LOG(INFO) << "=== Chunk Header ===";
      LOG(INFO) << "chunk_type: " << chunk_header->chunk_type;
      LOG(INFO) << "chunk_sz: " << chunk_header->chunk_sz;
      LOG(INFO) << "total_sz: " << chunk_header->total_sz;
    }
    if (sparse_header->chunk_hdr_sz > sizeof(chunk_header_t)) {
      /*
      * Skip the remaining bytes in a header that is longer
      * than we expected.
      */
      data += (sparse_header->chunk_hdr_sz -
        sizeof(chunk_header_t));
    }
    chunk_data_sz = sparse_header->blk_sz * chunk_header->chunk_sz;
    blkcnt = chunk_data_sz / block_size;
    switch (chunk_header->chunk_type) {
    case CHUNK_TYPE_RAW:
      if (chunk_header->total_sz !=
        (sparse_header->chunk_hdr_sz + chunk_data_sz)) {
        LOG(ERROR) << "Bogus chunk size for chunk type Raw";
        return -1;
      }
      if (blk + blkcnt > 0 + block_count) {
        LOG(ERROR) << "Request would exceed partition size!";
        return -1;
      }
      addr_offset = blk * block_size;
      ret = lseek64(fd, offset_ + addr_offset, SEEK_SET);
      if (ret < 0) {
        LOG(ERROR) << "failed to seek file to " << addr_offset << " error=" << strerror(errno);
        return -1;
      }
      written = write(fd, data, blkcnt * block_size);
      if (written < 0) {
        LOG(ERROR) << "SparseWriter: failed to write data of len ";
        return -1;
      }
      total_bytes = total_bytes + blkcnt * block_size;
      blks = written / block_size;
      blk += blks;
      bytes_written += blkcnt * block_size;
      total_blocks += chunk_header->chunk_sz;
      data += chunk_data_sz;
      break;
    case CHUNK_TYPE_FILL:
      if (chunk_header->total_sz !=
        (sparse_header->chunk_hdr_sz + sizeof(uint32_t))) {
        LOG(ERROR) << "Bogus chunk size for chunk type FILL total_sz err " << chunk_header->total_sz << "
";
        return -1;
      }
      ret = posix_memalign (&membuf, 64, 
            ROUNDUP(
            block_size * fill_buf_num_blks,
            64));
      if (ret) {
        LOG(ERROR) << "posix_memalign:" << strerror (errno);
        return -1;
      }
      fill_buf = (uint32_t *)membuf;
      if (!fill_buf) {
        LOG(ERROR) << "Malloc failed for: CHUNK_TYPE_FILL";
        return -1;
      }
      fill_val = *(uint32_t *)data;
      data = data + sizeof(uint32_t);
      for (i = 0;
        i < (block_size * fill_buf_num_blks /
         sizeof(fill_val));
        i++)
        fill_buf[i] = fill_val;
      if (blk + blkcnt > 0 + block_count) {
        LOG(ERROR) << "Request would exceed partition size!";
        return -1;
      }
      for (i = 0; i < blkcnt;) {
        j = blkcnt - i;
        if (j > fill_buf_num_blks)
          j = fill_buf_num_blks;
        addr_offset = blk * block_size;
        ret = lseek64(fd, offset_ + addr_offset, SEEK_SET);
        if (ret < 0) {
          LOG(ERROR) << "failed to lseek file to " << addr_offset << " error=" << strerror(errno);
          return -1;
        }
        written = write(fd, fill_buf, j * block_size);
        if (written < 0) {
          LOG(ERROR) << "SparseWriter: failed to write data of len ";
          return -1;
        }
        total_bytes = total_bytes + j * block_size;
        blks = written / block_size;
        if (blks < j) {
          LOG(ERROR) << "Write failed, block";
          free(fill_buf);
          return -1;
        }
        blk += blks;
        i += j;
      }
      bytes_written += blkcnt * block_size;
      total_blocks += chunk_data_sz / sparse_header->blk_sz;
      free(fill_buf);
      break;
    case CHUNK_TYPE_DONT_CARE:
      blk += blkcnt;
      total_blocks += chunk_header->chunk_sz;
      break;
    case CHUNK_TYPE_CRC32:
      if (chunk_header->total_sz !=
        sparse_header->chunk_hdr_sz) {
        LOG(ERROR) << "Bogus chunk size for chunk type CRC32 total_sz err " << chunk_header->total_sz;
        return -1;
      }
      total_blocks += chunk_header->chunk_sz;
      data += chunk_data_sz;
      break;
    default:
      LOG(INFO) << __func__ << ": Unknown chunk type: " << chunk_header->chunk_type;
      return -1;
    }
  }
  LOG(INFO) << "Wrote "<< chunk <<"blocks, expected to write " << sparse_header->total_blks << "blocks
";
  LOG(INFO) << "........ wrote "<< bytes_written <<"bytes to " << partitionName << "
";
  LOG(INFO) << "total_bytes=" << total_bytes;
  return 0;
}
3、验证稀疏镜像升级(1)拷贝升级包将制作的稀疏镜像升级包通过HDC工具推进系统data目录,并修改文件名为updater.zip,路径如下:/data/updater/updater.zip(2)启动升级系统启动后,在命令行工具输入如下命令启动升级reboot updater:--update_package=/data/updater/updater.zip输入命令后,系统重启,进入升级页面,等待升级完成。 本文介绍了OpenHarmony系统中实现稀疏镜像升级的方法,理解稀疏镜像原理及稀疏镜像还原方法可以快速在自己的系统中应用稀疏镜像升级,提高系统升级速度。


原文标题:稀疏镜像在OpenHarmony上的应用

文章出处:【微信公众号:OpenAtom OpenHarmony】欢迎添加关注!文章转载请注明出处。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 鸿蒙
    +关注

    关注

    60

    文章

    2856

    浏览量

    45340
  • OpenHarmony
    +关注

    关注

    31

    文章

    3926

    浏览量

    20698

原文标题:稀疏镜像在OpenHarmony上的应用

文章出处:【微信号:gh_e4f28cfa3159,微信公众号:OpenAtom OpenHarmony】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    基于Docker镜像逆向生成Dockerfile

    在本文中, 我们将通过理解Docker镜像如何存储数据, 以及如何使用工具查看镜像方方面面的信息来逆向工程一个Docker镜像; 以及如何使用Python的Docker API来构建一个类似Dedockify的工具来创建Dock
    的头像 发表于 03-10 09:45 1209次阅读
    基于Docker<b class='flag-5'>镜像</b>逆向生成Dockerfile

    docker-proxy镜像加速仓库

    自建多平台容器镜像代理服务,支持 Docker Hub, GitHub, Google, k8s, Quay, Microsoft 等镜像仓库。
    的头像 发表于 03-06 16:06 791次阅读
    docker-proxy<b class='flag-5'>镜像</b>加速仓库

    OpenVINO™ Toolkit中如何保持模型稀疏性?

    OpenVINO™ Toolkit 中支持的优化方法,保持模型稀疏性。
    发表于 03-06 06:47

    【北京迅为】itop-3568 开发板openharmony鸿蒙烧写及测试-第1章 体验OpenHarmony—烧写镜像

    【北京迅为】itop-3568 开发板openharmony鸿蒙烧写及测试-第1章 体验OpenHarmony—烧写镜像
    的头像 发表于 03-04 16:31 867次阅读
    【北京迅为】itop-3568 开发板<b class='flag-5'>openharmony</b>鸿蒙烧写及测试-第1章 体验<b class='flag-5'>OpenHarmony</b>—烧写<b class='flag-5'>镜像</b>

    蜂鸟板Openharmony系统跑QT程序

    将QT程序放到Openharmony系统跑,可以运行,但是会被覆盖掉。(用的网盘里面的install,支持QT组件的版本)。 运行情况是,在终端运行QT程序,可以正常运行出来,但是触摸屏幕后,会被Openharmony操作界面
    发表于 02-26 13:04

    迅为RK3568开发板篇OpenHarmony实操HDF驱动配置LED-LED测试

    将编译好的镜像全部进行烧写,镜像在源码根目录 out/rk3568/packages/phone/images/目录下。 烧写完成之后,在调试串口查看打印日志,如下图所示: 然后打开 hdc 工具
    发表于 02-12 11:26

    Flexusx 实例与 Harbor 私有镜像仓库的完美结合

    前言 华为云 828 企业云节,Flexus X 实例携手 Harbor 私有镜像仓库,共创云安全高效新生态!Flexus X 以其卓越性能与稳定性,为 Harbor 提供了理想的运行环境
    的头像 发表于 01-22 18:04 634次阅读
    Flexusx 实例与 Harbor 私有<b class='flag-5'>镜像</b>仓库的完美结合

    Docker-镜像的分层-busybox镜像制作

    目录 知识点1:镜像的分层 示例:进入 docker hub查看Jenkins的Dockerfile 知识点2:base镜像 知识点3:scratch镜像 scratch 镜像是什么?
    的头像 发表于 01-15 10:44 999次阅读
    Docker-<b class='flag-5'>镜像</b>的分层-busybox<b class='flag-5'>镜像</b>制作

    华为云 X 实例的镜像管理详解

    前言 随着云计算的不断普及,云服务器成为企业和开发者日常工作中的重要工具。为了提升工作效率和降低运维成本,云服务器镜像的管理尤为重要。镜像作为服务器或磁盘的模板,预装了操作系统、软件及配置,是快速
    的头像 发表于 01-07 17:01 1483次阅读
    华为云 X 实例的<b class='flag-5'>镜像</b>管理详解

    Dockerfile镜像制作与Docker-Compose容器编排

    Dockerfile镜像制作 docker/podman中, 镜像是容器的基础,每次执行docker run的时候都会指定哪个基本镜像作为容器运行的基础。我们之前的docker的操作都是使用来
    的头像 发表于 01-07 11:01 1140次阅读
    Dockerfile<b class='flag-5'>镜像</b>制作与Docker-Compose容器编排

    OpenHarmony通过挂载镜像来修改镜像内容,RK3566鸿蒙开发板演示

    OpenHarmony通过挂载镜像来修改镜像内容的教程,提高修改镜像内容效率!
    的头像 发表于 01-03 14:21 951次阅读
    <b class='flag-5'>OpenHarmony</b>通过挂载<b class='flag-5'>镜像</b>来修改<b class='flag-5'>镜像</b>内容,RK3566鸿蒙开发板演示

    OpenHarmony源码编译后烧录镜像教程,RK3566鸿蒙开发板演示

    本文介绍瑞芯微主板/开发板编译OpenHarmony源码后烧录镜像的教程,触觉智能Purple Pi OH鸿蒙开发板演示。搭载了瑞芯微RK3566四核处理器,树莓派卡片电脑设计,支持开源鸿蒙OpenHarmony3.2-5.0系
    的头像 发表于 12-30 10:08 1506次阅读
    <b class='flag-5'>OpenHarmony</b>源码编译后烧录<b class='flag-5'>镜像</b>教程,RK3566鸿蒙开发板演示

    dayu200 rk3568 openharmony5.0 sim卡 通话服务

    想请问一下有人在dayu200 rk3568插过sim卡并且成功完成过通话服务吗? 我现在在dayu200烧录了openHarmony release 5.0.0版本的镜像,在上面
    发表于 12-26 16:52

    嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-mfgtools烧录流程介绍之烧写所需镜像

    USB OTG烧写所需镜像在:ELF 1开发板资料包\\06-常用工具\\06-4 烧写工具\\OTG烧写\\mfgtools\\Profiles\\Linux\\OS Firmware
    发表于 12-21 09:25

    飞凌嵌入式ElfBoard ELF 1板卡-mfgtools烧录流程介绍之烧写所需镜像

    USB OTG烧写所需镜像在:ELF 1开发板资料包\\06-常用工具\\06-4 烧写工具\\OTG烧写\\mfgtools\\Profiles\\Linux\\OS Firmware
    发表于 12-20 09:05