2026/10/14 21:14:17

全志CQR40 SDK目录结构深度解析:嵌入式Linux构建体系拆解

全志CQR40 SDK目录结构深度解析:嵌入式Linux构建体系拆解 很多拿到 SDK 的兄弟第一反应都是赶紧把build.sh跑起来看看能不能点亮开发板。但我自己拿到全志 CQR40 平台这套 SDK 之后的做法恰恰相反我先把整个文件夹结构完整过了一遍前后花了两天才动手编译。这套 SDK 解压开就几十个 GB如果你不先把目录逻辑摸清楚后面跑构建、改配置、裁剪系统每一步都会像在黑屋子里找开关。这篇文章就是我这次做 CQR40 平台 SDK 文件夹分析时留下的记录包含目录怎么分层、每个目录里到底放了什么、哪些文件动了会出事以及我实际排查过的几个经典问题。想快速上手的做 BSP、驱动移植或系统定制的工程师都可以直接拿这份思路去应付同类 SDK。1. 项目背景为什么文件夹结构本身值得花时间分析1.1 拿到 SDK 后的第一件事不要急着编译我以前接过一个项目拿到一套不熟悉的 SDK上来就编译结果第一次构建就报了找不到/out/xxx的错误。那时候我连out这个目录是编译期生成还是源码自带的都不确定最后只能老老实实回去翻目录。全志 CQR40 平台的 SDK 不是一个小工具包它包含了引导程序、内核、根文件系统、构建工具链、板级配置、烧录工具甚至还有一堆文档和预编译产物。只要其中一个子模块的路径不对整条构建链就会断掉。所以我的建议很明确先建目录树再读 README 和顶层build.sh然后顺藤摸瓜把关键配置文件的路径全部标出来。这个过程看起来像是在做“文档工作”但实际上能给你省出后面大把的调试时间。拿到 SDK眼睛不要只盯着哪个 defconfig 该选什么先搞清楚“哪个目录是源码、哪个目录是输出、哪个目录是配置”这三大问题。1.2 这份分析记录适合谁看如果你是刚入门嵌入式 Linux 开发的这套 SDK 目录分析记录可以帮助你避开很多无效劳动。尤其是当你面对的不是“干净”的开源主线内核而是由芯片厂商整理过的 BSP 包时目录结构和构建脚本里的隐含逻辑往往比代码本身更影响成败。如果你是做驱动移植或者内核裁剪的工程师也需要这份记录。因为你要改的文件大概率分散在 kernel、device、buildroot 三个目录里如果不知道它们之间怎么联动改了配置也编不出来。如果你只是做上层应用集成那一部分目录可以略过但建议还是看看第 3、4 节了解镜像是怎么被打包出来的对排查启动问题会有帮助。2. CQR40 平台 SDK 的顶层目录架构2.1 第一眼看到的整体结构全志 CQR40 平台这一版 SDK 解压后顶层目录大致长这样:CQR40_Linux_SDK/ ├── brandy/ ├── buildroot/ ├── device/ ├── docs/ ├── external/ ├── kernel/ ├── out/ ├── prebuilt/ ├── scripts/ ├── target/ ├── tools/ └── build.sh不用被这么多个目录吓到。它们其实可以归纳成四类源码类kernel/、brandy/、buildroot/、external/配置类device/、target/构建类scripts/、tools/、build.sh输出类out/、部分prebuilt/我对这种组织的理解是SDK 实际上把一整个嵌入式 Linux 系统拆成了若干独立编译单元。每个单元都有自己单独的构建入口最后再由顶层脚本统一收集产物生成烧录镜像。这种设计的好处是方便做“单个模块增量编译”比如你只改内核驱动那就可以只编 kernel不必把整个 buildroot 重新跑一遍节省时间。2.2 顶层目录之间的依赖关系只看目录名不够还得知道谁的产物会被谁拿去用。我在分析时专门画过一张依赖表虽然 CQR40 平台有一定的私有细节但大体思路是通用的。目录编译产物主要依赖最终去向brandyboot0、Uboot工具链、芯片初始化配置烧录到启动分区kernelzImage/Image、dtb板级 dts、内核 defconfigboot 分区buildrootrootfs 镜像、各类应用库包配置、交叉编译工具链rootfs 分区scripts/tools打包工具、签名工具无生成整包镜像target镜像打包规则、分区表device 下的配置文件out 目录中的最终烧录包从编译顺序上看一般是 toolchain → brandy → kernel → buildroot → pack。但你要灵活点如果只是做应用层调试完全可以只跑 buildroot 和打包步骤。理解依赖关系后就不会出现“改完内核却在 buildroot 目录里找产物”这类低级问题。2.3 目录命名的规律这套 SDK 的目录命名是有门道的。kernel下面一般会有个linux-x.y.z之类的深层目录buildroot下面也可能带具体版本后缀。不要被这些多层嵌套搞晕本质上就是“厂商把某个开源组件原样放进 SDK然后再在外面包一层自己的链接”。更值得注意的是scripts/目录名称不同版本的 SDK 可能叫scripts也可能叫platform、build但作用都差不多。我在分析 CQR40 平台时习惯先把build.sh里所有source的脚本路径列出来因为那才是真正决定构建流程的文件。3. 核心目录逐个拆解3.1 brandy引导程序的三个关键产物brandy这个目录在 CQR40 平台里存放的是整个系统的起点逻辑。按我拆解后的结果它内部通常还分成若干子模块最重要的两个是 boot0 和 Uboot。boot0 负责最基础的硬件初始化比如 DDR、时钟和片外 FlashUboot 则在 boot0 之上负责加载内核、传递启动参数、实现环境变量等。这里有个很容易被忽略的细节boot0 和 Uboot 虽然是两个独立模块但它们的配置必须保持一致尤其是 DDR 参数和引脚复用。我见过有人只改了 Uboot 的串口配置boot0 还在用默认串口结果烧进去之后启动日志一片混乱。翻brandy目录时重点看各模块的样板配置文件比如boards或者configs下的文件而不是一上来就钻进去读汇编代码。真正改引导程序的机会不多但一旦要改必须顺着这个目录树找到对应平台配置。CQR40 平台的硬件差异基本都集中在board_xxx之类的头文件中注意看宏开关很多看似相同的配置在不同宏下行为完全不同。3.2 kernel 与板级设备树kernel/目录是整个 SDK 里最庞大的部分。它里面不是只有一个内核源码树还挂着许多厂商补充的驱动源码。要在这个目录里分析先记住两个关键的定位方式内核配置文件在arch/arm64/configs/或arch/arm/configs/下CQR40 平台一般对应一个xxx_cqr40_defconfig设备树源文件在arch/arm64/boot/dts/或arch/arm/boot/dts/下文件名可能会带有sunxi、cqr40之类的平台标识我拿到 SDK 之后第一件要干的事就是确认这份 SDK 用的是 32 位还是 64 位内核。因为这会直接决定你后面make ARCHarm64 CROSS_COMPILE...的命令写法。CQR40 这一版以 64 位为主但内核源码里还是会混着 32 位平台的东西这并不奇怪不用觉得是目录结构坏了。分析时建议重点看dts目录下与 CQR40 相关的.dts和.dtsi文件。板级外设的寄存器地址、中断号、GPIO 复用全部在这里。很多人喜欢直接去改驱动源码但我在实际排查中发现不少“驱动不工作”的 bug最后问题其实出在设备树节点没配对。改设备树后记得重新生成 dtb别只重新编内核镜像。3.3 buildroot根文件系统不是“一个静态目录”buildroot/目录负责构建整个 rootfs。它从表面上看起来是一堆 Makefile 和配置文件但实际作用是从下载源码包、编译库、生成根文件系统目录树到最后压缩成镜像一条龙完成。CQR40 平台的 SDK 里buildroot 往往预先配置好了一批工具和库比如网络管理、音频框架、图形界面组件等。分析这个目录时我建议先看configs/里与这个平台对应的 defconfig。里面会有一大堆BR2_PACKAGE_xxxy的配置项这些就是系统定制的开关。我常用grep去搜某个包名比如BR2_PACKAGE_XXX来确定某个库是不是已经被编进了系统。还有一个容易踩的坑是buildroot 编译时会去联网下载软件包如果你的环境不能联网一定要提前把下载好的dl/目录保留好或者配置离线包源。这个目录里的package/、utils/等子目录对于普通应用开发来说可以暂时不动。但如果你想裁剪系统减小最终 rootfs 镜像的体积那configs/里每一项BR2_PACKAGE_*都值得认真过一遍。3.4 device 与 target板级配置的“总闸”在我看来整个 SDK 里最不能乱动、但也最容易出问题的目录就是device/。以 CQR40 平台为例路径一般会深入到device/config/chips/cqr40/下面可能还有configs/这样的子目录。这里存放的是芯片级和板级配置包括系统配置文件常见是sys_config.fex或更通用的config.fex、分区表image.cfg、环境变量env.cfg以及板级设备树文件。这些文件会被打包脚本读取直接影响最终镜像的分区布局、启动参数和外设配置。如果你需要调整某个 GPIO 的功能、换一个默认的屏幕分辨率或者修改默认串口波特率多半都要来这里改配置。target/目录则更像是一个“打包规则集合”它定义了 out 目录下固件包的组成方式。很多奇怪的问题比如“内核起来了但 rootfs 挂不上”、“烧录完整个系统反复重启”最后都能追溯到device/和target/之间的配置不一致。所以我分析 SDK 时会把device/config/chips/cqr40下的文件全部列成一张速查表包括每个文件对应哪个模块、被哪些脚本引用这样后面定位问题会快很多。3.5 scripts、tools 和 out别忽略“胶水层”scripts/和tools/是 SDK 的“胶水层”。顶层build.sh会调用一系列脚本比如设置交叉编译环境、选择平台配置、执行各模块的make最后再调用打包工具生成烧录镜像。tools/里常见的是烧录工具、签名工具和调试辅助工具。分析这些脚本时不需要逐行读重点看两个东西变量定义和函数调用顺序。比如build.sh里那些export CROSS_COMPILE...就是决定你用哪个工具链的关键。另外out/目录在首次编译前可能不存在它是构建系统自动创建的。很多人第一眼看到 SDK 没有out/就以为自己拿到的是不完整的包其实不是跑一次构建后自然就有了。我习惯把out/叫做“所有罪案的案发现场”。编译报错、打包失败、找不到文件大部分日志都集中在这里。分析目录时提前确认out/在什么路径、日志写到哪个子目录能帮你省下不少找日志的时间。4. 实操方法从一棵目录树到一份分析文档4.1 用工具快速生成全景目录面对一个几十 GB 的 SDK靠鼠标一层层点文件夹不是不行但效率太低。我全程在 Linux 下操作用的第一个命令就是treetree -L 3 -d --dirsfirst-L 3表示最多只展开三层目录-d表示只看目录不看文件。如果系统里没有 tree可以用 find 代替find . -maxdepth 3 -type d | sort这一步的目标不是把目录全部塞进脑子里而是快速识别出 SDK 的整体形状。输出结果可以保存成文件比如tree.txt后面写分析记录时直接用。不要一上来就递归完整目录否则屏幕会直接刷到让人怀疑人生。拿到这份目录树之后我再结合du -sh检查哪些子目录体积最大du -sh */体积大的往往是内核源码、buildroot 源码包、或者预编译产物。这些信息能帮助你判断一套 SDK 里哪些模块是“大头”后续如果要裁剪镜像心里会有数。4.2 顺着编译脚本分析主流程目录树只能告诉你“有什么”但“怎么构建”要看脚本。我会用grep和编辑器把顶层build.sh读一遍重点看它调用的函数名和涉及的脚本路径。常见的方式是在脚本里搜索以下关键词grep -n source\|\. \|export\|make\|function build.sh实际分析时我发现 CQR40 平台 SDK 的构建脚本会把一系列子模块的编译打包到一个总的流程里。它可能有多个分支比如build.sh kernel、build.sh boot、build.sh rootfs、build.sh pack之类的命令。这个设计非常实用意味着你不必每次全量编译。顺着脚本找到变量定义也很关键例如grep -n CQR\|CROSS_COMPILE\|ARCH\|OUT_DIR scripts/build_env.sh我把这些变量整理出来后就会搞清楚 SDK 默认目标架构、输出目录和工具链。以后再看到“某个脚本突然找不到交叉编译器”多半是环境变量没被正确 source。4.3 建立关键路径速查表分析文件夹不是看完就完了我会把结果落成文档。我在 CQR40 平台的项目里维护了一个DIRECTORY.md内容就是一个大表格记录模块、源码目录、配置文件、编译命令、产物路径。这个文档最大的价值在于“可检索”。几个月后你再回头改这个平台不用重新翻一遍 SDK直接打开DIRECTORY.md查表即可。下面是我当时记录的简化示例模块源码/配置路径常见操作产物引导程序brandy/配置板级头文件uboot.bin、boot0内核kernel/linux-x.y/make xxx_cqr40_defconfigImage、dtb设备树device/config/chips/cqr40/修改 dts/dtsidtb根文件系统buildroot/修改 BR2_PACKAGE_ 选项rootfs 镜像打包脚本scripts/、target/pack 命令最终固件包建立速查表要注意一点路径必须写相对 SDK 根目录的相对路径不要写你自己机器的绝对路径否则换一台电脑整个文档就废了。5. 常见问题与排查技巧实录5.1 板级配置对不上启动串口一片空白CQR40 平台这套 SDK 里最让我头疼的问题是板级配置不一致。具体情况是设备树里配的串口是 UART0但sys_config.fex里默认配置却把引脚复用到了 UART3 上。内核起来后没有任何输出你会以为内核压根没起来其实内核已经在跑了数据只是从另一个串口出去了。排查思路非常直接先确认整套 SDK 里同一个外设被配置了几个地方。常见位置有设备树.dts/.dtsidevice/config/chips/cqr40/configs/下的.fex或.json文件Uboot 的板级头文件然后用grep一次性搜出所有相关配置项grep -rn uart0\|uart3\|s_uart device/config/chips/cqr40/ arch/arm64/boot/dts/经验之谈遇到启动无输出先不要怀疑代码先检查配置口径是否一致。5.2 磁盘被 out 目录和 buildroot 灌满SDK 编译起来非常吃磁盘。CQR40 平台完整编一遍out/下可能会有将近 20GB 的中间产物buildroot 还会额外膨胀。如果你还保留了历史构建产物磁盘很容易爆。我的清理策略是把“增量产物”和“最终产物”分开看。out/下包括各个模块的中间文件真正要保留的是最终打包出来的固件。我通常会在清空out/之前先执行一次打包把最终固件复制出来然后再跑make clean或直接删掉中间目录。buildroot 则可以通过它自己的 clean 命令来局部清理make -C buildroot clean这个命令有时候会把编译好的工具链也清掉重新编译很耗时建议大家先用du -sh buildroot/*看清各子目录占用再决定删到什么程度。5.3 符号链接失效导致内核路径找不到CQR40 平台 SDK 里有一些目录是符号链接比如kernel下的某个链接指向具体的内核版本目录。如果你把 SDK 从 Windows 环境拷贝到 Linux或者用某些解压工具解压符号链接可能会变成普通文本文件或直接失效。之后执行编译时系统就找不到内核源码了。遇到这种情况不用慌先检查ls -l kernel/ file 某个链接如果确实失效了用ln -sfn重新建立正确的链接关系。我踩过这个坑之后就给自己立了条规矩SDK 一律在 Linux 环境解压、复制、编译绝不经过 Windows 中转。5.4 新旧 SDK 目录结构差异同一个硬件平台不同版本的 SDK 目录结构可能变化很大。早期版本可能直接把内核放在顶层linux/下新版本可能把内核挪到kernel/linux-x.y/下。跨版本升级时如果你把旧路径背得滚瓜烂熟容易在新 SDK 里碰壁。我的处理方式是拿到新版后先做一次目录 diff用自己整理的DIRECTORY.md去对比。优先关注这几处构建脚本名称、芯片配置路径、烧录工具调用方式。只要这三样能对上其他目录就算多几个少几个影响都不大。6. 从目录分析延伸到后续维护6.1 让分析文档跟着项目一起演进文件夹分析不是一次性工作。SDK 会更新、项目会定制、同事会踩坑文档必须持续维护。我现在习惯把每次新发现的问题和对应目录路径直接追加到DIRECTORY.md里而不是单独记在聊天记录或者草稿里。这样团队里的新人拿到文档后可以少走很多弯路。6.2 为自动化构建做准备目录分析清楚之后可以顺手把关键变量固化下来做一套可重复的构建脚本甚至接入 CI。当时我基于这份分析文档把“拉代码 → 配置交叉编译环境 → 编译内核 → 编译 buildroot → 打包”写成了一条可复现的流水线。有了这一步想每次提交代码后自动出固件就非常轻松了。6.3 根据目录热点指导系统裁剪最后再说一个延伸用途。目录体积分析能直观反映哪些模块占空间大。如果做量产项目需要精简固件我就直接对照du的结果和 buildroot 的配置项把没用的测试工具、示例程序、大体积可选库从配置里去掉。这个操作不需要动代码只需要知道每个包在 buildroot 里的开关名就行。CQR40 平台上的 rootfs 一旦删除无用包体积可以降得很明显系统启动速度也会提升。我个人在多次 SDK 分析之后最大的体会是目录结构不是摆设它就是芯片厂商设计思路的说明书。把这份说明书读透比拿源码硬啃要高效得多。以后再拿到一套陌生 SDK花两天整理目录和构建流程绝对是一笔划算的投资。