Android OTA 相关工具(七) 使用 lpunpack 解包 super.img

news2024/11/25 20:45:53

文章目录

    • 1. lpunpack 的编译
    • 2. lpunpack 的帮助信息
    • 3. lpunpack 的用法
      • 3.1 解包所有镜像
      • 3.2 解包指定名称分区镜像
      • 3.3 解包指定槽位分区镜像
    • 4. 其它

从 Android 10(Q) 开始,引入了动态分区,伴随的就是一组动态分区内容数据增删改查相关的操作,以及这些操作所需要的工具,包括 lpdump, lpmake, lpunpack, lpadd, lpflash。

工具名称前缀 lp 表示是 logic partition,即逻辑分区。

所谓逻辑分区,是相对于物理分区而言,因为动态分区内部的各种分区并不是实际的物理分区。

因此,可以说动态分区本身的 super 是物理分区,但 super 内包含的各种分区就是逻辑分区。

前面两篇分别介绍了 lpdump 和 lpmake,本篇介绍 lpunpack。

本文基于 android-13.0.0_r41 编译生成的 lpunpack 介绍该工具的使用,但也适用于 Android 10(Q) 开始的其它 Android 版本。

《Android OTA 相关工具》系列,目前已有文章列表:

  • 《Android OTA 相关工具(一) 虚拟 A/B 之 snapshotctl》
  • 《Android OTA 相关工具(二) 动态分区之 dmctl》
  • 《Android OTA 相关工具(三) A/B 系统之 bootctl 工具》
  • 《Android OTA 相关工具(四) 查看 payload 文件信息》
  • 《Android OTA 相关工具(五) 使用 lpdump 查看动态分区》
  • 《Android OTA 相关工具(六) 使用 lpmake 打包生成 super.img》
  • 《Android OTA 相关工具(七) 使用 lpunpack 解包 super.img》

本文为洛奇看世界(guyongqiangx)原创,转载请注明出处。

文章链接:https://blog.csdn.net/guyongqiangx/article/details/132598451

1. lpunpack 的编译

lpmake 工具从 Android Q 版代码开始引入,源码位于 system/extras/partition_tools 目录下,默认编译 Android 后输出到 out/host/linux-x86/bin/lpmake ,第一次编译以后,通过 source 和 lunch 操作设置 Android 编译环境后就可以引用。

例如:

$ source build/envsetup.sh 
$ lunch aosp_panther-userdebug
$ which lpunpack 
/public/rocky/android-13.0.0_r41/out/host/linux-x86/bin/lpunpack

当然,也可以将 out/host/linux-x86/bin 添加到当前目录下使用:

$ echo $PATH
/home/rocky/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ export PATH=${PWD}/out/host/linux-x86/bin:$PATH
$ echo $PATH
/public/rocky/android-13.0.0_r41/out/host/linux-x86/bin:/home/rocky/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ which lpunpack 
/public/rocky/android-13.0.0_r41/out/host/linux-x86/bin/lpunpack

两种方式都差不多,不过个人推荐前者。

2. lpunpack 的帮助信息

lpunpack 的帮助信息非常简单:

android-13.0.0_r41$ lpunpack -h
lpunpack - command-line tool for extracting partition images from super

Usage:
  lpunpack [options...] SUPER_IMAGE [OUTPUT_DIR]

Options:
  -p, --partition=NAME     Extract the named partition. This can
                           be specified multiple times.
  -S, --slot=NUM           Slot number (default is 0).

直接将 raw 格式的 super 镜像解包到指定的输出目录。

lpunpack 不能识别 sparse 镜像格式,所以如果 super.img 是 sparse 格式,则需要先将其转换成 raw 格式。

有两个可选参数"–partition" 和 “–slot”,分别用于指定提取镜像的分区名称(name)和槽位(slot),如果没有提供选项参数,则默认提取所有存在的分区镜像。

3. lpunpack 的用法

从上一节的帮助信息可以看到,lpunpack 的用法比较简单,主要分成两步:

  1. 将 sparse 格式的 super.img 转换成 raw 格式
  2. 提取 raw 格式的 super.img 内部的分区镜像

这里以 android-13.0.0_r41 代码编译参考设备 panther 得到的 super.img 为例演示 lpunpack 的操作

准备工作:

# 设置环境
$ source build/envsetup.sh 
$ lunch aosp_panther-userdebug

