android-ndk开发(9): undefined reference to `__aarch64_ldadd4_acq_rel` 报错分析

1. 概要

基础库 libbase.a 基于 android ndk r18b 编译, 被算法库 libfoo.so 和算法库 libbar.a 依赖, 算法库则分别被 libapp1.so 和 libapp2.so 依赖。

libapp1.so 的开发者向 libfoo.so 的开发者反馈了链接报错:

error: undefined symbol: __aarch64_ldadd4_acq_rel

libapp2.so 的开发者向 libbar.a 的开发者反馈了链接报错:

undefined reference to `__aarch64_ldadd4_acq_rel'

libapp2.so 的开发者通过升级 NDK 版本解决了问题。 由于没有 libbar.a 和 libapp2.so 对应的 ndk 详细版本和最小复现代码,

在搜索引擎上以 “undefined reference to `__aarch64_ldadd4_acq_rel’” 为关键字找到相似案例并分析, 确定了升级 NDK 能消除上述链接报错的原因。

2. opencv issue 24856

2.1 报错描述

https://github.com/opencv/opencv/issues/24856 (参考链接[1])

用户 kevinzezel 在 android 平台, 使用 OpenCV 4.9.0 + ncnn, 遇到链接报错, 而使用 OpenCV 4.5.5~4.8.0 时没有报错。 报错信息是:

