=嵌入式开发中的一句话备忘 - 嵌入式linux内核(非安卓)

215 阅读18分钟

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写寄存器地址字节数数据量几字节命令含义
21i2ctransfer -y -f 4 w3@0x38 0x3a 0x01 0x10向i2c-4号总线0x38设备的0x3a01寄存器写0x10,w3表示寄存器0x3a01和写入值0x10的长度为3字节
21i2ctransfer -f -y 2 w2@0x29 0x00 0x0d r1输入地址2byte=0x000d,返回值1byte
216i2ctransfer -y -f 4 w2@0x38 0x3a 0x01 r16从i2c-4号总线0x38设备的0x3a01寄存器开始读16个字节的数据,w2表示寄存器0x3a01的长度为2个字节
116i2ctransfer -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版本发布日期支持状态
scarthgap5.0.x2024年4月支持到2026年
nanbield4.3.x2023年10月支持到2025年
mickledore4.2.x2023年4月支持到2024年10月
langdale4.1.x2022年10月已结束
kirkstone4.0.x2022年4月LTS,支持到2026年
honister3.4.x2021年10月已结束
hardknott3.3.x2021年4月已结束
gatesgarth3.2.x2020年10月已结束
dunfell3.1.x2020年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

使用建议:

  1. 对于日常使用:主要关注 发布代号(如kirkstone、dunfell等)

  2. 对于bug报告:需要提供完整的版本信息,包括:

    • 发布代号
    • 具体版本号
    • BitBake版本
    • 使用的层和版本
  3. 对于兼容性检查:关注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/ - 自定义层

自定义

添加新应用

  1. sources/meta-custom/recipes-example/ 添加新配方
  2. 在镜像配方中添加包: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

使用方法

  1. 克隆和初始化:
git clone <this-repo>
cd my-yocto-minimal
chmod +x *.sh scripts/*.sh
./setup.sh
  1. 构建镜像:
# 自动构建
./scripts/build-image.sh

# 或手动构建
source setup-environment.sh
bitbake core-image-minimal-custom
  1. 运行测试:
# 在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_URIDEPENDS等。
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 会删除下载缓存,对于网络慢的环境慎用。通常 cleancleansstate 已足够。
  • 开发首选:需要修改现有配方时,强烈建议使用 devtool 而非手动操作,它能妥善处理依赖、补丁和应用流程。

🎯 典型工作流与快速参考

  1. 添加新层bitbake-layers add-layer ../sources/meta-newlayer
  2. 查找包bitbake-layers show-recipes | grep -i package_name
  3. 构建镜像bitbake core-image-sato
  4. 修改已有包devtool modify <recipe> -> 修改代码 -> devtool build <recipe> -> devtool finish <recipe> <layer>
  5. 创建新包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.1
autoconf :2.71-r0
...
2. 按层查看配方bitbake-layers show-recipes强烈推荐。按层组织显示所有配方(包括未设置构建的)。更清晰,可查看归属。
bitbake-layers show-recipes输出示例:
=== meta-layer ===
aspell:
meta-layer 6.0.26.1
autoconf:
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: -f1bitbake -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生成依赖图文件,可查看具体依赖关系。

💡 操作步骤与须知

  1. 初始化构建环境:执行任何BitBake命令前,必须先进入构建目录并执行初始化脚本

    cd /path/to/your/build-directory
    source oe-init-build-env
    
  2. 理解命令差异

    • bitbake -s:查询的是当前配置下可构建的配方列表(受 DISTROMACHINELAYERS 影响)。
    • bitbake-layers show-recipes:展示的是所有已添加层中的配方,无论当前配置是否使能。
  3. 注意网络与缓存:首次运行 bitbake -s 可能会下载元数据或解析缓存,速度较慢。之后运行会快很多。

  4. 获取更详细的信息:如果想查看某个配方的具体信息(如版本、依赖、描述),可以使用:

    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目录进行清理。

💡 最佳实践建议

  1. 优先使用命令清理:始终通过 bitbake -c cleansstate 等命令来清理,这是最安全的方式。
  2. 备份关键产出:在清理deploy目录前,将重要的镜像文件备份到其他位置。
  3. 源码管理:将你的定制层(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 cleanmake distclean自动处理。

🔧 正确的清理方法

与其手动删除文件夹,不如使用Buildroot提供的标准命令:

  • make <package-name>-dirclean 这是最精确的清理命令。它会删除指定软件包在output/build/下的整个构建目录。下次编译时,该软件包会从解压开始完整重建。这是修复单个软件包编译问题的最佳方式

  • make clean 删除output/目录下的大部分内容(如build/, staging/, target/, images/),但会保留host/(主机工具)和dl/(下载缓存)。用于生成全新的目标文件系统和镜像。

  • make distclean 最彻底的清理。将项目恢复到源码状态,删除output/dl/目录以及.config配置文件。除非你想一切从头开始,否则慎用。

💎 总结与最佳实践

  1. 机制理解:Buildroot的构建是“按包从头执行”的。删除一个包的构建目录,只是移除了它上一轮构建的“结果”和“中间状态”,其构建规则(在package/目录下)和源码(在dl/缓存或指定位置)仍然存在,所以可以重建。
  2. 首选命令:需要清理时,优先使用make <package-name>-dirclean,这是最安全、最符合设计的方式。
  3. 避免手动删host/:除非你知道自己在做什么,否则不要手动清理output/host/
  4. 备份镜像:定期将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。

  1. cat /boot/config-$(uname -r)命令可以查看当前正在运行的内核配置。

  2. zcat /proc/config.gz命令可以查看压缩的内核配置文档。请注意,在内核编译时增减相应的选项才会生成该文档,因此如果系统中没有该文档,可能无法获取内核编译的开关信息。