uboot
//uboot编译时,help帮助为了详细,需要把单板配置头文件(比如include/configs/imx8qm_mek_android_auto.h)的这个配置 不要 #undef:
#undef CONFIG_SYS_LONGHELP
嵌入式linux
编译linux kernel时,如何修改makefile等,让编译后的版本在开机时有自己姓名的特征打印?
有几种方法可以在编译 Linux 内核时添加自定义特征打印。以下是几种常见的实现方式:
方法1:修改版本信息
修改 Makefile 文件
在内核根目录的 Makefile 中修改版本信息:
# 在文件开头附近找到版本定义
NAME = Your Name's Kernel
# 修改 EXTRAVERSION(大约在第4行)
VERSION = 6
PATCHLEVEL = 1
SUBLEVEL = 0
EXTRAVERSION = -custom-by-yourname
方法2:使用内核配置选项
1. 修改 .config 文件
# 在配置中添加
CONFIG_LOCALVERSION="-custom-by-yourname"
2. 或者通过 menuconfig
make menuconfig
# 进入 General setup -> Local version
# 添加: -custom-by-yourname
完整的编译步骤
# 1. 获取内核源码
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz
tar xvf linux-6.1.tar.xz
cd linux-6.1
# 2. 应用修改(选择上述方法之一)
# 修改 init/main.c 或 Makefile
# 3. 配置内核
make defconfig
# 4. 可选:进一步配置
make menuconfig
# 5. 编译内核
make -j$(nproc)
# 6. 安装模块
sudo make modules_install
# 7. 安装内核
sudo make install
# 8. 更新引导配置
sudo update-grub # 对于 Debian/Ubuntu
# 或者 sudo grub2-mkconfig -o /boot/grub2/grub.cfg # 对于 RHEL/CentOS
# 9. 重启
sudo reboot
验证结果
重启后,使用以下命令查看启动信息:
dmesg | grep -i "custom\|yourname"
# 或者
journalctl -b 0 | grep -i "custom\|yourname"
高级定制
你还可以创建更复杂的特征显示:
// 在 init/main.c 中添加
static void __init display_custom_info(void)
{
printk(KERN_NOTICE "\n");
printk(KERN_NOTICE "**************************************************\n");
printk(KERN_NOTICE "* Custom Linux Kernel by [你的姓名] *\n");
printk(KERN_NOTICE "* Build Date: %s %s *\n", __DATE__, __TIME__);
printk(KERN_NOTICE "* Contact: your.email@example.com *\n");
printk(KERN_NOTICE "**************************************************\n");
printk(KERN_NOTICE "\n");
}
// 在 start_kernel() 中调用
display_custom_info();
这样编译后的内核在启动时就会显示你的个性化信息了。
//编译代码时,一般用:
make -j16
等,但是对于性能差的电脑不合适,可以修改为自适应进程数:
make -j$(nproc)
//打印时间戳尽量用后者:
trinket:/ # date
Fri Sep 6 09:47:46 CST 2024
trinket:/ # date +"%m-%d %H:%M:%S"
09-06 09:48:01
//查询内核EXPORT_SYMBOL 导出的符号:
sudo cat /proc/kallsyms
#!/bin/sh
make mrproper
export KBUILD_OUTPUT=$(pwd)/_output/
make 100ask_imx6ull_defconfig
#printenv
make zImage -j4 V=1 2>&1 | tee ./_build_kernel_log.txt
cp $(pwd)/_output/arch/arm/boot/zImage ~/nfs_rootfs
//设备初始化举例:
有28和29这2个地址的max20086,其中29地址没有贴片,则会发现内核初始化时:
mek_8q_raite:/ # find / -iname "*max20086*" 2>/dev/null
/sys/firmware/devicetree/base/i2c@5a820000/max20086_28@28 //设备树中有28和29这2个地址都有
/sys/firmware/devicetree/base/i2c@5a820000/max20086_29@29
/sys/bus/i2c/drivers/max20086_29 //没有指向/sys/devices/platform的29地址芯片的链接
/sys/bus/i2c/drivers/max20086_28 //有指向/sys/devices/platform的28地址芯片的链接
/sys/devices/platform/5a820000.i2c/i2c-4/4-0028/max20086_reg // 在/sys/devices/platform只有28设备
即如下:
max20086_29mek_8q_raite:/sys/firmware/devicetree/base/iomuxc/imx8qm-mek/max20086_29 # ls -al /sys/bus/i2c/drivers/max20086_28
total 0
drwxr-xr-x 2 root root 0 2022-01-01 00:02 .
drwxr-xr-x 20 root root 0 1970-01-01 08:00 ..
lrwxrwxrwx 1 root root 0 2022-01-01 00:02 4-0028 -> ../../../../devices/platform/5a820000.i2c/i2c-4/4-0028
--w------- 1 root root 4096 2022-01-01 00:02 bind
--w------- 1 root root 4096 2022-01-01 00:02 uevent
--w------- 1 root root 4096 2022-01-01 00:02 unbind
mek_8q_raite:/sys/firmware/devicetree/base/iomuxc/imx8qm-mek/max20086_29 # ls -al /sys/bus/i2c/drivers/max20086_29
total 0
drwxr-xr-x 2 root root 0 2022-01-01 00:02 .
drwxr-xr-x 20 root root 0 1970-01-01 08:00 ..
--w------- 1 root root 4096 2022-01-01 00:02 bind
--w------- 1 root root 4096 2022-01-01 00:02 uevent
--w------- 1 root root 4096 2022-01-01 00:02 unbind
mek_8q_raite:/sys/firmware/devicetree/base/iomuxc/imx8qm-mek/max20086_29 #
//打开系统中所有的动态调试信息:
echo -n '+p' > /sys/kernel/debug/dynamic_debug/control
设置 /proc/sys/kernel/printk 以尽可能多地打开日志:
echo "8,8,8,7" > /proc/sys/kernel/printk
//如果一套代码给几个项目公用,通过切分支来区分(比如P20项目和D600项目),那么编译脚本中就需要填export OUT_DIR=_out_p20或者export OUT_DIR=_out_d600来区分不同项目的编译结果,如果仅仅一个项目,就不必了.
//unixbench 的跑分用法:
wget http://soft.laozuo.org/scripts/UnixBench5.1.3.tgz
tar xf UnixBench5.1.3.tgz
cd UnixBench
make
./Run
# 无需图像测试
vim Makefile
# 注释掉 GRAPHIC_TESTS = defined
./Run #开始跑分
也可以通过一键脚本执行测试
wget <http://www.vpsmark.com/sh/unixbench.sh>
sh ./unixbench.sh
//cmdline的配置 如
console=hvc0 vmalloc=400M DFTC_IVI_CONFIG=01_0x2000000000000000 DFTC_IC_CONFIG=01_0x2000000000000000
可以通过__setup 来获取,类似M57项目的如下:
__setup("DFTC_IVI_CONFIG=", guest_get_ivi_config);
//从对方IP地址接收文件
tftp -gr 文件名 对方IP地址
//向对方IP地址发送文件
tftp -pr 文件名 对方IP地址
//常用添加打印:
printk("nisy_%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
查询内核打印的配置:
cat /proc/sys/kernel/printk
关闭打印:
echo 0 > /proc/sys/kernel/printk
打开所有打印:
echo 8 > /proc/sys/kernel/printk
//查看运行中Linux的内核配置选项
zcat /proc/config.gz
需要开启内核配置项CONFIG_IKCONFIG才会生成这个文件
//强制关闭串口打印:
echo 0 > /proc/sys/kernel/printk
//DTC除了可以编译.dts文件以外,其实也可以“反汇编”.dtb文件为.dts文件,其指令格式为:
dtc -I dtb -O dts -o xxx.dts arch/arm/boot/dts/xxx.dtb
用这种方式把树莓派的设备树“还原”出来。对于安卓系统,通过如下导出设备树:
adb pull /sys/firmware/fdt .
然后再生成dts.
//Linux kernal的文档使用rst结构化文本编写,阅读kernal\msm-4.1.4\README文档可知,可以通过
make htmldocs
生成可读的html,不过也可以直接在
https://www.kernel.org/doc/html/latest/index.html#
就能搜索。
//打印编译fail的某个.c文件的完整编译命令,内核原始makefile有v=1来开启,也可以在直接篡改一下: 原始:
# Use 'make V=1' to see the full commands
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
修改后:
# Use 'make V=1' to see the full commands
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = 1
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 1
endif
// 对于某些定位问题的时候需要时间戳,可以使用 ts命令 在每一行行首增加了时间戳。下面给出另一个例子:
$ ls -l | ts
Aug 21 13:34:25 total 120
Aug 21 13:34:25 drwxr-xr-x 2 sk users 12288 Aug 20 20:05 Desktop
Aug 21 13:34:25 drwxr-xr-x 2 sk users 4096 Aug 10 18:44 Documents
Aug 21 13:34:25 drwxr-xr-x 24 sk users 12288 Aug 21 13:06 Downloads
[...]
--- 来自文章 (linux.cn/article-996… 《一套有用的 Unix 实用程序 》) 类似地,可以用sudo execsnoop |ts 来记录函数调用的时间戳。
或者使用华为的ipop工具,带有串口监视器功能,自带打印时间戳.
//Q:linux下有多套gcc版本,如何手工切换需要的版本?
A:使用 update-alternatives 选择某个版本的gcc: 往系统添加一个 arm-linux-guneabi-gcc-5 的链接配置并设置优先级。
$ sudo update-alternatives --install /usr/bin/arm-linux-gnueabi-gcc arm-linux-gnueabi-gcc /usr/bin/arm-linux-gnueabi-gcc-5 5
接下来往系统添加一个 arm-linux-guneabi-gcc-7 的链接配置并设置优先级。
$ sudo update-alternatives --install /usr/bin/arm-linux-gnueabi-gcc arm-linux-gnueabi-gcc /usr/bin/arm-linux-gnueabi-gcc-7 7
使用 update-alternatives 命令来选择一个配置。
$ sudo update-alternatives --config arm-linux-gnueabi-gcc
然后可以看到arm-linux-gnueabi-gcc 指向的是/etc/alternatives/ 的一个自己选择的版本号的gcc
ls -al /usr/bin/arm-linux-gnueabi-gcc
lrwxrwxrwx 1 root root 39 7月 13 2020 /usr/bin/arm-linux-gnueabi-gcc -> /etc/alternatives/arm-linux-gnueabi-gcc-5
//i2ctools软件中的工具 i2ctransfer 和 i2cdump 都可以读芯片的寄存器,命令稍不同,如下: 注意i2cdetect 扫描到只是说明设备树中有这个设备,实际可能这个芯片根本没有贴片.
# i2cdetect -y -a 9
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- 2c -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- 41 42 43 44 45 -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
可以看到bus=9下面有6a,41~45,2c这几个i2c设备.
i2cdump -f -y 9 0x6a
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: ef 00 00 01 00 00 90 51 2d d4 ff e4 91 f3 50 0b ?..?..?Q-?.???P?
10: 00 00 f3 0f e4 9b 00 01 00 00 60 00 f4 ff 40 02 ..????.?..`.?.@?
20: 55 00 0f 00 00 00 00 bf 00 00 00 00 00 00 00 00 U.?....?........
30: fa 45 99 99 b6 00 00 00 00 00 00 2e 24 54 c8 22 ?E???.......$T?"
40: 5a 10 00 a0 00 00 55 ff ef 0f ff 00 00 00 05 00 Z?.?..U.??....?.
50: 00 00 05 00 00 00 05 00 00 00 05 90 51 2d 00 00 ..?...?...??Q-..
60: 45 ff 1f 00 00 12 96 00 50 b0 00 00 00 00 00 00 E.?..??.P?......
70: f0 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ??..............
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
mek_8q_raite:/ #
mek_8q_raite:/ #
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x0 r2
0xef 0x00
mek_8q_raite:/ #
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x2 r2
0x00 0x01
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x4 r2
0x00 0x00
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x6 r2
0x90 0x51
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x8 r2
0x2d 0xd4
mek_8q_raite:/ #
mek_8q_raite:/ #
mek_8q_raite:/ # i2ctransfer -f -y 9 w1@0x6a 0x0 r16
0xef 0x00 0x00 0x01 0x00 0x00 0x90 0x51 0x2d 0xd4 0xff 0xe4 0x91 0xf3 0x50 0x0b
1|mek_8q_raite:/ # i2cget -f -y 9 0x6a 0 //读寄存器0#
0xef
mek_8q_raite:/ # i2cget -f -y 9 0x6a 1 //读寄存器1#
0x00
mek_8q_raite:/ # i2cset -f -y 9 0x6a 1 4 //写寄存器1#
mek_8q_raite:/ # i2cget -f -y 9 0x6a 1 //读寄存器1#
0x04
注意:i2cdump/i2cget 读寄存器只能输入1byte地址,返回1到多字节结果;如果需要输入地址为2byte,则只能使用 i2ctransfer ,比如读max96722的ID寄存器:
console:/ # i2ctransfer -f -y 2 w2@0x29 0x00 0x0d r1 (输入地址2byte=0x000d,返回值1byte)
0xa1 //正确返回,地址为0x000d
console:/ # i2cget -f -y 2 0x29 0x0d
0x00 //错误返回,地址当做0x0d
| 读or写 | 寄存器地址字节数 | 数据量几字节 | 命令 | 含义 |
|---|---|---|---|---|
| 写 | 2 | 1 | i2ctransfer -y -f 4 w3@0x38 0x3a 0x01 0x10 | 向i2c-4号总线0x38设备的0x3a01寄存器写0x10,w3表示寄存器0x3a01和写入值0x10的长度为3字节 |
| 读 | 2 | 1 | i2ctransfer -f -y 2 w2@0x29 0x00 0x0d r1 | 输入地址2byte=0x000d,返回值1byte |
| 读 | 2 | 16 | i2ctransfer -y -f 4 w2@0x38 0x3a 0x01 r16 | 从i2c-4号总线0x38设备的0x3a01寄存器开始读16个字节的数据,w2表示寄存器0x3a01的长度为2个字节 |
| 读 | 1 | 16 | i2ctransfer -f -y 9 w1@0x6a 0x0 r16 | 从0x0地址连续读16个寄存器的值 |
| 读 | 256 |
//截屏: 截取fb1界面到sdcard,文件名为fb1.png
screencap -d /dev/graphics/fb1 sdcard/fb1.png
//使用文件系统节点来操作时,需要注意有可能设备树的修改导致节点路径变更了,比如: 原始: /sys/devices/platform/58226000.i2c/i2c-9/9-006a/max9286_reset 删除了i2c 一个bus后,变成 /sys/devices/platform/58226000.i2c/i2c-8/8-006a/max9286_reset 可能造成上层找不到文件节点,导致功能异常. 上层使用节点,最好使用find 先搜到节点路径,用搜索结果再去操作.
//python循环打印max96722(bus2,addr=0x29)的寄存器的值:
import os
import time
a=0
os.system('adb root')
os.system('adb remount')
s_hex=""
while a < 255:
b='adb shell i2ctransfer -f -y 2 w2@0x29 0x00 '
b += s_hex + hex(a)
b +=' r1'
os.system(b)
a+=1
//dos的命令每隔5秒周期性操作adb shell "factory_test -s fan_switch on" 的参考脚本:
@echo off
adb root
for /l %%i in (1,0,1) do adb shell "factory_test -s fan_switch on" &&choice /T 1 /C ync /CS /D y /n
pause
//打印结构体的内容,使用 print_hex_dump(),类似:
print_hex_dump(KERN_NOTICE,"" ,DUMP_PREFIX_OFFSET ,16,1,sd,sizeof(struct v4l2_subdev),true);
//CPU异常占用分析:
- 使用
top -H -p pid号确认当前进程所有线程CPU占用 - 使用
ps -AT -O psr | grep cpu占用异常pid 确认线程状态以及所运行的cpu
//如果下电/上电正常,但是reboot不正常,请检查芯片的上电流程的下电部分,是否时间太少.
thermal
//polling-delay-passive:温控发生时 的轮询周期。 配置为0,代表不使用轮询方式,通过tsensor中断触发温控。 polling-delay:温控未发生时 轮询周期。
测试
//挂机测试需要同时保存串口日志,机身中的日志比如重启常常只能从串口看到,重启查查来不及保存到日志文件,但是容易从串口吐出来.
yocto编译linux系统
Q:如何查询yocto的版本号
有多种方法可以查询Yocto的版本号,我为你整理了详细的方法:
1. 查看Yocto Project版本(发布代号)
通过层配置文件查看
# 查看Poky层的系列名称(最常用)
grep -r "LAYERSERIES_CORENAMES" sources/poky/meta*/conf/layer.conf
# 或查看LAYERSERIES_COMPAT
grep -r "LAYERSERIES_COMPAT" sources/*/conf/layer.conf
# 示例输出:
# LAYERSERIES_CORENAMES = "kirkstone"
# LAYERSERIES_COMPAT_meta-custom = "kirkstone"
通过DISTRO_VERSION查看
# 查看发行版版本
grep "DISTRO_VERSION" sources/poky/meta-poky/conf/distro/poky.conf
# 示例输出:
# DISTRO_VERSION = "4.0.4" # 对于kirkstone
2. 查看BitBake版本
# 查看bitbake版本
bitbake --version
# 或通过环境变量
bitbake -e | grep "^BITBAKE_VER"
# 直接查看bitbake文件
grep "__version__" sources/poky/bitbake/lib/bb/__init__.py
# 示例输出:
# BitBake Build Tool Core version 2.0.0
3. 通过构建环境查询
在构建环境中查询
# 进入构建环境
source sources/poky/oe-init-build-env build
# 查看所有环境变量中的版本信息
bitbake -e | grep -E "(^DISTRO_VERSION|^LAYERSERIES_CORENAMES|^BITBAKE_VER)"
# 查看具体的版本变量
bitbake -e | grep "^DISTRO_VERSION="
bitbake -e | grep "^LAYERSERIES_CORENAMES="
bitbake -e | grep "^BITBAKE_VER="
使用oe-init-build-env脚本
# 查看oe-init-build-env脚本中的信息
grep -i "version\|release" sources/poky/oe-init-build-env
4. 查看Git分支/标签
# 查看poky仓库的当前分支
cd sources/poky && git branch -a
# 查看所有标签
git tag -l | sort -V
# 查看当前签出的标签
git describe --tags
# 查看提交历史
git log --oneline -5
# 返回项目根目录
cd ../..
5. 查看构建的镜像版本
# 查看构建的镜像中的版本信息
cat build/tmp/deploy/images/*/os-release
# 或在运行的系统上查看
cat /etc/os-release
6. 编写查询脚本 (check-yocto-version.sh)
#!/bin/bash
# Yocto版本查询脚本
echo "=== Yocto Project Version Check ==="
echo ""
# 方法1: 检查LAYERSERIES_CORENAMES
echo "1. Layer Series (发布代号):"
if [ -f "sources/poky/meta/conf/layer.conf" ]; then
grep "LAYERSERIES_CORENAMES" sources/poky/meta/conf/layer.conf
else
find sources -name "layer.conf" -exec grep -l "LAYERSERIES_CORENAMES" {} \; | head -1 | xargs grep "LAYERSERIES_CORENAMES"
fi
echo ""
# 方法2: 检查DISTRO_VERSION
echo "2. Distro Version:"
if [ -f "sources/poky/meta-poky/conf/distro/poky.conf" ]; then
grep "DISTRO_VERSION" sources/poky/meta-poky/conf/distro/poky.conf
fi
echo ""
# 方法3: 检查BitBake版本
echo "3. BitBake Version:"
if command -v bitbake &> /dev/null; then
bitbake --version | head -1
else
echo "bitbake not found in PATH"
fi
echo ""
# 方法4: 检查Git信息
echo "4. Git Repository Info:"
if [ -d "sources/poky/.git" ]; then
cd sources/poky
echo "Branch/Tag: $(git describe --all 2>/dev/null || echo 'Not a git repo')"
echo "Latest Tag: $(git describe --tags --abbrev=0 2>/dev/null || echo 'No tags')"
cd ../..
else
echo "Not a git repository"
fi
echo ""
# 方法5: 已知版本对应表
echo "5. Yocto Release Name Mapping:"
cat << 'EOF'
+-------------------+-----------------+------------------+
| Release Name | Yocto Version | Release Date |
+-------------------+-----------------+------------------+
| kirkstone | 4.0.x | April 2022 |
| dunfell | 3.1.x | April 2020 |
| gatesgarth | 3.2.x | October 2020 |
| hardknott | 3.3.x | April 2021 |
| honister | 3.4.x | October 2021 |
| kirkstone | 4.0.x | April 2022 |
| langdale | 4.1.x | October 2022 |
| mickledore | 4.2.x | April 2023 |
| nanbield | 4.3.x | October 2023 |
| scarthgap | 5.0.x | April 2024 |
+-------------------+-----------------+------------------+
EOF
7. 快速查询命令总结
# 一键查询所有版本信息
echo "1. Release Name: $(find sources -name "layer.conf" -exec grep -h "LAYERSERIES_CORENAMES" {} \; 2>/dev/null | head -1)"
echo "2. Distro Version: $(grep -h "DISTRO_VERSION" sources/poky/meta-poky/conf/distro/poky.conf 2>/dev/null)"
echo "3. BitBake: $(bitbake --version 2>/dev/null | head -1)"
echo "4. Git Branch: $(cd sources/poky 2>/dev/null && git branch --show-current 2>/dev/null || echo "N/A")"
8. 通过构建输出查看
# 查看构建日志中的版本信息
find build/tmp/work -name "log.do_*" -exec grep -l "YOCTO\|Poky" {} \; | head -3
# 查看包版本
bitbake -s | grep "^poky"
9. 常见Yocto版本对照表
| 发布代号 | Yocto版本 | 发布日期 | 支持状态 |
|---|---|---|---|
| scarthgap | 5.0.x | 2024年4月 | 支持到2026年 |
| nanbield | 4.3.x | 2023年10月 | 支持到2025年 |
| mickledore | 4.2.x | 2023年4月 | 支持到2024年10月 |
| langdale | 4.1.x | 2022年10月 | 已结束 |
| kirkstone | 4.0.x | 2022年4月 | LTS,支持到2026年 |
| honister | 3.4.x | 2021年10月 | 已结束 |
| hardknott | 3.3.x | 2021年4月 | 已结束 |
| gatesgarth | 3.2.x | 2020年10月 | 已结束 |
| dunfell | 3.1.x | 2020年4月 | LTS,支持到2024年 |
10. 检查脚本示例
#!/bin/bash
# yocto-version-info.sh
check_version() {
echo "=== Yocto Project Information ==="
echo ""
# 检查是否在Yocto环境中
if [ ! -d "sources/poky" ]; then
echo "Error: Not in a Yocto project directory!"
echo "Please run this script from your Yocto project root."
return 1
fi
# 1. 发布代号
echo "Release Codename:"
local series=$(find sources -name "layer.conf" -type f -exec grep -h "LAYERSERIES_CORENAMES" {} \; 2>/dev/null | head -1 | awk -F'"' '{print $2}')
if [ -n "$series" ]; then
echo " $series"
else
echo " Not found"
fi
# 2. 版本号
echo "Distro Version:"
local version=$(grep -h "DISTRO_VERSION" sources/poky/meta-poky/conf/distro/poky.conf 2>/dev/null | awk -F'"' '{print $2}')
if [ -n "$version" ]; then
echo " $version"
else
echo " Not found"
fi
# 3. BitBake版本
echo "BitBake Version:"
if command -v bitbake >/dev/null 2>&1; then
bitbake --version | head -1 | sed 's/^/ /'
else
echo " Not in PATH"
fi
# 4. 层版本兼容性
echo "Layer Compatibility:"
find sources -name "layer.conf" -type f -exec sh -c 'echo " $(dirname $(dirname $0)): $(grep -h "LAYERSERIES_COMPAT" $0 2>/dev/null || echo "Not specified")"' {} \; 2>/dev/null
}
# 执行检查
check_version
使用建议:
-
对于日常使用:主要关注 发布代号(如kirkstone、dunfell等)
-
对于bug报告:需要提供完整的版本信息,包括:
- 发布代号
- 具体版本号
- BitBake版本
- 使用的层和版本
-
对于兼容性检查:关注
LAYERSERIES_COMPAT变量,确保所有层兼容同一版本
最简单的方法通常是:
# 查看当前Yocto版本(发布代号)
grep -r "LAYERSERIES_CORENAMES" sources/poky/meta/conf/layer.conf
这个会显示类似LAYERSERIES_CORENAMES = "kirkstone"的结果,告诉你使用的是哪个Yocto版本。
Q:yocto最小demo
我来为你创建一个Yocto最小化示例项目。这个Demo将展示如何构建一个最小的Linux系统。
Yocto最小Demo项目结构
1. 项目目录结构
my-yocto-minimal/
├── README.md
├── sources/
│ ├── poky/ # 从Yocto Project获取
│ ├── meta-openembedded/ # 从Yocto Project获取
│ └── meta-custom/ # 自定义层
├── build/ # 构建目录
├── setup.sh # 环境设置脚本
├── conf/
│ ├── bblayers.conf # 层配置
│ └── local.conf # 本地配置
└── scripts/
└── build-image.sh # 构建脚本
2. 初始化脚本 (setup.sh)
#!/bin/bash
# Yocto最小Demo初始化脚本
set -e
echo "=== Yocto Minimal Demo Setup ==="
# 创建目录结构
mkdir -p sources build/conf
# 克隆poky(使用最新的kirkstone分支)
if [ ! -d "sources/poky" ]; then
echo "Cloning poky repository..."
git clone -b kirkstone git://git.yoctoproject.org/poky.git sources/poky
fi
# 克隆meta-openembedded
if [ ! -d "sources/meta-openembedded" ]; then
echo "Cloning meta-openembedded repository..."
git clone -b kirkstone git://git.openembedded.org/meta-openembedded sources/meta-openembedded
fi
# 创建自定义层
if [ ! -d "sources/meta-custom" ]; then
echo "Creating custom layer..."
mkdir -p sources/meta-custom
mkdir -p sources/meta-custom/conf/layer
mkdir -p sources/meta-custom/recipes-core/images
mkdir -p sources/meta-custom/recipes-example/example
# 创建layer.conf
cat > sources/meta-custom/conf/layer.conf << 'EOF'
# We have a conf and classes directory, add to BBPATH
BBPATH .= ":${LAYERDIR}"
# We have recipes-* directories, add to BBFILES
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
${LAYERDIR}/recipes-*/*/*.bbappend"
BBFILE_COLLECTIONS += "meta-custom"
BBFILE_PATTERN_meta-custom = "^${LAYERDIR}/"
BBFILE_PRIORITY_meta-custom = "6"
LAYERSERIES_COMPAT_meta-custom = "kirkstone"
EOF
fi
# 创建最小镜像配方
cat > sources/meta-custom/recipes-core/images/core-image-minimal-custom.bb << 'EOF'
SUMMARY = "A minimal custom image"
LICENSE = "MIT"
inherit core-image
IMAGE_INSTALL = " \
packagegroup-core-boot \
base-files \
base-passwd \
busybox \
sysvinit \
initscripts \
${CORE_IMAGE_EXTRA_INSTALL} \
"
# 添加一些基本工具
IMAGE_INSTALL += " \
kernel-modules \
udev \
"
# 设置root密码为空(仅用于演示)
EXTRA_USERS_PARAMS = "usermod -p '' root;"
# 确保镜像很小
IMAGE_FEATURES = ""
IMAGE_ROOTFS_SIZE ?= "8192"
IMAGE_OVERHEAD_FACTOR = "1.0"
# 添加自定义欢迎信息
ROOTFS_POSTPROCESS_COMMAND += "add_welcome_message;"
add_welcome_message() {
echo "Welcome to Yocto Minimal Demo!" > ${IMAGE_ROOTFS}/etc/issue
echo "Built on $(date)" >> ${IMAGE_ROOTFS}/etc/issue
}
EOF
# 创建一个简单的hello-world示例配方
cat > sources/meta-custom/recipes-example/example/hello-world_1.0.bb << 'EOF'
SUMMARY = "Simple hello-world application"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://hello-world.c \
file://Makefile"
S = "${WORKDIR}"
do_compile() {
oe_runmake
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello-world ${D}${bindir}
}
EOF
# 创建hello-world源码
cat > sources/meta-custom/recipes-example/example/hello-world/hello-world.c << 'EOF'
#include <stdio.h>
int main() {
printf("Hello from Yocto Minimal Demo!\n");
printf("This is a custom application.\n");
return 0;
}
EOF
# 创建Makefile
cat > sources/meta-custom/recipes-example/example/hello-world/Makefile << 'EOF'
CC = ${CC}
CFLAGS = ${CFLAGS}
all: hello-world
hello-world: hello-world.c
$(CC) $(CFLAGS) -o hello-world hello-world.c
clean:
rm -f hello-world
EOF
# 创建bblayers.conf模板
cat > conf/bblayers.conf << 'EOF'
# LAYER_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
LCONF_VERSION = "7"
BBPATH = "${TOPDIR}"
BBFILES ?= ""
BBLAYERS ?= " \
##OEROOT##/sources/poky/meta \
##OEROOT##/sources/poky/meta-poky \
##OEROOT##/sources/meta-openembedded/meta-oe \
##OEROOT##/sources/meta-openembedded/meta-python \
##OEROOT##/sources/meta-openembedded/meta-networking \
##OEROOT##/sources/meta-custom \
"
EOF
# 创建local.conf模板
cat > conf/local.conf << 'EOF'
MACHINE ?= "qemux86-64"
DISTRO ?= "poky"
PACKAGE_CLASSES ?= "package_rpm"
# 使用内存中的tmpfs加速构建(需要足够内存)
# INHERIT += "rm_work"
# 设置下载目录
DL_DIR ?= "${TOPDIR}/../downloads"
SSTATE_DIR ?= "${TOPDIR}/../sstate-cache"
TMPDIR = "${TOPDIR}/tmp"
# 启用并行构建
BB_NUMBER_THREADS ?= "${@oe.utils.cpu_count()}"
PARALLEL_MAKE ?= "-j ${@oe.utils.cpu_count()}"
# 添加helloworld到镜像
IMAGE_INSTALL_append = " hello-world"
# 配置qemu模拟器
# ACCEPT_FSL_EULA = "1"
# 使用systemd(可选)
# DISTRO_FEATURES_append = " systemd"
# VIRTUAL-RUNTIME_init_manager = "systemd"
# DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
# VIRTUAL-RUNTIME_initscripts = ""
EOF
echo "=== Setup complete ==="
echo ""
echo "To start building:"
echo "1. source setup-environment.sh"
echo "2. bitbake core-image-minimal-custom"
echo ""
echo "Or run: ./scripts/build-image.sh"
3. 环境设置脚本 (setup-environment.sh)
#!/bin/bash
# Yocto环境设置脚本
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(realpath "$SCRIPT_DIR/..")"
if [ "$0" = "$BASH_SOURCE" ]; then
echo "Error: This script must be sourced!"
echo "Use: source $0"
exit 1
fi
# 设置环境变量
export PROJECT_ROOT
export BUILDDIR="$PROJECT_ROOT/build"
export OEROOT="$PROJECT_ROOT/sources/poky"
# 检查必要的目录
if [ ! -d "$OEROOT" ]; then
echo "Error: poky not found at $OEROOT"
echo "Run ./setup.sh first"
return 1
fi
# 进入构建目录
if [ ! -d "$BUILDDIR" ]; then
mkdir -p "$BUILDDIR"
fi
cd "$BUILDDIR" || return 1
# 替换bblayers.conf中的路径
sed "s|##OEROOT##|$PROJECT_ROOT|g" \
"$PROJECT_ROOT/conf/bblayers.conf" > "$BUILDDIR/conf/bblayers.conf"
# 复制local.conf
cp "$PROJECT_ROOT/conf/local.conf" "$BUILDDIR/conf/"
# 设置OE环境
echo "Setting up Yocto environment..."
source "$OEROOT/oe-init-build-env" "$BUILDDIR"
echo ""
echo "=== Yocto环境已设置 ==="
echo "项目根目录: $PROJECT_ROOT"
echo "构建目录: $BUILDDIR"
echo ""
echo "可用命令:"
echo " bitbake core-image-minimal-custom # 构建最小镜像"
echo " bitbake hello-world # 构建单个包"
echo " bitbake -c menuconfig virtual/kernel # 配置内核"
echo ""
4. 构建脚本 (scripts/build-image.sh)
#!/bin/bash
# 构建Yocto镜像脚本
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(realpath "$SCRIPT_DIR/..")"
echo "=== Starting Yocto Minimal Demo Build ==="
echo "Project root: $PROJECT_ROOT"
# 检查环境
if [ ! -d "$PROJECT_ROOT/sources/poky" ]; then
echo "Error: Yocto not initialized. Run ./setup.sh first"
exit 1
fi
# 设置环境
cd "$PROJECT_ROOT" || exit 1
if [ -f "setup-environment.sh" ]; then
echo "Sourcing environment..."
source setup-environment.sh
else
echo "Setting up environment..."
export BUILDDIR="$PROJECT_ROOT/build"
export OEROOT="$PROJECT_ROOT/sources/poky"
source "$OEROOT/oe-init-build-env" "$BUILDDIR"
fi
# 开始构建
echo "Starting build..."
MACHINE="${1:-qemux86-64}"
IMAGE="${2:-core-image-minimal-custom}"
echo "Building for MACHINE=$MACHINE, IMAGE=$IMAGE"
bitbake "$IMAGE"
echo ""
echo "=== Build Complete ==="
echo "Image location: $BUILDDIR/tmp/deploy/images/$MACHINE/"
echo ""
echo "To run with QEMU:"
echo " runqemu $MACHINE nographic"
5. README.md
# Yocto最小Demo
这是一个Yocto Project的最小化示例,展示如何构建一个自定义的嵌入式Linux系统。
## 特性
- 基于Yocto Project Kirkstone LTS
- 最小化Linux系统
- 包含自定义应用
- 支持QEMU模拟器
## 快速开始
### 1. 系统要求
- Ubuntu 20.04/22.04或类似Linux发行版
- 至少50GB磁盘空间
- 8GB以上RAM(推荐16GB)
### 2. 安装依赖
```bash
sudo apt-get update
sudo apt-get install -y gawk wget git diffstat unzip texinfo gcc build-essential \
chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils \
iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev \
pylint3 xterm python3-subunit mesa-common-dev zstd liblz4-tool
3. 初始化项目
# 克隆项目
git clone <repository-url>
cd my-yocto-minimal
# 初始化
chmod +x setup.sh
./setup.sh
4. 构建镜像
# 方法1:使用构建脚本
./scripts/build-image.sh
# 方法2:手动构建
source setup-environment.sh
bitbake core-image-minimal-custom
5. 运行QEMU
# 在构建环境中运行
runqemu qemux86-64 nographic
# 或指定镜像文件
runqemu $BUILDDIR/tmp/deploy/images/qemux86-64/core-image-minimal-custom-qemux86-64.ext4
项目结构
sources/- Yocto源码层build/- 构建输出conf/- 配置文件scripts/- 构建脚本meta-custom/- 自定义层
自定义
添加新应用
- 在
sources/meta-custom/recipes-example/添加新配方 - 在镜像配方中添加包:
IMAGE_INSTALL += "your-package"
更改目标机器
修改 local.conf:
MACHINE = "raspberrypi4" # 或其他支持的机器
调试
- 查看构建日志:
bitbake -c compile hello-world -v - 进入开发shell:
bitbake -c devshell hello-world - 清理包:
bitbake -c clean hello-world
参考
### 6. **极简版本** (`minimal-setup.sh`)
如果你想要一个更简单的版本:
```bash
#!/bin/bash
# 极简Yocto设置
# 1. 克隆poky
git clone -b kirkstone git://git.yoctoproject.org/poky.git
cd poky
# 2. 初始化环境
source oe-init-build-env
# 3. 修改local.conf
cat >> conf/local.conf << 'EOF'
MACHINE = "qemux86-64"
DL_DIR = "/tmp/yocto-dl"
SSTATE_DIR = "/tmp/yocto-sstate"
BB_NUMBER_THREADS = "4"
PARALLEL_MAKE = "-j 4"
EOF
# 4. 构建最小镜像
bitbake core-image-minimal
# 5. 运行QEMU
runqemu qemux86-64
使用方法
- 克隆和初始化:
git clone <this-repo>
cd my-yocto-minimal
chmod +x *.sh scripts/*.sh
./setup.sh
- 构建镜像:
# 自动构建
./scripts/build-image.sh
# 或手动构建
source setup-environment.sh
bitbake core-image-minimal-custom
- 运行测试:
# 在QEMU中运行
runqemu qemux86-64
# 进入生成的rootfs
cd $BUILDDIR/tmp/work/*/core-image-minimal-custom/*/rootfs
这个最小Demo包含了Yocto的基本概念:
- 层(layers)
- 配方(recipes)
- 镜像构建
- 自定义应用
- QEMU测试环境
你可以根据需求扩展这个Demo,添加更多包、支持更多硬件平台或自定义功能。
Q:yocto 有哪些常用的bitbake相关命令及其作用,比如 bitbake-layers等?
在Yocto项目中,BitBake及其相关工具链是核心。下表我将常用命令分为四大类,并说明其典型用途,帮助你系统掌握。
📋 核心命令分类与速查
| 命令分类 | 命令示例 | 主要作用与场景 |
|---|---|---|
| 1. 层与配方管理 | bitbake-layers show-layers | 查看已添加层的路径和优先级。 |
bitbake-layers show-recipes | 列出所有可用配方(按层分组),是全局搜索配方的首选。 | |
bitbake-layers show-overlayed | 查找因优先级问题被覆盖的配方,用于调试冲突。 | |
bitbake-layers show-appends | 查看所有追加文件(.bbappend)及其影响的配方。 | |
bitbake-layers add-layer <path> | 添加新层到项目。 | |
bitbake-layers remove-layer <path> | 从项目中移除层。 | |
| 2. 构建与清理 | bitbake <target> | 核心构建命令。目标可以是镜像(core-image-minimal)、配方包(busybox)或虚拟目标(world)。 |
bitbake -c clean <recipe> | 清理某个配方的工作目录(WORKDIR),下次构建时重新解压、打补丁和编译。 | |
bitbake -c cleanall <recipe> | 更彻底的清理,同时删除下载的源码和共享状态缓存。 | |
bitbake -c cleansstate <target> | 从sstate-cache中清除目标的状态缓存,让其他构建也重新编译它。 | |
bitbake -k <target> 或 bitbake --continue | 遇到错误时继续构建尽可能多的部分,用于收集所有错误。 | |
| 3. 信息查询与调试 | bitbake -s | 列出所有可构建的配方及其版本,用于获取全局清单。 |
bitbake -e <recipe> | 转储指定配方的所有环境变量(包括继承和覆盖后的最终值),调试时用于查询SRC_URI、DEPENDS等。 | |
bitbake -g <target> | 生成依赖关系图文件(pn-depends.dot, task-depends.dot),用于可视化分析。 | |
bitbake -p | 转储所有配方的变量信息,数据量极大,通常用于脚本分析。 | |
| 4. 开发与修改工具 | devtool modify <recipe> | 进入配方开发模式:自动解压源码到工作区,方便你修改并支持增量构建。 |
devtool finish <recipe> <layer> | 将开发模式下的修改打包成补丁,并生成或更新.bbappend文件到指定层。 | |
recipetool create <url> | 根据源代码地址(如Git仓库)自动创建新的配方模板。 | |
recipetool appendsrcfile -W <layer> <recipe> <file> | 向现有配方追加额外的源代码文件。 |
🔍 核心命令详解与示例
1. 层管理 (bitbake-layers)
这是管理项目结构(层)的核心工具集。
# 查看当前所有层(这是检查层是否添加成功的第一步)
bitbake-layers show-layers
# 输出示例:
# layer path priority
# ==========================================================================
# meta /yocto/sources/meta 5
# meta-poky /yocto/sources/meta-poky 5
# meta-mylayer /yocto/sources/meta-mylayer 6
# 查找一个配方(如`busybox`)在哪个层,以及是否有覆盖
bitbake-layers show-recipes busybox
2. 构建与清理 (bitbake)
这是日常使用频率最高的命令。
# 构建一个基础镜像
bitbake core-image-minimal
# 只想重新编译某个包(如openssh),并清理其之前的编译结果
bitbake openssh -c clean && bitbake openssh
# 生成core-image-minimal的依赖图,并用graphviz查看(需安装)
bitbake -g core-image-minimal && dot -Tpng pn-depends.dot -o depends.png
3. 信息查询 (bitbake -e)
这是调试配方时最强大的命令。
# 查询linux-yocto配方的源代码URI和源码目录S
bitbake -e linux-yocto | grep -E "^(SRC_URI|S)="
# 查询一个配方的所有依赖项
bitbake -e busybox | grep ^DEPENDS=
4. 开发工具 (devtool)
这是官方推荐的配方开发工作流,极大提升效率。
# 1. 进入开发模式:代码会被解压到workspace/sources,并自动打补丁
devtool modify linux-yocto
# 2. 此时你可以在`workspace/sources/linux-yocto`目录修改内核代码
# 3. 进行增量编译,快速验证
devtool build linux-yocto
# 4. 将修改内容打包,并生成bbappend文件到你的自定义层
devtool finish linux-yocto meta-mylayer
💡 使用建议与注意事项
- 环境初始化:所有命令都需在已执行
source oe-init-build-env <build_dir>的终端中运行。 - 命令作用域:
bitbake-layers操作的是整个项目的层结构;bitbake操作的是具体的构建目标;devtool操作的是单个配方的开发周期。 - 安全清理:
cleanall会删除下载缓存,对于网络慢的环境慎用。通常clean或cleansstate已足够。 - 开发首选:需要修改现有配方时,强烈建议使用
devtool而非手动操作,它能妥善处理依赖、补丁和应用流程。
🎯 典型工作流与快速参考
- 添加新层:
bitbake-layers add-layer ../sources/meta-newlayer - 查找包:
bitbake-layers show-recipes | grep -i package_name - 构建镜像:
bitbake core-image-sato - 修改已有包:
devtool modify <recipe>-> 修改代码 ->devtool build <recipe>->devtool finish <recipe> <layer> - 创建新包:
recipetool create https://github.com/example/project-> 调整生成的.bb文件 -> 添加到层。
掌握这些命令,你就能高效地管理、构建和调试Yocto项目了。如果你对某个特定命令(如 devtool finish 的具体参数)或某个具体场景(如如何排查构建失败)有进一步疑问,我可以提供更详细的说明。
Q:yocto中如何打印出所有的 recipe-name?
在Yocto中,要打印出所有配方的名称,核心方法是使用BitBake提供的命令。根据你的具体场景(如是否需要包含版本号、按层查看等),可以选择不同的命令。
🛠️ 核心方法与命令
| 命令 | 作用与输出 | 适用场景与说明 |
|---|---|---|
| 1. 基础全局清单 | bitbake -s | 列出所有可构建的配方及其版本。输出格式为 <recipe-name>,适合获取最全列表。 |
bitbake -s | 输出示例:aspell :6.0.26.1autoconf :2.71-r0... | |
| 2. 按层查看配方 | bitbake-layers show-recipes | 强烈推荐。按层组织显示所有配方(包括未设置构建的)。更清晰,可查看归属。 |
bitbake-layers show-recipes | 输出示例:=== meta-layer ===aspell: meta-layer 6.0.26.1autoconf: meta-layer 2.71-r0 | |
| 3. 在源目录中查找 | find <source-directory> -name "*.bb" | 直接在源代码目录中查找所有 .bb 文件(配方文件)。最原始,但有时有用。 |
find ./sources -name "*.bb" | 输出为文件路径,如:./sources/meta/recipes-core/glibc/glibc_2.35.bb | 可用 basename -s .bb 提取纯名称。 |
🔍 常用命令组合与进阶用法
你可以根据具体需求组合使用以下命令:
| 需求场景 | 命令示例 | 说明 |
|---|---|---|
| 仅获取配方名(无版本) | bitbake -s | grep '^[a-zA-Z]' | cut -d: -f1 | 从 bitbake -s 输出中提取冒号前的名称。 |
| 统计配方总数 | bitbake-layers show-recipes | grep "^[a-zA-Z].*:" | wc -l | 计算 show-recipes 中列出的配方总数。 |
| 查找特定配方 | bitbake-layers show-recipes | grep -i <keyword> | 在配方名中搜索关键词(如 gcc)。 |
| 生成已构建包列表 | bitbake -g <image-name> && cat pn-buildlist | sort -u | 生成指定镜像的依赖关系文件,其中 pn-buildlist 包含要构建的配方。 |
| 获取某个配方的所有依赖 | bitbake -g <recipe-name> && cat pn-depends.dot | 生成依赖图文件,可查看具体依赖关系。 |
💡 操作步骤与须知
-
初始化构建环境:执行任何BitBake命令前,必须先进入构建目录并执行初始化脚本。
cd /path/to/your/build-directory source oe-init-build-env -
理解命令差异:
bitbake -s:查询的是当前配置下可构建的配方列表(受DISTRO、MACHINE、LAYERS影响)。bitbake-layers show-recipes:展示的是所有已添加层中的配方,无论当前配置是否使能。
-
注意网络与缓存:首次运行
bitbake -s可能会下载元数据或解析缓存,速度较慢。之后运行会快很多。 -
获取更详细的信息:如果想查看某个配方的具体信息(如版本、依赖、描述),可以使用:
bitbake -e <recipe-name> | grep ^PV= # 查看版本 bitbake -e <recipe-name> | grep ^DEPENDS= # 查看依赖
🎯 总结与直接建议
- 想快速查看所有配方及所属层:直接使用
bitbake-layers show-recipes,这是最清晰、最常用的方法。 - 需要程序化处理配方名列表:使用
bitbake -s | grep '^[a-zA-Z]' | cut -d: -f1获取纯净的名称列表。 - 想了解构建某个镜像或包时具体会构建哪些配方:使用
bitbake -g <target> && cat pn-buildlist。
根据你的Yocto项目规模和具体需求,选择最适合的方法即可。如果你能告知具体的使用场景(例如是为了调试、编写脚本,还是分析依赖),我可以提供更精准的命令组合。
Q:make编译系统中,我可以删除文件,重新编译时会自动生成;yocto的机制中,我手工删除文件或者文件夹,会造成编译fail吗?
简单来说,是的,在Yocto中手动删除编译过程中生成的文件,通常会导致后续编译失败。
这与你熟悉的make系统有本质区别。下面的表格清晰地对比了二者在处理文件删除上的核心差异:
| 特性 | Make 编译系统 | Yocto (BitBake) 编译系统 |
|---|---|---|
| 核心机制 | 基于文件时间戳的依赖检查。如果目标文件比源文件旧,就重新生成。 | 基于任务签名的复杂状态机。系统记录每个任务(如解压、配置、编译)是否成功完成。 |
| 删除文件的影响 | 重新编译时会自动重新生成缺失的目标文件,因为依赖规则仍在。 | 破坏了BitBake内部的状态跟踪。系统可能认为任务已完成,但实际文件缺失,导致依赖此文件的任务失败。 |
| 自动恢复能力 | 强。通常只需重新运行make。 | 弱或无。需要手动清理状态才能触发完整重做。 |
🔍 Yocto为什么不能随意删除
这是因为Yocto的编译目录(通常是build/tmp/)不仅是输出目录,更是状态记录和增量编译的基础。
- 工作目录 (
work/): 这是每个软件包(Recipe)解压、打补丁、配置、编译和安装的地方。手动删除这里的文件,例如build/tmp/work/<arch>/<package>/下的内容,会导致BitBake无法判断哪些步骤需要重新执行,从而报错。 - 部署目录 (
deploy/): 存放最终的镜像、软件包等。删除这里通常不影响编译过程,但会丢失产出物,下次编译时会重新生成。 - 共享状态缓存 (
sstate-cache/): 这是加速编译的核心。删除会迫使所有包从头编译,但通常不会导致“错误”,只会极大延长编译时间。
🗑️ 正确的清理与补救方法
当你需要清理时,应该使用BitBake命令,而不是直接删除文件。
1. 针对特定软件包的清理 这是最安全、最推荐的方式,只清理指定包的状态,让其从指定步骤开始重做。
bitbake <recipe-name> -c cleansstate: 清理该包的所有工作目录和任务状态,但保留下载的源码。bitbake <recipe-name> -c cleanall: 最彻底的清理,包括工作目录和下载缓存(在DL_DIR中)。
2. 整体环境重置
如果遇到难以定位的奇怪错误,可以参考PHYTEC Wiki的指南,进入build目录,选择性删除以下子目录:
tmp/: 必删。这会清除所有中间状态,相当于让整个系统从头开始编译。cache/: 可删。清除BitBake解析元数据后的缓存。deploy/: 可删。清除所有已部署的镜像和软件包。sstate-cache/和downloads/: 建议保留。删除它们会丢失所有编译缓存和下载的源码,导致后续编译极慢。
3. 如果已经手动删除并出错
可以参考中提到的步骤,使用bitbake -c cleansstate <package-name>清理出错的包,然后重新编译。如果问题依旧,可能需要对相关包或整个tmp目录进行清理。
💡 最佳实践建议
- 优先使用命令清理:始终通过
bitbake -c cleansstate等命令来清理,这是最安全的方式。 - 备份关键产出:在清理
deploy目录前,将重要的镜像文件备份到其他位置。 - 源码管理:将你的定制层(
meta-*)和配置文件(conf/local.conf,bblayers.conf等)纳入Git等版本控制系统。编译目录(build/)则应加入.gitignore。
总结来说,Yocto的tmp/work/目录是编译的状态核心,而非简单的输出目录。理解了它基于任务签名的增量编译机制,就能明白为什么不能像对待make的中间文件那样随意处理。
如果你遇到了特定的编译错误信息,我可以帮你进一步分析具体的解决步骤。
buildroot编译linux系统
Q:make编译系统中,我可以删除文件,重新编译时会自动生成;buildroot系统的机制中,我手工删除文件或者文件夹,会造成编译fail吗?
简单来说:在Buildroot中,手动删除文件一般不会导致永久性失败,但具体影响取决于你删除的位置,通常可以通过重新编译自动恢复。
这与Yocto基于任务签名的机制有本质不同。Buildroot的行为更像一个“增强版”的自动化Makefile集合。下面的表格清晰地对比了三种系统:
| 特性 | Make 编译系统 | Buildroot 系统 | Yocto (BitBake) 系统 |
|---|---|---|---|
| 核心机制 | 基于文件时间戳的依赖检查。 | 基于软件包规则和时间戳。每个软件包有独立的下载、解压、配置、编译、安装流程。 | 基于任务签名的复杂状态机,记录每个任务(解压、打补丁、编译等)的完成状态。 |
删除输出/构建目录文件的影响 | 重新make会自动重新生成。 | 通常安全。重新编译时,会重新执行该软件包的完整构建流程(从解压开始)。 | 通常导致失败。破坏了内部状态跟踪,系统认为任务已完成但文件缺失。 |
| 自动恢复能力 | 强。 | 强。重新运行make即可触发完整的重建。 | 弱。通常需要手动清理任务状态(-c cleansstate)。 |
📁 Buildroot目录结构及删除影响
关键在于你删除哪个目录下的文件。一个典型的Buildroot项目结构如下:
your_project/
├── dl/ # 源码包和补丁的**下载缓存**
├── output/
│ ├── build/ # **核心构建目录**,每个软件包在此解压、配置、编译
│ ├── host/ # 主机工具(交叉编译器等)
│ ├── images/# **最终生成的镜像**(可安全删除,会重新生成)
│ ├── staging/# 类似交叉编译环境的`sysroot`
│ └── target/# **根文件系统的雏形**
└── .config # 配置文件(绝对不能删)
1. 安全的删除(推荐做法)
output/images/:可以安全删除。运行make会重新生成所有镜像。output/build/<package_name>/:可以安全删除。这是处理单个软件包错误最有效的方法。例如,你修改了busybox的配置后,可以直接删除output/build/busybox-*,然后运行make,Buildroot会从解压源码开始完整地重建它。- 整个
output/目录:可以安全删除。运行make会触发完整重建(但会耗时很长,因为需要重新编译所有包)。
2. 需谨慎的删除
dl/目录:通常可以删除,但有代价。这是下载缓存,删除后所有源码包需要重新下载,但不会导致编译失败。在网络不佳或需要绝对干净重建时使用。output/host/目录:不建议手动删除。这是为主机构建的工具链和工具。删除后,Buildroot需要重新构建整个主机工具链,非常耗时。通常由make clean或make distclean自动处理。
🔧 正确的清理方法
与其手动删除文件夹,不如使用Buildroot提供的标准命令:
-
make <package-name>-dirclean这是最精确的清理命令。它会删除指定软件包在output/build/下的整个构建目录。下次编译时,该软件包会从解压开始完整重建。这是修复单个软件包编译问题的最佳方式。 -
make clean删除output/目录下的大部分内容(如build/,staging/,target/,images/),但会保留host/(主机工具)和dl/(下载缓存)。用于生成全新的目标文件系统和镜像。 -
make distclean最彻底的清理。将项目恢复到源码状态,删除output/和dl/目录以及.config配置文件。除非你想一切从头开始,否则慎用。
💎 总结与最佳实践
- 机制理解:Buildroot的构建是“按包从头执行”的。删除一个包的构建目录,只是移除了它上一轮构建的“结果”和“中间状态”,其构建规则(在
package/目录下)和源码(在dl/缓存或指定位置)仍然存在,所以可以重建。 - 首选命令:需要清理时,优先使用
make <package-name>-dirclean,这是最安全、最符合设计的方式。 - 避免手动删
host/:除非你知道自己在做什么,否则不要手动清理output/host/。 - 备份镜像:定期将
output/images/下的重要镜像备份到项目目录外。
总而言之,你可以把Buildroot看作一个高度自动化、管理着数百个独立软件包的Makefile项目。它为你管理依赖和流程,但最终每个包的构建过程仍然是传统的。因此,删除output/build/下的文件是安全的,相当于手动触发该包的“强制重新编译”。
如果你遇到了特定的编译错误,并提供错误信息,我可以帮你分析是否适合通过make <package>-dirclean来解决。
其他
打印函数执行时间
举例:
int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct video_device *vdev = video_devdata(file);
int val = 0;
#ifdef FEATURE_DONGFENG_DEBUG
u64 start_time, end_time ;
u64 diff = 0;
start_time = ktime_get_real_ns();
udelay(10); //延时10us,因为2次调用ktime_get_real_ns()有可能因为调度或者中断,导致后一次的值反而小,所以这里人为在第一次取时间戳后加一个短延时
#endif
if (vb2_queue_is_busy(vdev, file))
{
return -EBUSY;
}
val = vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
#ifdef FEATURE_DONGFENG_DEBUG
{
end_time = ktime_get_real_ns();
diff = end_time - start_time;
diff =(u32) diff /(u32) 1000UL;
}
if(diff > 30000)
{
printk("df_dqbuf:%c-%llu us\n",vdev->name[8] , diff);
}
#endif
return val;
}
visual studio code 中过滤内核代码的无关部分
比如 kernel-4.14.code-workspace 文件:
{
"folders": [
{
"path": "."
}
],
"settings": {
"search.exclude": {
"arch/alpha" :true,
"arch/m68k" :true,
"arch/arc" :true,
"arch/arm" :true,
"arch/blackfin" :true,
"arch/c6x" :true,
"arch/cris" :true,
"arch/frv" :true,
"arch/h8300" :true,
"arch/hexagon" :true,
"arch/ia64" :true,
"arch/m32r" :true,
"arch/metag" :true,
"arch/microblaze" :true,
"arch/mn10300" :true,
"arch/nios2" :true,
"arch/mips" :true,
"arch/openrisc" :true,
"arch/parisc" :true,
"arch/powerpc" :true,
"arch/s390" :true,
"arch/score" :true,
"arch/sh" :true,
"arch/sparc" :true,
"arch/tile" :true,
"arch/um" :true,
"arch/unicore32" :true,
"arch/x86" :true,
"arch/xtensa" :true,
"arch/arm64/boot/dts/actions" :true,
"arch/arm64/boot/dts/allwinner" :true,
"arch/arm64/boot/dts/amd" :true,
"arch/arm64/boot/dts/apm" :true,
"arch/arm64/boot/dts/broadcom" :true,
"arch/arm64/boot/dts/exynos" :true,
"arch/arm64/boot/dts/hisilicon" :true,
"arch/arm64/boot/dts/qcom" :true,
"arch/arm64/boot/dts/renesas" :true,
"arch/arm64/boot/dts/socionext" :true,
"arch/arm64/boot/dts/xilinx" :true,
"arch/arm64/boot/dts/al" :true,
"arch/arm64/boot/dts/altera" :true,
"arch/arm64/boot/dts/amlogic" :true,
"arch/arm64/boot/dts/arm" :true,
"arch/arm64/boot/dts/cavium" :true,
"arch/arm64/boot/dts/freescale" :true,
"arch/arm64/boot/dts/lg" :true,
"arch/arm64/boot/dts/marvell" :true,
"arch/arm64/boot/dts/nvidia" :true,
"arch/arm64/boot/dts/realtek" :true,
"arch/arm64/boot/dts/rockchip" :true,
"arch/arm64/boot/dts/marvell" :true,
"arch/arm64/boot/dts/mediatek" :true,
"arch/arm64/boot/dts/nvidia" :true,
"arch/arm64/boot/dts/realtek" :true,
"arch/arm64/boot/dts/rockchip" :true,
"arch/arm64/boot/dts/sprd" :true,
"arch/arm64/boot/dts/synaptics" :true,
"arch/arm64/boot/dts/ti" :true,
"arch/arm64/boot/dts/zte" :true,
"arch/arm64/boot/dts/riscv" :true,
},
"files.exclude": {
"arch/alpha" :true,
"arch/m68k" :true,
"arch/arc" :true,
"arch/arm" :true,
"arch/blackfin" :true,
"arch/c6x" :true,
"arch/cris" :true,
"arch/frv" :true,
"arch/h8300" :true,
"arch/hexagon" :true,
"arch/ia64" :true,
"arch/m32r" :true,
"arch/metag" :true,
"arch/microblaze" :true,
"arch/mn10300" :true,
"arch/nios2" :true,
"arch/mips" :true,
"arch/openrisc" :true,
"arch/parisc" :true,
"arch/powerpc" :true,
"arch/s390" :true,
"arch/score" :true,
"arch/sh" :true,
"arch/sparc" :true,
"arch/tile" :true,
"arch/um" :true,
"arch/unicore32" :true,
"arch/x86" :true,
"arch/xtensa" :true,
"arch/arm64/boot/dts/actions" :true,
"arch/arm64/boot/dts/allwinner" :true,
"arch/arm64/boot/dts/amd" :true,
"arch/arm64/boot/dts/apm" :true,
"arch/arm64/boot/dts/broadcom" :true,
"arch/arm64/boot/dts/exynos" :true,
"arch/arm64/boot/dts/hisilicon" :true,
"arch/arm64/boot/dts/qcom" :true,
"arch/arm64/boot/dts/renesas" :true,
"arch/arm64/boot/dts/socionext" :true,
"arch/arm64/boot/dts/xilinx" :true,
"arch/arm64/boot/dts/al" :true,
"arch/arm64/boot/dts/altera" :true,
"arch/arm64/boot/dts/amlogic" :true,
"arch/arm64/boot/dts/arm" :true,
"arch/arm64/boot/dts/cavium" :true,
"arch/arm64/boot/dts/freescale" :true,
"arch/arm64/boot/dts/lg" :true,
"arch/arm64/boot/dts/marvell" :true,
"arch/arm64/boot/dts/mediatek" :true,
"arch/arm64/boot/dts/nvidia" :true,
"arch/arm64/boot/dts/realtek" :true,
"arch/arm64/boot/dts/rockchip" :true,
"arch/arm64/boot/dts/sprd" :true,
"arch/arm64/boot/dts/synaptics" :true,
"arch/arm64/boot/dts/ti" :true,
"arch/arm64/boot/dts/zte" :true,
"arch/arm64/boot/dts/riscv" :true,
}
}
}
//在Linux系统内部,可以通过查看配置文档来获取内核编译的开关信息。这些配置文档通常位于/boot/config-$(uname -r)或/proc/config.gz。
-
cat /boot/config-$(uname -r)命令可以查看当前正在运行的内核配置。
-
zcat /proc/config.gz命令可以查看压缩的内核配置文档。请注意,在内核编译时增减相应的选项才会生成该文档,因此如果系统中没有该文档,可能无法获取内核编译的开关信息。