undefined reference to `__aarch64_ldadd8_acq_rel’

opencv 维护人员 asmorkalov 提议让用户开启 -mno-outline-atomics 编译选项, 用户 kevinzezel 反馈说 ndk 21.4.7075529 不支持这一选项.

catboost 维护者 andrey-khropov 说, 很可能是 clang 13.0.0-rc1 版本引入的问题, 对应到 commit: https://github.com/llvm/llvm-project/commit/c5e7e649d537067dec7111f3de1430d0fc8a4d11 (参考链接[2])。 解决方法: 要么使用低版本 clang (< 13.0.0), 要么用高版本 clang 但是传入 -mno-outline-atomics 选项。

用户 bvnp43 说, 升级 ndk 到 26.2.11394342 后问题解决。

用户 chenyan-master 说, 编译 V8 时遇到类似问题 (ndk 22.0.7026061), 升级 ndk 版本解决了。

2.2 分析

ncnn 版本没有被提及, 大概率是没有变化。 OpenCV 从 4.8.0 到 4.9.0 引发了链接报错, 猜测是因为 OpenCV 官方编译 android sdk 时更换了 ndk 版本, 而 ndk 版本和 clang 版本有一定的对应关系。

https://stackoverflow.com/questions/53385892/find-the-ndk-version-used-for-building-opencv-android-native-libraries (参考链接[3]) 给出了从 OpenCV 静态库反推编译他它的 ndk 版本、 ndk 里的 clang 版本的方法:

strings libopencv_core.a | ag "Android NDK"
strings libopencv_core.a | ag "C++ Compiler"
opencvndkclang
4.9.025.2.951965314.0.7
4.8.018.1.50630457.0.2

ndk 和 clang 的版本对应关系, 则通过 clang++ --version 确定, e.g.

D:/soft/android-ndk/r21e/toolchains/llvm/prebuilt/windows-x86_64/bin/clang++ --version

ndk 的数字形式的 x.y.z 版本, 和字母形式的版本如 r21e, 则通过 <ndk-目录>/source.properties 确定, 例如 ndk-r21e:

Pkg.Desc = Android NDK
Pkg.Revision = 21.4.7075529

整理后的 ndk 和 clang 版本对应关系如下:

ndkndkclang
r28b28.1.1335670919.0.0
r27c27.2.1247901818.0.3
r26d26.3.1157926417.0.2
r25c25.2.951965314.0.7
r25b25.1.893739314.0.6
r2424.0.821588814.0.1
r23c23.2.856831312.0.9
r22b22.1.717167011.0.5
r21e21.4.70755299.0.9
r21b21.1.63524629.0.8
r20b20.1.59489448.0.7
r19c19.2.53456008.0.2
r18b18.1.50630457.0.2

2.3 结论

根据前一节的两个表格知道:

opencv 4.8.0 是用 ndk-r18b 编译, 对应的编译器是 clang 7.0.2

opencv 4.9.0 是基于 ndk-r25c 编译, 对应的编译器是 clang 14.0.7

ndk 没有使用过 clang 13.0.0 版本, 是从 clang 12.0.9 (ndk-r23c) 直接升级到 clang 14.0.1 (ndk-r24)

编译选项 -moutline-atomics-mno-outline-atomics 是在 clang 13.0.0 引入的, ndk-r21e 使用的是 clang 9.0.9, 因此会提示“不支持 -mno-outline-atomics 选项”.

3. libgcc.a 的类似报错: __aarch64_swp4_acq_rel

3.1 问答描述

https://stackoverflow.com/questions/75045297/libgcc-linker-error-hidden-symbol-aarch64-swp1-acq-rel-in-libgcc-a-is-referen (参考链接[4]) 给出了 gcc aarch64 下的链接报错, __aarch64_swp4_acq_rel 符号找不到, 这和 opencv issue 24856 报错很像, 符号名字结尾略有差别。

用户 Bill Cong 的回答给出了很好的最小复现: 定义和使用 std::atomic<int> 变量, 使用 -O1 优化等级, 在不同编译器下查看对应的反汇编,

#include <atomic>std::atomic<int> ai(3);int main() {return ai.exchange(5);
}
  • ARM64 gcc 9.3, 不会生成 __aarch64_swp4_acq_rel 汇编指令

  • ARM64 gcc 10.2, 会生成 __aarch64_swp4_acq_rel 汇编指令

  • ARM64 gcc 10.2, 传入 -mno-outline-atomics 选项, 不会生成 __aarch64_swp4_acq_rel 汇编指令

https://godbolt.org/z/ssK3GGaoE (参考链接[5])

在这里插入图片描述

3.2 分析

-m-outline-atomics 编译选项是哪个版本引入 GCC 的? 含义是什么?

https://stackoverflow.com/questions/65239845/how-to-enable-mno-outline-atomics-aarch64-flag (参考链接[6]) 提到, gcc 9.4 开始提供 -m-outline-atomics 编译选项。

https://gcc.gnu.org/gcc-9/changes.html (参考链接[7]) 则证实了这一点
在这里插入图片描述

The option -moutline-atomics has been added to aid deployment of the Large System Extensions (LSE) on GNU/Linux systems built with a baseline architecture targeting Armv8-A. When the option is specified code is emitted to detect the presence of LSE instructions at run time and use them for standard atomic operations. For more information please refer to the documentation.

有人在编译 Android FFmpeg 5.0 后的运行阶段遇到报错 (参考链接 [8])

dlopen failed: cannot locate symbol “__aarch64_ldadd8_acq_rel”

参考链接[9] 则给出了关于 LSE 的进一步解释:

Out-of-line Atomics for LSE deploymentAArch64 Large System Extensions (LSE) were introduced in Armv8.1-A. These provide more efficient atomic instructions for large multi-core systems.LLVM 12 adds support for a new flag ‘-moutline-atomics', which detects at runtime whether the processor supports LSE. It then uses these new atomic instructions if possible, falling back to Armv8.0-A LL/SC loops on processors without LSE support. This option behaviour mirrors similar support available within GNU family of projects.  We are working towards making this option enabled by default in the upcoming LLVM13 release.

即:

Armv8.1-A 引入了 LSE(大系统扩展), 用于在多核系统上提供更高效的原子指令。 LLVM12 添加了 -moutline-atomics 编译选项, 在运行时检查处理器是否支持 LSE, 如果存在则使用, 不存在则回退到 Arm8.0-A 的 LL/SC。 这个选项在 GNU 家族的工程中也提供了(就是 GCC)。 在 LLVM13 中 -moutline-atomics 选项则被默认开启。

clang 12.0.1 开始,添加了 -moutline-atomics-mno-outline-atomics 选项(参考链接 [10]):
在这里插入图片描述

而根据参考链接 [11], LLVM 的开发者们讨论提到, gcc 9.3.1 开始提供了 LSE 的支持, 并在 gcc 10.1 默认开启:

Outline atomics were added with gcc 9.3.1 and turned on by default in gcc 10.1

3.3 结论

-moutline-atomics 编译选项表示开启 LSE (Large System Extension), 意思是在运行时提供更高效率的原子操作, 在 Arm8.1-a 上起作用, 在 Arm8.0 上回退到原本的原子操作处理上。 -mno-outline-atomics 则关闭这一编译选项。

-moutline-atomics 是在 gcc 9.3.1 版本开始提供, 在 clang 12.0.1 版本开始提供, 但都是默认不开启状态。 在 gcc 10.1 版本和 clang 13.0.0-rc1 版本中默认开启了 -moutline-atomics 编译选项。

4. __aarch64_ldadd4_acq_rel__aarch64_swp4_acq_rel 指令, 对应的 C/C++ 代码是什么?

4.1 C++11

基于参考链接[5], 很容易构造如下代码, 并在 armv8-a clang 13.0.0 生成 __aarch64_ldadd4_acq_rel 指令 (仍然使用 -O1):

https://godbolt.org/z/T7vzesfxd (参考链接[12])

#include <atomic>
std::atomic<int> ai(3);int main() {ai.fetch_add(1, std::memory_order_acquire);return ai.exchange(5);
}

汇编如下, 生成了 __aarch64_ldadd4_acq__aarch64_swp4_acq_rel 指令.

main:stp     x29, x30, [sp, #-32]!str     x19, [sp, #16]mov     x29, spadrp    x19, aiadd     x19, x19, :lo12:aimov     w0, #1mov     x1, x19bl      __aarch64_ldadd4_acqmov     w0, #5mov     x1, x19bl      __aarch64_swp4_acq_relldr     x19, [sp, #16]ldp     x29, x30, [sp], #32retai:.word   3

4.2 C++03: ncnn XADD 宏的实现

ncnn 库使用 C++03 编译, 在大部分平台使用到了 __atomic_fetch_add, __sync_fetch_add_add 等类似的 builtin 函数:

https://github.com/Tencent/ncnn/blob/20250503/src/allocator.h#L105-L151 (参考链接[13])

#if NCNN_THREADS
// exchange-add operation for atomic operations on reference counters
#if defined __riscv && !defined __riscv_atomic
// riscv target without A extension
static NCNN_FORCEINLINE int NCNN_XADD(int* addr, int delta)
{int tmp = *addr;*addr += delta;return tmp;
}
#elif defined __INTEL_COMPILER && !(defined WIN32 || defined _WIN32)
// atomic increment on the linux version of the Intel(tm) compiler
#define NCNN_XADD(addr, delta) (int)_InterlockedExchangeAdd(const_cast<void*>(reinterpret_cast<volatile void*>(addr)), delta)
#elif defined __GNUC__
#if defined __clang__ && __clang_major__ >= 3 && !defined __ANDROID__ && !defined __EMSCRIPTEN__ && !defined(__CUDACC__)
#ifdef __ATOMIC_ACQ_REL
#define NCNN_XADD(addr, delta) __c11_atomic_fetch_add((_Atomic(int)*)(addr), delta, __ATOMIC_ACQ_REL)
#else
#define NCNN_XADD(addr, delta) __atomic_fetch_add((_Atomic(int)*)(addr), delta, 4)
#endif
#else
#if defined __ATOMIC_ACQ_REL && !defined __clang__
// version for gcc >= 4.7
#define NCNN_XADD(addr, delta) (int)__atomic_fetch_add((unsigned*)(addr), (unsigned)(delta), __ATOMIC_ACQ_REL)
#else
#define NCNN_XADD(addr, delta) (int)__sync_fetch_and_add((unsigned*)(addr), (unsigned)(delta))
#endif
#endif
#elif defined _MSC_VER && !defined RC_INVOKED
#define NCNN_XADD(addr, delta) (int)_InterlockedExchangeAdd((long volatile*)addr, delta)
#else

但查看 ncnn 库文件的反汇编, 例如 ncnn-20250503-android-shared.zip, 并没有找到 __atomic_fetch_add 指令:

aarch64-linux-android-objdump -d ncnn-20250503-android-shared/arm64-v8a/lib/libncnn.so | ag '__aarch64_ldadd4_acq'

原因是 ncnn 的 CMakeLists.txt 里主动开启了 -mno-outline-atomics:

https://github.com/Tencent/ncnn/blob/master/src/CMakeLists.txt#L648-L652

    if(ANDROID_NDK_MAJOR AND (ANDROID_NDK_MAJOR GREATER_EQUAL 23))# llvm 12 in ndk-23 enables out-of-line atomics by default# disable this feature for fixing linking atomic builtins issue with old ndktarget_compile_options(ncnn PRIVATE -mno-outline-atomics)endif()

(此处存疑, 可能是 ncnn 注释 typo, 个人理解是 llvm 13 和 ndk-r25 默认开启 out-of-line atomics; nihui: 使用 __aarch64_ldadd4_acq_rel后,树莓派上会 crash,链接器会有undefined行为, 因此 ncnn 未开启 )

4.3 自行构造

构造了简单直白的一份 MY_XADD 宏的实现: https://godbolt.org/z/x567r5Gfr (参考链接[14])

#if _MSC_VER
#   include <windows.h>
#   define MY_XADD(addr, delta) ::InterlockedExchangeAdd((volatile long*)(addr), delta)
#elif __linux__
#   define MY_XADD __sync_fetch_and_add
#endifint main() {int refcount = 1;return MY_XADD(&refcount, -1);
}

使用 -O1 优化等级, 在各个编译器下结果如下:

在这里插入图片描述

  • armv8-a clang 12.0.0: 生成 “平凡的” ldaxr, stlxr 指令
  • armv8-a clang 13.0.0: 生成 __aarch64_ldadd4_acq_rel 指令
  • armv8-a clang 13.0.0, 生成 -mno-outline-atomics 编译选项: 生成 “平凡的” ldaxr, stlxr 指令
  • arm64 msvc v19.43 VS17.13: 生成 “平凡的” ldaxr, stlxr 指令
  • x86-64 gcc 15.1: 生成 lock xadd 指令

ldaxr 指令的解释:

Load-Acquire Exclusive Register derives an address from a base register value, loads a 32-bit word or 64-bit doubleword from memory, and writes it to a register. The memory access is atomic. The PE marks the physical address being accessed as an exclusive access. This exclusive access mark is checked by Store Exclusive instructions. See Synchronization and semaphores. The instruction also has memory ordering semantics as described in Load-Acquire, Store-Release. For information about memory accesses, see Load/Store addressing modes.

Load-Acquire Exclusive Register 指令根据基址寄存器的值计算地址,从内存中读取一个 32 位字(word)或 64 位双字(doubleword),并写入目标寄存器。此内存访问是原子的。处理单元(PE)会将正在访问的物理地址标记为“独占访问”,该标记稍后由 Store Exclusive 指令检查。详见“同步与信号量”一节。
此外,该指令具有 Load-Acquire/Store-Release 描述的内存排序语义。有关内存访问的更多信息,请参阅“装载/存储寻址模式”。

stlxr 指令的解释:

Store-Release Exclusive Register stores a 32-bit word or a 64-bit doubleword to memory if the PE has exclusive access to the memory address, from two registers, and returns a status value of 0 if the store was successful, or of 1 if no store was performed. See Synchronization and semaphores. The memory access is atomic. The instruction also has memory ordering semantics as described in Load-Acquire, Store-Release. For information about memory accesses, see Load/Store addressing modes.

Store-Release Exclusive Register(存储-释放独占寄存器)指令在处理元(PE)对某内存地址拥有独占访问权限时,可将两个寄存器中的数据写入内存:写入 32 位字或 64 位双字。如果写入成功,指令返回状态值 0;若未执行写入,则返回状态值 1。参见“同步与信号量”。

该内存访问是原子的。此指令还具备与“Load-Acquire / Store-Release”描述一致的内存排序语义。关于内存访问的详细说明,请参阅“加载/存储寻址模式”。

4.4 分析 libbase.a 库

前一节构造的最小复现代码,MY_XADD 宏的定义和使用, 对应到文章开头 libbase.a 基础库中的 BASE_XADD 宏实现:

base/atomic.hpp

#if _MSC_VER
#   include <windows.h>
#   define BASE_XADD(addr, delta) ::InterlockedExchangeAdd((volatile long*)(addr), delta)
#elif __linux__
#   define BASE_XADD __sync_fetch_and_add
#endif

base/atomic.hpp 被不同的编译器处理:

  • ndk-r18: clang 7.0.2, 编译出 libbase.a

  • ndk-r25c 或更高版本: clang >= 14.0.7, 编译出 libfoo.so 和 libbar.a

猜测 libapp1.solibapp2.so 不会接触到 base/atomic.hpp, 并且这两个 app so 是用 ndk-r24 或更低版本编译。 这样一来, libfoo.so 和 libbar.a 的编译过程产生了非预期的 __aarch64_ldadd4_acq_rel 汇编指令, 下游的集成人员遇到链接报错, 集成人员换到 clang >= 13.0.0 的 ndk 版本, 也就是 ndk >= r25, 就避免了报错。

另一种改法: 是 libfoo.solibbar.a 的构建过程, 做类似于 ncnn 的配置, 判断 ndk >= r24 并添加 -mno-outline-atomics 编译选项。

5. 总结

gcc 9.3.1 和 clang 12.0.1 添加了 -moutline-atomics 编译选项, 用来开启 LSE (Large System Extension), 用于在 Armv8.1-a 上改善多核情况下的原子操作的性能。

这个选项在 gcc 9 和 clang 12 是默认不开启的, 相当于是默认开启了 -mno-outline-atomics

从 gcc 10 和 clang 13 开始默认开启 -moutline-atomics 编译选项。

ndk 版本和 clang 版本有对应的关系, clang >= 13 对应到 ndk >= r24。

当使用 clang >= 13 (ndk >= r24) 编译了带有 __sync_fetch_and_add (C/C++ Builtin) 或 C++11 的 std::atomic<T> 的代码, 例如 opencv/ncnn 中的 XADD 的实现, 会生成 __aarch64_ldadd4_acq_rel 等 LSE 相关的汇编指令, 这样的二进制被 clang < 13 (ndk < r24) 的编译器做链接, 会遇到 __aarch64_ldadd4_acq_rel 符号找不到的问题。

当遇到上述报错, 可以对齐编译器版本到 clang >= 13 (ndk >= r24), 或手动指定 -mno-outline-atomics 选项。

如果是 clang 12 或 gcc 9 编译器, 但传入了 -moutline-atomics 选项, 也会发生类似的链接报错, 处理方式同前。

当然, 考虑到 ndk-r24 没有 ndk-24b, ndk-r24c 等版本, 建议用 ndk >= r25c 的版本。

References

  • [1] https://github.com/opencv/opencv/issues/24856
  • [2] https://github.com/llvm/llvm-project/commit/c5e7e649d537067dec7111f3de1430d0fc8a4d11
  • [3] https://stackoverflow.com/questions/53385892/find-the-ndk-version-used-for-building-opencv-android-native-libraries
  • [4] https://stackoverflow.com/questions/75045297/libgcc-linker-error-hidden-symbol-aarch64-swp1-acq-rel-in-libgcc-a-is-referen
  • [5] https://godbolt.org/z/ssK3GGaoE
  • [6] https://stackoverflow.com/questions/65239845/how-to-enable-mno-outline-atomics-aarch64-flag
  • [7] https://gcc.gnu.org/gcc-9/changes.html
  • [8] https://juejin.cn/post/7149468268674154510
  • [9] https://community.arm.com/arm-community-blogs/b/tools-software-ides-blog/posts/llvm12-for-arm
  • [10] https://releases.llvm.org/12.0.1/tools/clang/docs/ReleaseNotes.html
  • [11] https://reviews.llvm.org/D93585
  • [12] https://godbolt.org/z/T7vzesfxd
  • [13] https://github.com/Tencent/ncnn/blob/20250503/src/allocator.h#L105-L151
  • [14] https://godbolt.org/z/x567r5Gfr

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

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

相关文章

如何清除windows 远程桌面连接的IP记录

问题 在远程桌面连接后&#xff0c;会在输入列表留下历史IP记录&#xff0c;无用的IP多了会影响我们查找效率&#xff0c;也不安全。 现介绍如何手动删除这些IP记录。 解决方案 1、打开注册表 按 Win R&#xff0c;输入 regedit&#xff0c;回车定位到远程桌面记录的注册表…

使用 React Native实现鸿蒙开发的详细方案

一、环境准备 1. 基础环境要求 操作系统:Windows 10/11 或 macOS (建议版本最新)Node.js: v16.x 或更高版本npm: v8.x 或更高版本Java JDK: 11 或更高版本DevEco Studio: 3.1 或更高版本 (鸿蒙官方IDE)2. 安装 DevEco Studio 从华为开发者官网下载安装时选择以下组件: Harmo…

贪心算法应用:顶点覆盖问题详解

贪心算法应用&#xff1a;顶点覆盖问题详解 贪心算法是解决顶点覆盖问题的经典方法之一。下面我将从基础概念到高级优化&#xff0c;全面详细地讲解顶点覆盖问题及其贪心算法解决方案。 一、顶点覆盖问题基础 1. 问题定义 顶点覆盖问题&#xff08;Vertex Cover Problem&am…

Excel安全防护:开源批量加密工具推荐与使用指南

先放下载链接&#xff1a;https://tool.nineya.com/s/1iqsn2sh0 在日常办公里&#xff0c;像财务数据、客户信息、项目报表这类核心资料&#xff0c;常常是以 Excel 文件的形式来存储的。要是手动一个一个地给这些文件加密&#xff0c;那可太费时间和精力了&#xff0c;而且还…

【C++】学习、项目时Debug总结

这里写目录标题 1. 内存问题1.1. 内存泄漏1.1.1. 内存泄漏案例检查方法1.1.2. 主线程提前退出导致【控】1.1.3. PostThreadMessage失败导致的内存泄漏**【控】**1.1.4. SendMessage 时关闭客户端【控】1.1.5. 线程机制导致【**控】**1.1.6. exit&#xff08;0&#xff09;导致【…

2025 后端自学UNIAPP【项目实战:旅游项目】1、创建项目框架

1、创建项目 ①项目名称&#xff1a;自定义&#xff0c;【我是travel】 ②vue版本&#xff1a;vue3 ③其他默认&#xff0c;最后创建 2、创建页面 ①展开自己刚才创建的项目 ②单击选中pages文件夹 --->鼠标右键---->新建页面 ③页面名称&#xff1a;自定义favouri…

WPF 子界面修改后通知到主页面

子页面&#xff1a; public partial class MyPopupWindow : Window { public event Action OnClose; private void CloseWindowButton_Click(object sender, RoutedEventArgs e) { OnClose?.Invoke(); this.Close(); } } 主界面&#xff1a…

Python中的标识、相等性与别名:深入理解对象引用机制

在Python编程中&#xff0c;理解变量如何引用对象以及对象之间的比较方式是至关重要的基础概念。本文将通过Lewis Carroll的笔名示例&#xff0c;深入探讨Python中的对象标识、相等性判断以及别名机制。 别名现象&#xff1a;变量共享同一对象 >>> charles {name: …

python 闭包获取循环数据经典 bug

问题代码 def create_functions():functions []for i in range(3):# 创建一个函数,期望捕获当前循环的i值functions.append(lambda: print(f"My value is: {i}"))return functions# 创建三个函数 f0, f1, f2 create_functions()# 调用这些函数 f0() # 期望输出 &…

克里金模型+多目标优化+多属性决策!Kriging+NSGAII+熵权TOPSIS!

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 克里金模型多目标优化多属性决策&#xff01;KrigingNSGAII熵权TOPSIS&#xff01;&#xff01;matlab2023b语言运行&#xff01; 1.克里金模型&#xff08;Kriging Model&#xff09;是一种基于空间统计学的插值方法…

Prompt Engineering 提示词工程学习

一、Prompt Engineering 简介 Prompt Engineering 是设计和优化输入提示(Prompt)以获得预期输出的过程。在与大型语言模型(如 GPT-4)交互时,如何构造提示会显著影响模型的回答质量。 二、Prompt 的重要性 提高生成准确性:通过正确的 Prompt 引导,模型能够更好地理解用…

MATLAB安装常见问题及解决方案详解(含代码示例)

MATLAB作为科学计算和工程分析的核心工具&#xff0c;其安装过程可能因操作系统版本、硬件配置或网络环境等因素而出现各种问题。本文基于MATLAB官方文档和社区经验&#xff0c;系统总结了安装过程中常见的问题&#xff0c;并提供详细的解决方案和代码示例&#xff0c;帮助用户…

免安装 + 快速响应Photoshop CS6 精简版低配置电脑修图

各位PS小白和修图大神们&#xff0c;今天来给大家聊聊Photoshop CS6精简版这个宝藏软件&#xff01; Photoshop CS6精简版就是Adobe Photoshop CS6的“瘦身版”&#xff0c;它把一些不常用的功能给简化了&#xff0c;只留下核心工具&#xff0c;特别适合那些想高效操作、节省系…

微服务架构实战:从服务拆分到RestTemplate远程调用

微服务架构实战&#xff1a;从服务拆分到RestTemplate远程调用 一 . 服务拆分1.1 服务拆分注意事项1.2 导入服务拆分 Demo1.3 小结 二 . 服务间调用2.1 注册 RestTemplate2.2 实现远程调用2.3 小结 三 . 提供方和消费方 在分布式系统设计中&#xff0c;微服务架构因其灵活性、可…

MySQL 索引与事务详解

目录 一、索引&#xff08;Index&#xff09; 二、事务&#xff08;Transaction&#xff09; 三、总结 一、索引&#xff08;Index&#xff09; 索引的本质&#xff1a;一种数据结构&#xff08;如 BTree、Hash&#xff09;&#xff0c;用于快速定位数据&#xff0c;避免全…

macOS Python 环境配置指南

1. 检查现有 Python 环境 python3 --version # 检查 Python 3 版本 pip3 --version # 检查 pip 版本 2. 安装 pyenv&#xff08;Python 版本管理工具&#xff09; # 使用 Homebrew 安装 pyenvbrew install pyenv# 配置 pyenv 环境变量&#xff08;添加到 ~/.zshrc&#…

游戏引擎学习第272天:显式移动转换

回顾并为今天的内容铺垫背景 我们刚开始为游戏主角编写一些程序逻辑&#xff0c;因为我们之前已经完成了大部分引擎方面的开发&#xff0c;现在可以专注在角色身上。这个角色的移动方式会有些特别&#xff0c;与大多数游戏角色的运动机制不太一样。我们当前正在实现的控制方式…

软件测试都有什么???

文章目录 一、白盒测试&#xff08;结构测试&#xff09;二、黑盒测试&#xff08;功能测试&#xff09;三、灰盒测试四、其他测试类型五、覆盖准则对比六、应用场景 软件测试主要根据测试目标、技术手段和覆盖准则进行分类。分为白盒测试、黑盒测试、灰盒测试及其他补充类型 一…

very_easy_sql(SSRF+SQL注入)

题目有一行提示&#xff1a; you are not an inner user, so we can not let you have identify~&#xff08;你不是内部用户&#xff0c;所以我们不能让你进行身份验证&#xff09;联想到可能存在SSRF漏洞&#xff0c;一般情况下&#xff0c;SSRF攻击的目标是外网无法访问的内…

国内外主流AI编程工具全方位对比分析(截至2025年5月)

一、国际主流工具对比 1. Windsurf&#xff08;Codeium公司&#xff09; 核心功能&#xff1a;代理型AI编程&#xff08;代码导航/修改/命令执行&#xff09;、浏览器DOM访问、网页研究功能语言支持&#xff1a;70语言&#xff0c;包括Python/Java/JavaScript/Rust等[[22-23]…