# 编译 dist 输出
$ make dist -j80

# 查找系统的 super.img 镜像
$ find out -type f -name super.img
out/target/product/panther/obj/PACKAGING/super.img_intermediates/super.img
out/dist/super.img

# 把 dist 下的 sparse 格式的 super.img 转换成 raw 格式
$ file out/dist/super.img 
out/dist/super.img: Android sparse image, version: 1.0, Total of 2082816 4096-byte output blocks in 159 input chunks.

# 使用 simg2img 将 sparse 格式转换成 raw 格式
$ simg2img out/dist/super.img super_raw.img 

# 查看 super 分区的信息
$ lpdump super_raw.img 
Slot 0:
Metadata version: 10.2
Metadata size: 1256 bytes
Metadata max size: 65536 bytes
Metadata slot count: 3
Header flags: virtual_ab_device
Partition table:
------------------------
  Name: system_a
  Group: google_dynamic_partitions_a
  Attributes: readonly
  Extents:
    0 .. 1732063 linear super 2048
------------------------
  Name: system_b
  Group: google_dynamic_partitions_b
  Attributes: readonly
  Extents:
    0 .. 53343 linear super 1734656
------------------------
  Name: system_dlkm_a
  Group: google_dynamic_partitions_a
  Attributes: readonly
  Extents:
    0 .. 679 linear super 1789952
------------------------
  Name: system_dlkm_b
  Group: google_dynamic_partitions_b
  Attributes: readonly
  Extents:
------------------------
  Name: system_ext_a
  Group: google_dynamic_partitions_a
  Attributes: readonly
  Extents:
    0 .. 588663 linear super 1792000
------------------------
  Name: system_ext_b
  Group: google_dynamic_partitions_b
  Attributes: readonly
  Extents:
------------------------
  Name: product_a
  Group: google_dynamic_partitions_a
  Attributes: readonly
  Extents:
    0 .. 718839 linear super 2381824
------------------------
  Name: product_b
  Group: google_dynamic_partitions_b
  Attributes: readonly
  Extents:
------------------------
  Name: vendor_a
  Group: google_dynamic_partitions_a
  Attributes: readonly
  Extents:
    0 .. 1214359 linear super 3100672
------------------------
  Name: vendor_b
  Group: google_dynamic_partitions_b
  Attributes: readonly
  Extents:
------------------------
  Name: vendor_dlkm_a
  Group: google_dynamic_partitions_a
  Attributes: readonly
  Extents:
    0 .. 84063 linear super 4315136
------------------------
  Name: vendor_dlkm_b
  Group: google_dynamic_partitions_b
  Attributes: readonly
  Extents:
------------------------
Super partition layout:
------------------------
super: 2048 .. 1734112: system_a (1732064 sectors)
super: 1734656 .. 1788000: system_b (53344 sectors)
super: 1789952 .. 1790632: system_dlkm_a (680 sectors)
super: 1792000 .. 2380664: system_ext_a (588664 sectors)
super: 2381824 .. 3100664: product_a (718840 sectors)
super: 3100672 .. 4315032: vendor_a (1214360 sectors)
super: 4315136 .. 4399200: vendor_dlkm_a (84064 sectors)
------------------------
Block device table:
------------------------
  Partition name: super
  First sector: 2048
  Size: 8531214336 bytes
  Flags: none
------------------------
Group table:
------------------------
  Name: default
  Maximum size: 0 bytes
  Flags: none
------------------------
  Name: google_dynamic_partitions_a
  Maximum size: 8527020032 bytes
  Flags: none
------------------------
  Name: google_dynamic_partitions_b
  Maximum size: 8527020032 bytes
  Flags: none
------------------------

总结下 super 分区内的镜像内容:

  • 槽位 A 中包含 system_a, system_dlkm_a, system_ext_a, product_a, vendor_a 和 vendor_dlkm_a 镜像
  • 槽位 B 中包含 system_b 镜像(大小和 system_a 的镜像不一样)

关于如何下载 Android 代码并基于 Google 官方的参考设备进行编译,请参考:

《如何下载和编译 Android 源码?》

3.1 解包所有镜像

不带参数解包所有分区镜像。

