目的只是一次简单的记录基于 嵌入式 Linux alsa-lib 的简单 Audio APP 的编译环境的简单配置。虽然基础虽然没有采用公司级别的规范编译流程但是了解一下总也是好的也算是复习了2016年夏天那次在 嵌入式 Linux 平台上搞 C 编译 汇编 反汇编的可爱时光吧第二步 测试播放音频文件使用aplay播放标准 alsa-lib 工具登录板子终端直接播放这个文件。这一步会走系统的默认路由linarolinaro-alip:~/Desktop/Music$ aplay ZhenDeAiZheNi.wav Playing WAVE ZhenDeAiZheNi.wav : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo如果能听到声音说明你的板子、喇叭/耳机、以及音频文件全都是 OK 的第三步 配置指定 SDK 内置的 C 标准库与 SDK 内置的编译工具链编译毋庸置疑权威参考 SDK 的编译方式找到 SDK 的根目录并找到官方指定的编译方式mapleKICKPI-RK3576:~/work/sdk/20251021/rk3576-linux-20251021/rk3576-linux$ tree -L 1 . ├── app ├── build_multi_dtb.sh ├── buildroot ├── build.sh - device/rockchip/common/scripts/build.sh ├── common - device/rockchip/common ├── compile_commands.json ├── Copyright_Statement.md - docs/licenses/LICENSE ├── debian ├── device ├── docs ├── external ├── kernel - kernel-6.1 ├── kernel-6.1 ├── Makefile - device/rockchip/common/Makefile ├── output ├── prebuilts ├── README.md - device/rockchip/common/README.md ├── rkbin ├── rkflash.sh - device/rockchip/common/scripts/rkflash.sh ├── rockdev - output/firmware ├── tools ├── u-boot ├── ubuntu └── yocto这里的build.sh - device/rockchip/common/scripts/build.sh就是官方指定的编译方式把他交给 Deepseek 分析出编译工具链和头文件等所需信息。分析好以后得出如下 当使用 SDK 预置工具链时头文件搜索路径由工具链自身的 sysroot 决定# 工具链位置 prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/ ├── aarch64-none-linux-gnu/ │ ├── include/ # 标准C库头文件编译 Audio APP 时需要的 C 标准库 │ │ ├── stdio.h │ │ ├── stdlib.h │ │ └── stddef.h │ └── lib/ # 库文件 ├── bin/ # 可执行文件 └── lib/ # 工具链自身库设置编译环境 - 永久环境变量推荐# RK3576 SDK 环境变量 export SDK_ROOT~/work/sdk/20251021/rk3576-linux-20251021/rk3576-linux export SDK_TOOLCHAIN$SDK_ROOT/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu export PATH$SDK_TOOLCHAIN/bin:$PATH export CROSS_COMPILEaarch64-none-linux-gnu- export CC${CROSS_COMPILE}gcc export CXX${CROSS_COMPILE}g export AR${CROSS_COMPILE}ar export LD${CROSS_COMPILE}ld export STRIP${CROSS_COMPILE}strip export OBJCOPY${CROSS_COMPILE}objcopy ​ # 快速跳转别名 alias sdk-rootcd $SDK_ROOT alias sdk-appcd $SDK_ROOT/app alias sdk-build$CC # END 生效source ~/.bashrc这种环境变量的方式设置编译环境不会污染 Linux_SDK 自身的编译生态✅完全安全不修改任何文件✅不会污染 SDK只影响当前终端✅不会污染系统系统 gcc 仍然可用✅符合最佳实践是标准的开发环境配置✅易于管理可以随时 source 或清除增值知识点问题我明明没有设置 标准 C 库的头文件指定呀那么 编译工具链 是如何找到他的呢在你的环境变量里确实只指定了CC...gcc的路径压根没有任何一个字提到标准 C 库比如stdio.h、stdlib.h在哪里。你什么都没做但编译系统依然能准确找到 ARM64 的标准 C 库头文件是因为交叉编译器在出厂被组织好Build的那一刻起它的脑子里就已经“死死硬编码”了全套标准库的绝对路径️ 编译器私藏的“藏宝图” 这个底层逻辑叫做Built-in Sysroot内置系统根路径。当你在终端执行$CC my_pcm_playback.c时这个名叫aarch64-none-linux-gnu-gcc的程序启动后它会在内部默默执行两件事寻找自己的出生地它会通过自己的可执行文件路径逆向推导找到它所在的安装目录。启动内置雷达Sysroot在它被芯片官方ARM 公司或者瑞芯微Rockchip打包编译出来的时候它的源码里就已经硬编码了一个相对路径。它会自动定位到它同级目录下的标准库位置。我们可以用一条硬核的命令让你的编译器自己把这个“秘密”吐出来$CC -print-sysroot这个目录就是你的交叉编译器自带的“标准库沙盒”。同理在处理 alsa-lib 标准库的头文件时原理就是实现“自定义沙盒”1. 下载版本匹配的 alsa-lib 源码2. 通过 ./configure --prefix$(pwd)/_install 进行“沙盒地址绑定”3. 执行make make install正式“向沙盒注入资产”4. 在应用 Makefile 中通过CFLAGS -I...和LDFLAGS -L...实现“精准雷达导航”。 编译器眼中的双层世界当你敲下make编译时由于有了这个内置的 Sysroot编译器眼中其实有两个完全并行的世界搜索维度寻找标准依赖如 stdio.h寻找第三方依赖如 alsa/asoundlib.h它去哪里找它偷偷去自己的肚子里找$SDK_TOOLCHAIN/.../aarch64-none-linux-gnu/libc/usr/include它顺着你给的-I导航线找./alsa-lib-1.2.11/_install/include谁决定的出厂时硬编码决定的你不需要做任何事。你的CFLAGS 决定的你需要显式指定。第四步 配置asoundlib.h:需要在 SDK 中或在交叉编译环境中提供 ARM64 版本的 ALSA 库开发包搜索 SDK 代码里是否存在rg -g asoundlib.h --filesmapleKICKPI-RK3576:~/work/sdk/20251021/rk3576-linux-20251021/rk3576-linux$ rg -g asoundlib.h --files external/common_algorithm/audio/rockaa/include/tinyalsa/asoundlib.h external/rk_pcba_test/tinyalsa/asoundlib.h找到 2 个但都是 tinyALSA 版本的这不兼容于标准 ALSA 版本。那么自己就在 x86 主机系统内安装一个 arm64 架构的 alsa-lib 标准库。具体如下应用代码如果您需要完整的 ALSA API应该安装标准的 ALSA 开发包新增安装包的 arm64 架构sudo dpkg --add-architecture arm64配置源新增 arm64 版本# 1. 华为云源仅用于 PC 的 amd64 软件包 X-Reponame: huawei-amd64 Types: deb URIs: http://mirrors.huaweicloud.com/ubuntu Suites: noble noble-updates noble-security noble-backports Components: main restricted universe multiverse Architectures: amd64 Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg ​ # 2. 阿里云 Ports 源专门用于 arm64 交叉编译包国内加速 X-Reponame: Alibaba-arm64 Types: deb URIs: http://mirrors.aliyun.com/ubuntu-ports Suites: noble noble-updates noble-security noble-backports Components: main restricted universe multiverse Architectures: arm64 Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg更新后即可安装这个方案容易弄混被否了替代方案在第六步#3# 这个方案容易弄混被否了替代方案在第六步#3 # 安装 ARM64 交叉编译的 ALSA 库 sudo apt-get update sudo apt-get install libasound2-dev:arm64 ​ # 或者安装本地的 ALSA 库如果在开发板上原生编译 sudo apt-get install libasound2-devsudo apt-get install libasound2-dev:arm64相当于✅ 在系统中新增了 ALSA 库这个软件包✅ 在/usr/下新增了 ALSA 的头文件和库文件✅ 让您的交叉编译器能够使用这些新增的库✅Debian/Ubuntu 的 Multiarch 机制确保了不同架构的头文件和库文件可以安全、有序地共存绝不会混乱。安装完libasound2-dev:arm64后可以通过dpkg -L命令来查看它安装了哪些文件以及它们分别在哪里。dpkg -L libasound2-dev:arm64细节补充asoundlib.h内的函数在编译时没有其实现的源代码参与而是等运行时执行到pcm_open()时✅确实发生了跨 so 动态库的调用✅程序跳转到libasound.so中执行✅执行完返回您的程序头文件只是编译时的说明书真正的跨 so 动态库的执行发生在运行时程序会直接调用 SO 中的函数代码。 跨 SO 交互的几种方式方式1隐式链接您正在使用的// 代码中直接调用 #include asoundlib.h pcm_open(...); // 编译时不知道实现运行时通过动态库解析# 编译时指定 gcc -o my_app my_app.c -lasound方式2显式加载dlopen#include dlfcn.h ​ // 运行时手动加载 SO void* handle dlopen(libasound.so, RTLD_LAZY); // 获取函数指针 int (*pcm_open)(...) dlsym(handle, pcm_open); // 调用 pcm_open(...);方式3插件系统// 主程序 plugin-process(); // 调用不同 SO 中的实现第五步 编写代码ALSA_LOCAL ./alsa-lib-1.2.11/_install ​ CFLAGS -I$(ALSA_LOCAL)/include LDFLAGS -L$(ALSA_LOCAL)/lib -lasound ​ TARGET my_pcm_playback SRCS my_pcm_playback.c ​ all: $(CC) $(SRCS) -o $(TARGET) $(CFLAGS) $(LDFLAGS) ​ clean: rm -f $(TARGET)第三步和第四步的目的就是为了在代码里能够找到 C标准库 和 alsa-lib 标准库的头文件#include stdio.h #include stdlib.h #include string.h #include errno.h #include alsa/asoundlib.h第六步 编译这一步在编译之前主要解决的是一定要选择版本匹配的 alsa-lib 标准库的源码把匹配版本的 alsa-lib 源码进行编译并服务于编译过程中的#include alsa/asoundlib.h的引用。解决了所有头文件引用后即 C 标准库、alsa-lib 标准库而不是 tinyALSA 库便可以 make 编译了。make 编译过程中使用的是# 1. 回到你的 my_app 目录 cd ~/work/sdk/20251021/rk3576-linux-20251021/rk3576-linux/app/my_app ​ # 2. 下载 alsa-lib 源码包 (以稳定的 1.2.11 为例) wget https://www.alsa-project.org/files/pub/lib/alsa-lib-1.2.11.tar.bz2 tar -xjf alsa-lib-1.2.11.tar.bz2 cd alsa-lib-1.2.11 ​ # 3. 配置配置脚本指定交叉编译器指定输出路径 # 3.1 进入 alsa-lib 源代码根目录注意必须保证与 SDK 的 libasound.so 版本一致 cd alsa-lib-1.2.11 make distclean #3.2 在 configure 层面把所有的安装触角全部收回本地沙盒 ./configure --hostaarch64-none-linux-gnu \ --prefix$(pwd)/_install \ --with-configdir/usr/share/alsa \ --with-alsadatadir$(pwd)/_install/share/alsa \ --with-pkgconfigdir$(pwd)/_install/lib/pkgconfig \ --datarootdir$(pwd)/_install/share \ --disable-python ​ # 4. 编译并安装到 _install 目录 cd alsa-lib-1.2.11 make -j$(nproc) make install ​ # 5. 检查成果你会看到 libasound.so 等一众 ARM64 格式的库文件已经在里面静静躺着了。 cd .. ls -l _install/lib ​ # 6. 执行编译命令(my_app 根目录下执行) make对于你的音频应用程序App来说这个libasound.so以及它的软链接就是整个编译、链接过程中最核心、也是唯一需要物理交互的动态库实体。_install/内部存在libasound.so与应用APP代码里的#include alsa/asoundlib.h被用于在编译阶段进行规格检查如果用最通俗、最本质的话来定义它在电脑上交叉编译时这个本地的.so库和.h头文件联合起来确实只负责一件事——“严格的资质审查”与“开具上岗证明”。 真正的“大招”在开发板上才会释放为什么说它在 PC 上“只负责检查”因为真正干活的音频核心代码此时此刻根本没有放进你的my_pcm_playback程序里。当这套检查通过、程序生成后好戏才刚刚开始空壳出厂你编译出来的 App 只是一个持有“上岗证明”但体内没有半点音频驱动代码的空壳程序。上板合体当你把这个 App 丢进RK3576 开发板并敲下回车运行时开发板的操作系统会看到程序头部的“防伪印章”。现场加载板子系统会立刻在板子本地的/usr/lib里抓取板子上的libasound.so。注入灵魂就在运行的那一微秒板子上的.so库代码被临时加载进内存和你的 App 物理绑定。这时候你调用的snd_pcm_open才会真正变成一条条直接戳向内核音频驱动、让耳机孔发出声音的硬件指令 一句话顿悟在 PC 电脑上交叉编译阶段.h负责检查语法对不对.so负责检查符号有没有开具签证。在开发板上的运行时阶段我们的应用 APP 会按照标准与libasound.so这个动态库进行耦合运行。这正是 Linux 动态库链接机制的本质 我板子一直用的是 tinyALSA 我担心这个标准库的 libasound.so 不会被加载。名字决定一切只要板子上有libasound.so.2这个文件你的 App 就能启动。TinyALSA 是合格的“替身”对于大多数音频播放任务TinyALSA 完全能胜任标准 ALSA 的角色。无需担心你之前担心的“标准库不会被加载”的情况不会发生。相反加载 TinyALSA 是一个精心设计的、完全正常的兼容方案。重点./configure --hostaarch64-none-linux-gnu \ --prefix$(pwd)/_install \ --with-configdir/usr/share/alsa \ --with-alsadatadir$(pwd)/_install/share/alsa \ --with-pkgconfigdir$(pwd)/_install/lib/pkgconfig \ --datarootdir$(pwd)/_install/share \ --disable-python其实这段configure命名的核心作用是“建立一个专属于你的、完美的 ARM64 音频开发沙盒或称军工厂”。它本身并不直接把头文件喂给你的应用那是你的应用Makefile干的事但它通过一系列精密的“路径轰炸”在本地催生出了你的应用所需要的所有头文件和库。 完美的原理补齐实现“自定义音频沙盒”下载版本匹配的alsa-lib源码目的获得原材料。确保源码中使用的标准 C 函数能在编译器自带的旧Sysroot旧 GLIBC里找到对应的实现不产生代差。通过./configure --prefix$(pwd)/_install进行“沙盒地址绑定”目的划定领地。强行改变alsa-lib的默认安装常识默认是装进 PC 的/usr/local。使用$(pwd)动态获取当前绝对路径命令它在项目内部生成一个叫做_install的封闭盒子。所有编译出的头文件和.so库必须全部吐在这个盒子里不准外溢实现空间物理隔离。执行make make install正式“向沙盒注入资产”目的兵工厂开工。让交叉编译器$CC拿着它肚子里的旧标准 C 库常识去翻译alsa-lib源码。编译成功后把流水线上产出的专属 ARM64 架构的include/头文件和lib/库文件正式搬进_install盒子里。至此自定义音频沙盒构建完成。在应用 Makefile 中通过CFLAGS -I...和LDFLAGS -L...实现“精准雷达导航”目的知识补齐强行带路。当你编译自己的应用my_pcm_playback.c时遇到stdio.h编译器自动去它死记硬背的“标准库沙盒”Built-in Sysroot里抓取。遇到alsa/asoundlib.h编译器顺着你给的CFLAGS导航线精准降落到你刚刚建好的“自定义音频沙盒”./_install/include里抓取。# makefile 编译流程 my_app/ ├── my_pcm_playback.c - 你的应用源码 ├── makefile - 拿着全局的环境变量指着下方的私有沙盒进行编译 └── alsa-lib-1.2.11/ └── _install/ - 纯净的 ARM64 资源军工厂 ├── include/ - 提供应用编译所需的 alsa/asoundlib.h └── lib/ - 提供符合 SDK 旧版 Glibc 要求的 libasound.so所以 整个过程没有任何 SDK 原生生态环境污染SDK 的脚本一般不会使用环境变量而是根据自己内部的路径特征进行自洽搜索的它本身就是完全自洽的。 为什么说我们的操作做到了“0污染”正因为 SDK 拥有这样完美的自洽性再结合我们这次的操作整个生态打出了一套漂亮的配合对你的 PC 宿主机 0 污染我们通过configure层面精准的路径结扎没用sudo没往系统的/usr/lib扔一丁点垃圾你的电脑依然干净。对 SDK 源码 0 污染我们在app/my_app/这个属于你自己的独立应用目录下悄悄下载解压了alsa-lib并把产物留在了./alsa-lib-1.2.11/_install里。你没有破坏 SDK 任何一个原生脚本和核心目录。完美借力你的my_app利用了全局暴露出来的$CC工具链确保了 GLIBC 底层血统纯正同时又在本地完美自给自足了音频依赖补齐了业务缺失。这就是为什么你现在能编译成功而且不管你怎么折腾SDK 依然稳如磐石。因为你和 SDK各自都在自己的“自洽沙盒”里安全地运行着。 最终的工程结论步骤您的做法评价1. 选择匹配版本的 ALSA 源码alsa-lib-1.2.11✅ 正确2. 编译并安装到本地沙盒_install/✅ 正确避免了污染系统3. 编译时使用这些头文件和库-I _install/include -L _install/lib✅ 正确4. 运行时依赖板子的 libasound.so动态链接器自动处理✅ 正确您现在可以放心地进行make编译了如果还有编译错误那是代码逻辑问题而不是库环境问题了。在 KICKPI K7 主机上执行# 给程序添加执行权限 chmod x ./my_pcm_playback # 使用 ./ 指定当前目录下的程序真的爱着你-明慧版.wav ./my_pcm_playback ./ZhenDeAiZheNi.wav # 能够正常播放。内容经过验证 成功 confirmed OK。2026年7月7日16:29:36