Native开辟工具之CPU 和架构(三)(1)
您仍可使用这些扩展指令集,只要您使用运行时功能探测来启用它们,而且为不支持它们的设备提供回退机制。请参阅以下文档相识更多详情:
[*] 不同 C++ 编译器和操作系统的调用规范
[*] Intel64 和 IA-32 架构软件开辟者手册第 2 卷:指令集参考
[*] Intel64 和 IA-32 Intel 架构软件开辟者手册第 3 卷:系统编程
为特定 ABI 天生代码
默认情况下,NDK 指向所有非弃用 ABI。您可通过在 Application.mk 文件中设置 APP_ABI 来指向单个 ABI。以下代码段演示了使用 APP_ABI 的几个示例
APP_ABI := arm64-v8a # Target only arm64-v8a
APP_ABI := all # Target all ABIs, including those that are deprecated.
APP_ABI := armeabi-v7a x86_64 # Target only armeabi-v7a and x86_64.
要详细相识您可以为 APP_ABI 变量指定的值,请参阅 Android.mk。
编译系统的默认举动是将每个 ABI 的二进制文件包罗在单个 APK(也称为胖 APK)内。与仅包含单个 ABI 的二进制文件的 APK 相比,胖 APK 要大得多;要权衡的是兼容性更广,但 APK 更大。强烈建议您使用拆分 APK 减小 APK 的大小,同时仍保持最大限度的设备兼容性。
在安装时,软件包管理器只解压缩最适合目标设备的机器代码。详情请参阅安装时自动解压缩原生代码。
Android 平台上的 ABI 管理
本部分详细阐明白 Android 平台怎样管理 APK 中的原生代码。
应用软件包中的原生代码
Play 商店和软件包管理器都盼望能在 APK 中符合以下格式的文件路径上找到 NDK 天生的库:
/lib//lib.so
此中,<abi> 是支持的 ABI 下列出的 ABI 名称之一,<name> 是您为 Android.mk 文件中的 LOCAL_MODULE 变量定义库时使用的库名称。由于 APK 文件只是 zip 文件,因此打开它们并确认共享原生库位于该位于的位置很简单。
假如系统在预期位置找不到原生共享库,便无法使用它们。在这种情况下,应用本身必须复制这些库,然后执行 dlopen()。
在胖 APK 中,每个库位于名称与相应 ABI 匹配的目次下。例如,胖 APK 大概包含:
/lib/armeabi/libfoo.so
/lib/armeabi-v7a/libfoo.so
/lib/arm64-v8a/libfoo.so
/lib/x86/libfoo.so
/lib/x86_64/libfoo.so
留意:搭载 4.0.3 或更早版本、基于 ARMv7 的 Android 设备从 armeabi 目次(而非 armeabi-v7a 目次,假如两个目次都存在)安装原生库。这是由于在 APK 中,/lib/armeabi/ 在 /lib/armeabi-v7a/ 后面。从 4.0.4 开始,此题目已修复。
Android 平台 ABI 支持
Android 系统在运行时知道它支持哪些 ABI,由于版本特定的系统属性会指示:
[*] 设备的重要 ABI,与系统映像本身使用的机器代码对应。
[*] (可选)与系统映像也支持的其他 ABI 对应的辅助 ABI。
此机制确保系统在安装时从软件包提取最佳机器代码。
为实现最佳性能,应直接针对重要 ABI 进行编译。例如,基于 ARMv5TE 的典范设备只会定义重要 ABI:armeabi。相反,基于 ARMv7 的典范设备将重要 ABI 定义为 armeabi-v7a,并将辅助 ABI 定义为 armeabi,由于它可以运举动每个 ABI 天生的应用原生二进制文件。
64 位设备也支持其 32 位变体。以 arm64-v8a 设备为例,该设备也可以运行 armeabi 和 armeabi-v7a 代码。但请留意,假如应用以 arm64-v8a 为目标,而非依赖于运行 armeabi-v7a 版应用的设备,应用在 64 位设备上的性能要好得多。
许多基于 x86 的设备也可运行 armeabi-v7a 和 armeabi NDK 二进制文件。对于这些设备,重要 ABI 将是 x86,辅助 ABI 是 armeabi-v7a。
安装时自动解压缩原生代码
安装应用时,软件包管理器服务将扫描 APK,并查找以下形式的任何共享库:
lib//lib.so
假如未找到,而且您已定义辅助 ABI,该服务将扫描以下形式的共享库:
lib//lib.so
找到所需的库时,软件包管理器会将它们复制到应用的 data 目次 (data/data/<package_name>/lib/) 下的 /lib/lib<name>.so。
假如根本没有共享对象文件,应用也会编译并安装,但在运行时会崩溃。
三、处理CPU功能
=========
ABI:使用预处理器的预定义宏
通常,在编译时使用 #ifdef 及以下各项确定 ABI 最为方便:
[*] 对于 32 位 ARM,使用 __arm__
[*] 对于 64 位 ARM,使用 __aarch64__
[*] 对于 32 位 X86,使用 __i386__
[*] 对于 64 位 X86,使用 __x86_64__
请留意:32 位 X86 称为 __i386__,而不是 __x86__,这大概与您预想的有所不同!
CPU 核心计数:使用 libc 的 sysconf(3)
sysconf(3) 既可以查询 _SC_NPROCESSORS_CONF(系统中的 CPU 核心数),又可以查询 _SC_NPROCESSORS_ONLN(当前在线的 CPU 核心数)。
功能:使用 libc 的 getauxval(3)
自 API 级别 18 开始,Android 的 C 库开始支持 etauxval(3)。AT_HWCAP 和 AT_HWCAP2 参数会返回列出特定 CPU 功能的位掩码。请参阅 NDK 中的各种 hwcap.h 头文件以获取要进行比较的常量,如用于 arm64 SHA512 指令的 HWCAP_SHA512,或用于 arm Thumb 整数除法指令的 HWCAP_IDIVT。
Google cpu_features 库
AT_HWCAP 的一个题目是偶尔设备会出错。例如,一些旧设备宣称拥有整数除法指令,但实际上并没有。
Google 的 cpu_features 库依附其对特定 SoC 的相识(通过解析 /proc/cpuinfo 掌握相应的 SoC),解决了此类题目。
另一个解决方案是为 SIGILL 安装信号处理步伐,然后直接实验执行相关指令。例如,BoringSSL/OpenSSL 使用的就是这种方法。
NDK cpufeatures 库
NDK 提供了一个名为 cpufeatures 的小型库,其功能类似于 getauxval(3),但它也可用于 API 级别 18 之前的版本。与其他 cpu_features 库不同,它对特定 SoC 并无额外的相识。
NDK cpufeatures API
uint64_t android_getCpuFeatures();
返回一组位标志,每个标志代表一个 CPU 系列特定的功能。本部分其余内容介绍了各个系列的功能。
32 位 ARM CPU 系列
以下标志实用于 32 位 ARM CPU 系列:
ANDROID_CPU_ARM_FEATURE_VFPv2
表示设备的 CPU 支持 VFPv2 指令集。大多数 ARMv6 CPU 都支持此指令集。
ANDROID_CPU_ARM_FEATURE_ARMv7
表示设备的 CPU 支持 armeabi-v7a ABI 所支持的 ARMv7-A 指令集。此指令集同时支持 Thumb-2 和 VFPv3-D16 指令。此返回值还表示支持 VFPv3 硬件 FPU 扩展指令集。
ANDROID_CPU_ARM_FEATURE_VFPv3
表示设备的 CPU 支持 VFPv3 硬件 FPU 扩展指令集。
此值等同于 VFPv3-D16 指令集,后者只提供 16 个硬件双精度 FP 寄存器。
ANDROID_CPU_ARM_FEATURE_VFP_D32
表示设备的 CPU 支持 32 个(而不是 16 个)硬件双精度 FP 寄存器。即使有 32 个硬件双精度 FP 寄存器,也只有 32 个单精度寄存器映射至同一寄存器组。
ANDROID_CPU_ARM_FEATURE_NEON
表示设备的 CPU 支持 ARM Advanced SIMD (NEON) 向量扩展指令集。请留意,ARM 要求这些 CPU 也要实现 VFPv3-D32,VFPv3-D32 提供 32 个硬件 FP 寄存器(与 NEON 单元共享)。
ANDROID_CPU_ARM_FEATURE_VFP_FP16
表示设备的 CPU 支持在 16 位寄存器上执行浮点运算的指令。该功能是 VFPv4 规范的一部分。
ANDROID_CPU_ARM_FEATURE_VFP_FMA
表示设备的 CPU 支持 VFP 指令集的融合乘积累加扩展指令集。它也是 VFPv4 规范的一部分。
ANDROID_CPU_ARM_FEATURE_NEON_FMA
表示设备的 CPU 支持 NEON 指令集的融合乘积累加扩展指令集。它也是 VFPv4 规范的一部分。
ANDROID_CPU_ARM_FEATURE_IDIV_ARM
表示设备的 CPU 在 ARM 模式下支持整数除法。仅实用于更高型号的 CPU,例如 Cortex-A15。
ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2
表示设备的 CPU 在 Thumb-2 模式下支持整数除法。仅实用于更高型号的 CPU,例如 Cortex-A15。
ANDROID_CPU_ARM_FEATURE_iWMMXt
表示设备的 CPU 支持可添加 MMX 寄存器和指令的扩展指令集。该功能仅实用于少数几个基于 XScale 的 CPU。
ANDROID_CPU_ARM_FEATURE_LDREX_STREX
表示设备的 CPU 支持自 ARMv6 后可用的 LDREX 和 STREX 指令。借助专用监督器,这些指令联合起来可在内存中提供原子级更新。
64 位 ARM CPU 系列
以下标志实用于 64 位 ARM CPU 系列:
ANDROID_CPU_ARM64_FEATURE_FP
表示设备的 CPU 具有浮点单元 (FPU)。所有 Android ARM64 设备都必须支持此功能。
ANDROID_CPU_ARM64_FEATURE_ASIMD
表示设备的 CPU 具有高级 SIMD (ASIMD) 单元。所有 Android ARM64 设备都必须支持此功能。
ANDROID_CPU_ARM64_FEATURE_AES
表示设备的 CPU 支持 AES 指令。
ANDROID_CPU_ARM64_FEATURE_CRC32
表示设备的 CPU 支持 CRC32 指令。
ANDROID_CPU_ARM64_FEATURE_SHA1
表示设备的 CPU 支持 SHA1 指令。
ANDROID_CPU_ARM64_FEATURE_SHA2
表示设备的 CPU 支持 SHA2 指令。
ANDROID_CPU_ARM64_FEATURE_PMULL
表示设备的 CPU 支持 64 位 PMULL 和 PMULL2 指令。
32 位 x86 CPU 系列
以下标志实用于 32 位 x86 CPU 系列。
ANDROID_CPU_X86_FEATURE_SSSE3
表示设备的 CPU 支持 SSSE3 扩展指令集。
ANDROID_CPU_X86_FEATURE_POPCNT
表示设备的 CPU 支持 POPCNT 指令。
ANDROID_CPU_X86_FEATURE_MOVBE
表示设备的 CPU 支持 MOVBE 指令。此指令特定于某些 Intel IA-32 CPU,例如 Atom。
android_getCpuFeatures() 针对没有列出扩展指令集的 CPU 系列返回 0。
int android_getCpuCount(void);
留意:考虑到 sysconf(3),此函数通常无用。
返回系统中的 CPU 核心数,大概高于实际在线的核心数。
AndroidCpuFamily android_getCpuFamily();
留意:考虑到预处理器宏,此函数通常无用。
返回以下此中一个常量,代表设备所支持的 CPU 系列/架构:
[*] ANDROID_CPU_FAMILY_ARM
[*] ANDROID_CPU_FAMILY_X86
[*] ANDROID_CPU_FAMILY_ARM64
[*] ANDROID_CPU_FAMILY_X86_64
对于 64 位系统上的 32 位可执行文件,此函数返回 32 位架构。
将 NDK cpufeatures 与 ndk-build 搭配使用
cpufeatures 库可用作导入模块。要使用此库,请执行以下操作:
[*] 将 cpufeatures 添加到 LOCAL_STATIC_LIBRARIES。
[*] 在源代码中 #include <cpu-features.h>。
[*] 在 Android.mk 的末尾导入 android/cpufeatures。
以下是导入 cpufeatures 的 Android.mk 示例:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := your-module-name
LOCAL_SRC_FILES := …
LOCAL_STATIC_LIBRARIES := cpufeatures
include $(BUILD_SHARED_LIBRARY)
$(call import-module,android/cpufeatures)
三、NEON 支持
=========
NDK 支持 ARM Advanced SIMD(通常称为 NEON),一种实用于 ARMv7 和 ARMv8 的可选扩展指令集。NEON 提供标量/向量指令和寄存器(与 FPU 共享),堪比 x86 中的 MMX/SSE/3DNow!。要运行 NEON,必要 VFPv3-D32(32 个硬件 FPU 64 位寄存器,而非最小值 16 个)。
并非所有基于 ARMv7 的 Android 设备都支持 NEON,但支持的设备大概会因 NEON 支持标量/向量指令而受益匪浅。
NDK 支持模块编译,乃至可以编译支持 NEON 的特定源文件。
使用 LOCAL_ARM_NEON
要让 NDK 编译支持 NEON 的所有源文件,请在模块定义中添加以下代码行:makefile LOCAL_ARM_NEON := true
假如您要编译仅包含 NEON 代码的静态或共享库,编译支持 NEON 的所有源文件大概特殊有用。
使用 .neon 后缀
为 LOCAL_SRC_FILES 变量列出源文件时,您可以选择使用 .neon 后缀表示要编译支持 NEON 的单个文件。例如,以下示例会编译一个支持 NEON 的文件 (foo.c),以及另一个不支持 NEON 的文件 (bar.c):makefile LOCAL_SRC_FILES := foo.c.neon bar.c
您可将 .neon 后缀与 .arm 后缀相联合,后者指定用于非 NEON 指令的 32 位 ARM 指令集(而非 Thumb2)。在这种情况下,.arm 必须在 .neon 之前。例如:foo.c.arm.neon 可行,但 foo.c.neon.arm 不可行。
编译要求
NEON 支持实用于 ( ) 和 ( ) ABI。假如 NDK 编译脚本在实验借助 NEON 支持进行编译时遇到其他 ABI,NDK 编译脚本将退出。务必在 Android.mk 文件中添加如下查抄代码:
define a static library containing our NEON code
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
include $(CLEAR_VARS)
LOCAL_MODULE := mylib-neon
LOCAL_SRC_FILES := mylib-neon.c
LOCAL_ARM_NEON := true
include $(BUILD_STATIC_LIBRARY)
endif # TARGET_ARCH_ABI == armeabi-v7a
运行时检测
应用必须执行运行时检测,以确认支持 NEON 的机器代码能够在目标设备上运行。这是由于并非所有基于 ARMv7 的 Android 设备都支持 NEON。应用可以使用 NDK 随附的 cpufeatures 库执行此查抄。
您应查抄 android_getCpuFamily() 是否返回 ANDROID_CPU_FAMILY_ARM,以及 android_getCpuFeatures() 是否返回一个值,而且设置了 ANDROID_CPU_ARM_FEATURE_NEON 标志。例如:
#include <cpu-features.h>
…
…
if (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM &&
(android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0)
{
// use NEON-optimized routines
…
}
else
{
// use non-NEON fallback routines instead
…
}
…
对 x86 提供跨平台支持
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己探索成长大概是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学结果低效又漫长,而且极易碰到天花板技能故步自封!
因此网络整理了一份《2024年Android移动开辟全套学习资料》,初衷也很简单,就是盼望能够资助到想自学提升又不知道该从何学起的朋友,同时减轻各人的负担。
https://img-blog.csdnimg.cn/img_convert/378b780976df2207c7ae28b806f8b652.png
https://img-blog.csdnimg.cn/img_convert/bc2ea14f7d64eb6a79a3792074fe017e.png
https://img-blog.csdnimg.cn/img_convert/2bd982e53af26d79454e83f884741fed.png
https://img-blog.csdnimg.cn/img_convert/6cfe7691c8d0e659bc53be74fb9cfa4f.png
https://img-blog.csdnimg.cn/img_convert/1ffcf535acd97b706d8eb744104c7ee0.png
既有适合小白学习的零基础资料,也有适合3年以上经验的小同伴深入学习提升的进阶课程,基本涵盖了95%以上Android开辟知识点,真正体系化!
由于文件比较大,这里只是将部分目次截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、解说视频,而且会连续更新!
假如你觉得这些内容对你有资助,可以扫码获取!!(备注:Android)
https://img-blog.csdnimg.cn/img_convert/ac86bbcb823ea12cf93aa8df20b2f37e.jpeg 最后
对于步伐员来说,要学习的知识内容、技能有太多太多,要想不被情况镌汰就只有不停提升自己,从来都是我们去顺应情况,而不是情况来顺应我们!
最后,我再重复一次,假如你想成为一个优秀的 Android 开辟职员,请会合精力,对基础和重要的事情做深度研究。
对于许多初中级Android工程师而言,想要提升技能,往往是自己探索成长,不成体系的学习结果低效漫长且无助。整理的这些架构技能盼望对Android开辟的朋友们有所参考以及少走弯路,本文的重点是你有没有劳绩与成长,其余的都不重要,盼望读者们能谨记这一点。
为了各人能够顺遂进阶中高级、架构师,我特地为各人准备了一套高手学习的源码和框架视频等精品Android架构师教程,包管你学了以后包管薪资上升一个台阶。
以下是本日给各人分享的一些独家干货:
https://img-blog.csdnimg.cn/img_convert/82b3bdfff1d2566f48f329fa81670ab4.webp?x-oss-process=image/format,png
《互联网大厂口试真题解析、进阶开辟核心学习笔记、全套解说视频、实战项目源码讲义》点击传送门即可获取!
roid)**
https://img-blog.csdnimg.cn/img_convert/ac86bbcb823ea12cf93aa8df20b2f37e.jpeg 最后
对于步伐员来说,要学习的知识内容、技能有太多太多,要想不被情况镌汰就只有不停提升自己,从来都是我们去顺应情况,而不是情况来顺应我们!
最后,我再重复一次,假如你想成为一个优秀的 Android 开辟职员,请会合精力,对基础和重要的事情做深度研究。
对于许多初中级Android工程师而言,想要提升技能,往往是自己探索成长,不成体系的学习结果低效漫长且无助。整理的这些架构技能盼望对Android开辟的朋友们有所参考以及少走弯路,本文的重点是你有没有劳绩与成长,其余的都不重要,盼望读者们能谨记这一点。
为了各人能够顺遂进阶中高级、架构师,我特地为各人准备了一套高手学习的源码和框架视频等精品Android架构师教程,包管你学了以后包管薪资上升一个台阶。
以下是本日给各人分享的一些独家干货:
[外链图片转存中…(img-n8tkkSfa-1713397147580)]
《互联网大厂口试真题解析、进阶开辟核心学习笔记、全套解说视频、实战项目源码讲义》点击传送门即可获取!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]