$ mkdir temp
$ lpunpack super_raw.img temp/
$ ls -lh temp/
total 2.1G
-rw-r--r-- 1 rocky users 351M Aug 29 14:09 product_a.img
-rw-r--r-- 1 rocky users    0 Aug 29 14:09 product_b.img
-rw-r--r-- 1 rocky users 846M Aug 29 14:09 system_a.img
-rw-r--r-- 1 rocky users  27M Aug 29 14:09 system_b.img
-rw-r--r-- 1 rocky users 340K Aug 29 14:09 system_dlkm_a.img
-rw-r--r-- 1 rocky users    0 Aug 29 14:09 system_dlkm_b.img
-rw-r--r-- 1 rocky users 288M Aug 29 14:09 system_ext_a.img
-rw-r--r-- 1 rocky users    0 Aug 29 14:09 system_ext_b.img
-rw-r--r-- 1 rocky users 593M Aug 29 14:09 vendor_a.img
-rw-r--r-- 1 rocky users    0 Aug 29 14:09 vendor_b.img
-rw-r--r-- 1 rocky users  42M Aug 29 14:09 vendor_dlkm_a.img
-rw-r--r-- 1 rocky users    0 Aug 29 14:09 vendor_dlkm_b.img

3.2 解包指定名称分区镜像

使用 “-p” 参数解包单个分区 system_a 的镜像:

$ rm -rf temp/*
$ lpunpack -p system_a super_raw.img temp/
$ ls -lh temp/
total 843M
-rw-r--r-- 1 rocky users 846M Aug 30 19:48 system_a.img

使用多个 “-p” 参数解包多个分区(system_a 和 vendor_a)镜像:

$ rm -rf temp/*
$ lpunpack -p system_a -p vendor_a super_raw.img temp/
$ ls -lh temp/
total 1.5G
-rw-r--r-- 1 rocky users 846M Aug 30 19:50 system_a.img
-rw-r--r-- 1 rocky users 593M Aug 30 19:50 vendor_a.img

3.3 解包指定槽位分区镜像

根据 help 信息,可以使用 “-S”/“–slot” 选项指定槽位,解包单个槽位镜像。

但必须吐槽一下这个选项,Bug 丛生。

槽点 1:使用 “-S”(大写) 选项,提示不认识 “-S” 选项

android-13.0.0_r41$ lpunpack -S 1 super_raw.img temp
lpunpack: unrecognized option '-S'
Unrecognized argument.
lpunpack - command-line tool for extracting partition images from super

Usage:
  lpunpack [options...] SUPER_IMAGE [OUTPUT_DIR]

Options:
  -p, --partition=NAME     Extract the named partition. This can
                           be specified multiple times.
  -S, --slot=NUM           Slot number (default is 0).

槽点 2:使用 “-s”(小写) 选项,提示不认识的参数

android-13.0.0_r41$ lpunpack -s 1 super_raw.img temp
Unrecognized command-line arguments.
lpunpack - command-line tool for extracting partition images from super

Usage:
  lpunpack [options...] SUPER_IMAGE [OUTPUT_DIR]

Options:
  -p, --partition=NAME     Extract the named partition. This can
                           be specified multiple times.
  -S, --slot=NUM           Slot number (default is 0).

槽点 3: 使用 “–slot 1” 选项提取槽位 1 的分区镜像,但实际上两个槽位的 image 都提取出来了

android-13.0.0_r41$ rm -rf temp/*
android-13.0.0_r41$ lpunpack --slot 1 super_raw.img temp/
android-13.0.0_r41$ ls -lh temp/
total 2.1G
-rw-r--r-- 1 rocky users 351M Aug 31 10:11 product_a.img
-rw-r--r-- 1 rocky users    0 Aug 31 10:11 product_b.img
-rw-r--r-- 1 rocky users 846M Aug 31 10:11 system_a.img
-rw-r--r-- 1 rocky users  27M Aug 31 10:11 system_b.img
-rw-r--r-- 1 rocky users 340K Aug 31 10:11 system_dlkm_a.img
-rw-r--r-- 1 rocky users    0 Aug 31 10:11 system_dlkm_b.img
-rw-r--r-- 1 rocky users 288M Aug 31 10:11 system_ext_a.img
-rw-r--r-- 1 rocky users    0 Aug 31 10:11 system_ext_b.img
-rw-r--r-- 1 rocky users 593M Aug 31 10:11 vendor_a.img
-rw-r--r-- 1 rocky users    0 Aug 31 10:11 vendor_b.img
-rw-r--r-- 1 rocky users  42M Aug 31 10:11 vendor_dlkm_a.img
-rw-r--r-- 1 rocky users    0 Aug 31 10:11 vendor_dlkm_b.img

没想到这个 lpunpack 工具已经提供好几年了,lpunpack 竟然还有问题。

好吧,那就只能去改下代码了。

如果你需要指定槽位操作的话,可能需要修改下代码,以下代码仅供参考:

android-13.0.0_r41$ repo diff system/extras/partition_tools/lpunpack.cc 
project system/extras/
diff --git a/partition_tools/lpunpack.cc b/partition_tools/lpunpack.cc
index 1f870c5d..951572d4 100644
--- a/partition_tools/lpunpack.cc
+++ b/partition_tools/lpunpack.cc
@@ -84,7 +84,7 @@ static int usage(int /* argc */, char* argv[]) {
             "Options:\n"
             "  -p, --partition=NAME     Extract the named partition. This can\n"
             "                           be specified multiple times.\n"
-            "  -S, --slot=NUM           Slot number (default is 0).\n",
+            "  -s, --slot=NUM           Slot number (default is 0).\n",
             argv[0], argv[0]);
     return EX_USAGE;
 }
