2. 嵌入式Linux基础
在嵌入式Linux项目开发中,Qt应用程序最终要运行在ARM、RISC-V或MIPS等目标板上,而开发工作通常在x86_64主机上完成。理解Linux文件系统、Shell命令、进程与线程模型,以及交叉编译工具链的搭建方式,是后续编译Qt、部署程序、排查运行问题的前提。本节从主机与目标板协同开发的角度,梳理这些基础概念及其实际用法。
一、Linux文件系统:一切皆文件
Linux文件系统采用树形目录结构,根目录为/,所有设备、进程信息、内核参数都以文件形式暴露。嵌入式系统常见的根文件系统类型包括ext4、squashfs、ubifs、jffs2和initramfs。其中squashfs只读压缩、占用小,适合量产固件;ubifs支持磨损均衡,适合NAND Flash;ext4常用于eMMC或SD卡。
开发中需要重点区分几个目录:/bin与/sbin存放基础命令,/lib与/usr/lib存放动态库,/etc存放配置,/dev存放设备节点,/proc与/sys是内核运行时信息的虚拟文件系统,/tmp通常挂载为tmpfs。Qt程序依赖的libQt5Core.so、libQt5Gui.so、平台插件libqlinuxfb.so等,必须放到目标板可搜索的库路径中,否则会出现“cannot open shared object file”。
echo $LD_LIBRARY_PATH,并用 ldd ./myapp 查看每个依赖库的解析结果。若显示“not found”,说明库缺失或路径未加入。二、Shell命令:开发与调试的基本功
嵌入式开发中,Shell是连接主机与目标板最直接的工具。常用命令可分为文件操作、权限管理、进程查看、网络传输和系统信息五类。下面列出高频命令清单:
| 类别 | 命令 | 典型用途 |
|---|---|---|
| 文件操作 | ls -l、cp -a、rm -rf、tar -xzf | 查看权限、复制目录、解压根文件系统 |
| 权限管理 | chmod 755、chown root:root | 设置可执行权限、修改属主 |
| 进程查看 | ps aux、top、kill -9 | 查看Qt进程、结束卡死程序 |
| 网络传输 | scp、ssh、ping、ifconfig | 向目标板传程序、验证网络 |
| 系统信息 | uname -a、df -h、free -m、dmesg | 确认内核架构、磁盘与内存、驱动日志 |
例如,将交叉编译好的Qt程序部署到目标板并运行,典型流程如下:
# 主机端:拷贝可执行文件与依赖库
scp -r ./bin/myapp root@192.168.1.10:/opt/app/
scp -r ./lib/*.so* root@192.168.1.10:/usr/lib/
# 目标板端:设置库路径并运行
export LD_LIBRARY_PATH=/usr/lib:/opt/app/lib:$LD_LIBRARY_PATH
chmod +x /opt/app/myapp
/opt/app/myapp -platform linuxfb
三、进程与线程:Qt程序运行的基础模型
Linux中进程是资源分配的基本单位,线程是CPU调度的基本单位。每个进程拥有独立的虚拟地址空间、文件描述符表和信号处理表;同一进程内的多个线程共享地址空间和文件描述符,但各自拥有独立的栈和寄存器上下文。Qt的QThread、QThreadPool、QtConcurrent都建立在pthread之上。
嵌入式Qt开发中,GUI主线程负责事件循环和界面刷新,耗时操作(串口读写、网络请求、文件解析)应放入工作线程,否则界面会卡顿。线程间通信不能直接操作对方控件,应通过信号槽、QMetaObject::invokeMethod或队列连接完成。进程间通信则常用管道、共享内存、消息队列、Socket和D-Bus。
kill -9 强制结束Qt程序后,若使用了framebuffer或DRM,可能残留显示状态异常。建议先尝试 kill -TERM 让程序正常退出,再检查 /dev/fb0 或DRM设备是否被占用。四、交叉编译概念与工具链搭建
交叉编译是指在一个平台上生成另一个平台可执行代码的过程。主机是x86_64 Linux,目标板是ARM Cortex-A系列,二者指令集不同,因此不能用主机gcc直接编译。交叉编译工具链通常包含arm-linux-gnueabihf-gcc、g++、ld、ar、strip以及目标平台的C库(glibc或musl)。
工具链获取方式有三种:芯片厂商SDK自带、Buildroot/Yocto生成、Linaro或Bootlin预编译包。搭建时需确认三件事:目标架构(arm/aarch64)、浮点ABI(gnueabihf/gnueabi)、C库类型。三者不匹配会导致链接失败或运行时段错误。
# 解压工具链并加入PATH
tar -xJf gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf.tar.xz -C /opt/
export PATH=/opt/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin:$PATH
# 验证交叉编译器
arm-none-linux-gnueabihf-gcc -v
arm-none-linux-gnueabihf-gcc hello.c -o hello_arm
file hello_arm
# 输出应包含:ELF 32-bit LSB executable, ARM, EABI5
Qt交叉编译时,需要先配置qtbase/mkspecs/devices/linux-arm-gnueabihf-g++/qmake.conf,指定QMAKE_CC、QMAKE_CXX、QMAKE_LINK和sysroot路径,再执行./configure -xplatform linux-arm-gnueabihf-g++ -sysroot /opt/sysroot ...。编译完成后,用file和readelf -h确认生成的Qt库是ARM格式,而不是主机x86_64格式。
五、常见误区与复盘
初学者最容易踩的坑集中在三处。第一,工具链ABI不匹配:用gnueabi编译的程序放到gnueabihf目标板上,浮点参数传递方式不同,运行时会崩溃或计算结果异常。第二,库路径混乱:主机上安装了Qt,目标板上却没有对应版本,ldd显示“not found”,此时应检查LD_LIBRARY_PATH和/etc/ld.so.conf。第三,平台插件缺失:Qt程序启动时报“This application failed to start because no Qt platform plugin could be initialized”,说明platforms/目录下缺少libqlinuxfb.so或libqeglfs.so,需将插件目录一并部署。
file确认架构、ldd确认依赖、readelf -d确认RPATH/RUNPATH。这三步能在部署前拦截绝大多数“编译通过、运行失败”的问题。