@@ -93,7 +93,7 @@ int main(int argc, char* argv[]) {
     // clang-format off
     struct option options[] = {
         { "partition",  required_argument,  nullptr, 'p' },
-        { "slot",       required_argument,  nullptr, 'S' },
+        { "slot",       required_argument,  nullptr, 's' },
         { nullptr,      0,                  nullptr, 0 },
     };
     // clang-format on
@@ -102,7 +102,7 @@ int main(int argc, char* argv[]) {
     std::unordered_set<std::string> partitions;
 
     int rv, index;
-    while ((rv = getopt_long_only(argc, argv, "+p:sh", options, &index)) != -1) {
+    while ((rv = getopt_long_only(argc, argv, "+p:s:h", options, &index)) != -1) {
         switch (rv) {
             case 'h':
                 usage(argc, argv);
@@ -110,7 +110,7 @@ int main(int argc, char* argv[]) {
             case '?':
                 std::cerr << "Unrecognized argument.\n";
                 return usage(argc, argv);
-            case 'S':
+            case 's':
                 if (!android::base::ParseUint(optarg, &slot_num)) {
                     std::cerr << "Slot must be a valid unsigned number.\n";
                     return usage(argc, argv);

从代码中可以看到,如果指定了 slot,实际上是从 super 中读取该 slot 对应的 metadata,然后使用这个 metadata 提取分区镜像。

由于读取到的 metadata 实际上还是包含了 a, b 两个槽位的信息,所以看起来即使指定了 “-s” 选项,但没什么作用,除非读取到的 metadata 只有 b 槽位的信息。

关于 metadata 的布局,请参考《Android 动态分区详解(一) 5 张图让你搞懂动态分区原理》中的下图:

安卓动态分区布局

好了,说了一大堆废话。

总结起来一句话就是:lpunpack 的 “-S”/“–slot” 选项有问题,没事就少用。使用的话,要慎重。

4. 其它

  • 到目前为止,我写过 Android OTA 升级相关的话题包括:
    • 基础入门:《Android A/B 系统》系列
    • 核心模块:《Android Update Engine 分析》 系列
    • 动态分区:《Android 动态分区》 系列
    • 虚拟 A/B:《Android 虚拟 A/B 分区》系列
    • 升级工具:《Android OTA 相关工具》系列

更多这些关于 Android OTA 升级相关文章的内容,请参考《Android OTA 升级系列专栏文章导读》。

如果您已经订阅了动态分区和虚拟分区付费专栏,请务必加我微信,备注订阅账号,拉您进“动态分区 & 虚拟分区专栏 VIP 答疑群”。我会在方便的时候,回答大家关于 A/B 系统、动态分区、虚拟分区、各种 OTA 升级和签名的问题,此群仅限专栏订阅者参与~

除此之外,我有一个 Android OTA 升级讨论群,里面现在有 400+ 朋友,主要讨论手机,车机,电视,机顶盒,平板等各种设备的 OTA 升级话题,如果您从事 OTA 升级工作,欢迎加群一起交流,请在加我微信时注明“Android OTA 讨论组”。此群仅限 Android OTA 开发者参与~

公众号“洛奇看世界”后台回复“wx”获取个人微信。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/953281.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数字证书UKey太多怎么管理,一分钟轻松学会

公司数字证书Ukey太多&#xff0c;管理乱&#xff0c;也不能满足异地使用需求&#xff0c;怎么办&#xff1f; 一台USB Sever就能解决&#xff01; 第一步 USB Sever有2口、8口、24口多种规格&#xff0c; 根据你的数字证书Ukey数量选择合适的规格。 第二步 把Ukey全部插上…

韶音骨传导耳机好不好,韶音骨传导耳机值得入手吗

韶音耳机的质量还是很不错的&#xff0c;其实力相比于百元价位的耳机而言领先了不少&#xff0c;具备多种功能&#xff0c;佩戴起来也是有着舒适性。它自主研发了骨传导音频技术&#xff0c;不过在今年开始&#xff0c;似乎已经将方向开始往运动偏移。 而在韶音的骨传导耳机中&…

PHY 芯片接口直连(不使用变压器)的设计

1.基础知识 网口 PHY 芯片对 TX 和 RX 信号有两种驱动方式&#xff1a;电压驱动和电流驱动。不同的驱动方式决定了 PHY 在与变压器连接的时候&#xff0c;变压器的中心抽头的接法。电压型中心抽头通过电容接到地&#xff0c;电流型中心抽头上拉至VDD&#xff0c;VDD为PHY的供电…

【问题】jdk20执行虚拟线程报错解决方案

java: ofPlatform() 是预览 API&#xff0c;默认情况下处于禁用状态。 &#xff08;请使用 --enable-preview 以启用预览 API&#xff09; 在idea增加配置 编辑配置 增加jvm参数 然后去执行 public static void main(String[] args) {int 次数 100000;System.out.println(&q…

智汇云舟亮相中国安防工程商集成商大会

智汇云舟亮相中国安防工程商集成商大会&#xff0c;以视频孪生驱动安防行业数字化转型 近日&#xff0c;由中国安全防范产品行业协会指导&#xff0c;永泰传媒主办的中国安防工程商&#xff08;系统集成商&#xff09;大会暨第69届中国安防新产品、新技术成果展示在石家庄圆满…

轻松解决Mac和Windows中Unity汉化问题

本文是参考https://blog.csdn.net/ChinarCSDN/article/details/83213739该文写的。 上述作者是针对windows平台来写的&#xff0c;亲测方法可用。文中有中文字体包&#xff0c;可自行下载。 其他中文包下载地址&#xff1a; https://new-translate.unity3d.jp/v1/live/54/XX…

Kafka系列五消费者

文章首发于个人博客&#xff0c;欢迎访问关注&#xff1a;https://www.lin2j.tech 与生产者对应的是消费者&#xff0c;应用程序可以通过 KafkaConsumer 来订阅主题&#xff0c;从主题中拉取消息。 使用 KafkaConsumer 之前需要先理解消费者和消费者组的概念。 消费者与消费…

性能提升3-4倍!贝壳基于Flink + OceanBase的实时维表服务

作者介绍&#xff1a;肖赞&#xff0c;贝壳找房&#xff08;北京&#xff09;科技有限公司 OLAP 平台负责人&#xff0c;基础研发线大数据平台部架构师。 贝壳找房是中国最大的居住服务平台。作为居住产业数字化服务平台&#xff0c;贝壳致力于推进居住服务的产业数字化、智能…

〔020〕Stable Diffusion 之 骨骼姿势 篇

✨ 目录 &#x1f388; 姿势检测 / OpenPose&#x1f388; 姿势检测 OpenPose 参数介绍&#x1f388; 姿势检测 OpenPose 基本使用&#x1f388; 深度库 / Depth Lib&#x1f388; 深度库 Depth Lib 参数介绍&#x1f388; 3D姿势检测 / 3D Openpose Editor&#x1f388; 3D姿势…

安达发|2024年,APS计划排产技术应运而生

随着制造业竞争加剧&#xff0c;企业如何提高生产效率、降低成本、满足客户需求成为关键。2024年&#xff0c;APS计划排产技术应运而生&#xff0c;通过实施APS自动排程&#xff0c;企业将获得诸多效益。 近年来&#xff0c;全球制造业正面临着原材料价格波动、人工成本上升、环…

图像化数据库工具DBeaver远程连接云服务器的MySQL数据库

一、安装宝塔面板 使用xshell、electerm、SecureCRT等远程终端连接登陆上云服务器&#xff0c;在Linux宝塔面板使用脚本安装 安装后&#xff0c;如下图&#xff1a;按照提示&#xff0c;在云服务器防火墙/安全组放行Linux宝塔面板的端口 在浏览器打开上述网址&#xff0c;…

命令行发邮件

第一步&#xff1a;打开windows命令行&#xff0c;运行如下代码 telnet smtp.qiye.163.com 25 返回如下结果代表成功&#xff1a; 返回220代码就可以继续操作了.... 第二步&#xff1a;执行ehlo xxx&#xff08;xxx随便写&#xff0c;就是个名称而已&#xff09; 第三步&a…

Kubernetes技术--使用kubeadm搭建高可用的K8s集群(贴近实际环境)

1.高可用k8s集群架构(多master) 2.安装硬件要求 一台或多台机器,操作系统 CentOS7.x-86_x64 硬件配置:2GB或更多RAM,2个CPU或更多CPU,硬盘30GB或更多 注: 这里属于教学环境,所以使用三台虚拟机模拟实现。 3.部署规划 4.部署前准备 (1).关闭防火墙 systemctl stop fi…

计算机网络 第二节

目录 一&#xff0c;计算机网络的分类 1.按照覆盖范围分 2.按照所属用途分 二&#xff0c;计算机网络逻辑组成部分 1.核心部分 &#xff08;通信子网&#xff09; 1.1电路交换 1.2 分组交换 两种方式的特点 重点 2.边缘部分 &#xff08;资源子网&#xff09; 进程通信的方…

无涯教程-Android - Spinner函数

Spinner允许您从下拉菜单中选择一个项目 例如。使用Gmail应用程序时,将显示如下所示的下拉菜单,您需要从下拉菜单中选择一个项目。 Spinner Example 示例 本示例演示计算机的类别,您需要从类别中选择一个类别。 以下是修改后的主要Activity文件src/com.example.spinner/Andr…

非科班菜鸡算法学习记录 | 代码随想录算法训练营第51天||309.最佳买卖股票时机含冷冻期 714.买卖股票的最佳时机含手续费 股票总结

309.最佳买卖股票时机含冷冻期 309. Best Time to Buy and Sell Stock with Cooldown(英文力扣连接) 知识点&#xff1a;动规 状态&#xff1a;看思路ok 思路&#xff1a; 四个状态需要想&#xff0c;持有/不持有且过了冷却期/当天卖/正处于冷却期&#xff1b; 具体看注释…

USB WLAN Dongle with AutoInstall

一、什么是Autoinstall USB WLAN dongle. 正常要使用USB 网卡时&#xff0c;都需要使用专门的驱动程式才能将产品驱动起来做使用。通常要安装驱动程式的方式&#xff0c;往往都是上购买品牌商的网页下载驱动&#xff0c;或是利用购买USB网卡附赠的光碟来进行安装。而所谓的Auto…

祝贺!Databend Cloud 和阿里云 PolarDB 达成认证

近日&#xff0c;北京数变科技有限公司旗下产品与阿里云 PolarDB 开源数据库社区展开产品集成认证。 测试结果表明&#xff0c;北京数变科技有限公司旗下产品《Databend Cloud&#xff08;V1.25&#xff09;》正式通过了《阿里云 PolarDB 数据库管理软件》的技术认证&#xff…

企业电子招投标采购系统源码之电子招投标的组成

营造全面规范安全的电子招投标环境&#xff0c;促进招投标市场健康可持续发展 传统采购模式面临的挑战 一、立项管理 1、招标立项申请 功能点&#xff1a;招标类项目立项申请入口&#xff0c;用户可以保存为草稿&#xff0c;提交。 2、非招标立项申请 功能点&#xff1a;非招标…

嵌入式学习笔记(7)ARM汇编指令4-多寄存器指令

多寄存器访问指令 ldr/str每周期只能访问4字节内存&#xff0c;如果需要批量读取、写入内存的话太慢&#xff0c;解决方案就是ldm/stm&#xff0c;ldm(load register multiple)&#xff0c;stm(store register multiple) 举例&#xff1a; stmia sp, {r0 - r12} 将r0存入sp指…