当前位置:首页>Linux>进阶方向六:嵌入式Linux开发 - 详细实操指南

进阶方向六:嵌入式Linux开发 - 详细实操指南

  • 2026-02-07 11:31:35
进阶方向六:嵌入式Linux开发 - 详细实操指南

预计时间:3-4个月 前置要求:完成基础五阶段学习


第一部分:交叉编译环境

1.1 环境准备

# 安装交叉编译工具链
sudo apt update
sudo apt install gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf
sudo apt install binutils-aarch64-linux-gnu binutils-arm-linux-gnueabihf
sudo apt install qemu-system-arm qemu-user-static

# 验证安装
aarch64-linux-gnu-gcc --version
arm-linux-gnueabihf-gcc --version

# 创建工作目录
mkdir -p ~/embedded_learning/{kernel,rootfs,bootloader,projects}
cd ~/embedded_learning

1.2 交叉编译Hello World

创建 hello.c

#include<stdio.h>

intmain() {
printf("Hello from ARM64!\n");
return0;
}

编译和测试:

# 编译ARM64版本
aarch64-linux-gnu-gcc -static -o hello_arm64 hello.c

# 编译ARM32版本
arm-linux-gnueabihf-gcc -static -o hello_arm32 hello.c

# 查看文件类型
file hello_arm64 hello_arm32

# 使用QEMU用户态模拟运行
qemu-aarch64-static ./hello_arm64
qemu-arm-static ./hello_arm32

1.3 交叉编译内核模块

创建 cross_module.c

#include<linux/init.h>
#include<linux/module.h>

MODULE_LICENSE("GPL");

staticint __init cross_init(void)
{
    pr_info("Cross-compiled module loaded on %s\n"
            IS_ENABLED(CONFIG_ARM64) ? "ARM64" : "ARM32");
return0;
}

staticvoid __exit cross_exit(void)
{
    pr_info("Cross-compiled module unloaded\n");
}

module_init(cross_init);
module_exit(cross_exit);

创建 Makefile

obj-m += cross_module.o

# 设置交叉编译
ARCH ?= arm64
CROSS_COMPILE ?= aarch64-linux-gnu-

# 内核源码路径(需要ARM64内核源码)
KDIR ?= ~/embedded_learning/kernel/linux-6.6

all:
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) M=$(PWD) modules

clean:
make -C $(KDIR) M=$(PWD) clean

第二部分:交叉编译内核

2.1 下载和配置内核

cd ~/embedded_learning/kernel

# 下载内核
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz
tar xf linux-6.6.tar.xz
cd linux-6.6

# 设置环境变量
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

# 使用默认配置
make defconfig

# 或使用menuconfig定制
make menuconfig

2.2 内核配置要点

# 关键配置项
make menuconfig

# General setup
#   [*] Support for paging of anonymous memory (swap)
#   [*] System V IPC
#   [*] POSIX Message Queues

# Platform selection (ARM64)
#   [*] ARMv8 based platforms
#   [*] QEMU ARM Virtual Machine

# Device Drivers
#   [*] Virtio drivers
#       [*] PCI driver for virtio devices
#       [*] Virtio block driver
#       [*] Virtio network driver
#       [*] Virtio console
#   [*] Block devices
#       [*] Virtio block driver

# File systems
#   [*] Ext4 filesystem
#   [*] Tmpfs support
#   Pseudo filesystems
#       [*] /proc file system support
#       [*] sysfs file system support

# 启用调试选项
# Kernel hacking
#   [*] Kernel debugging
#   [*] Debug filesystem

2.3 编译内核

# 编译内核和模块
make -j$(nproc)

# 编译结果
lsarch/arm64/boot/
# Image        - 内核镜像
# Image.gz     - 压缩镜像

# 编译设备树
make dtbs

lsarch/arm64/boot/dts/
# 设备树文件(.dtb)

第三部分:Bootloader (U-Boot)

3.1 下载和编译U-Boot

cd ~/embedded_learning/bootloader

# 下载U-Boot
git clone https://github.com/u-boot/u-boot.git --depth=1
cd u-boot

# 设置环境
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

# 配置(QEMU ARM64虚拟机)
make qemu_arm64_defconfig

# 编译
make -j$(nproc)

# 产物
ls u-boot.bin

3.2 U-Boot命令实践

# 使用QEMU启动U-Boot
qemu-system-aarch64 -M virt -cpu cortex-a72 -m 1024 \
    -bios u-boot.bin -nographic

# U-Boot常用命令
help# 帮助
printenv# 打印环境变量
setenv var value        # 设置变量
saveenv                 # 保存环境变量

# 内存操作
md 0x40000000 0x10      # 显示内存
mw 0x40000000 0x12345678 # 写内存

# 加载文件
fatload mmc 0:1 0x40000000 Image
# 或
tftp 0x40000000 Image

# 启动内核
booti 0x40000000 - 0x45000000
# 参数: kernel_addr - initrd_addr fdt_addr

# 退出QEMU: Ctrl+A, X

3.3 U-Boot环境配置

# 设置启动命令
setenv bootcmd 'fatload mmc 0:1 0x40000000 Image; fatload mmc 0:1 0x45000000 virt.dtb; booti 0x40000000 - 0x45000000'

# 设置内核参数
setenv bootargs 'console=ttyAMA0 root=/dev/vda rw rootwait'

# 保存
saveenv

第四部分:根文件系统

4.1 使用BusyBox构建最小rootfs

cd ~/embedded_learning/rootfs

# 下载BusyBox
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1

# 配置
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

# 关键配置:
# Settings -> Build static binary (no shared libs) = y
# Settings -> Cross compiler prefix = aarch64-linux-gnu-

# 编译安装
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- install CONFIG_PREFIX=../rootfs_arm64

4.2 完善rootfs结构

cd ~/embedded_learning/rootfs/rootfs_arm64

# 创建目录结构
mkdir -p {proc,sys,dev,etc,tmp,var,root,home}
mkdir -p etc/init.d
mkdir -p dev/pts
mkdir -p var/{log,run,lock}

# 创建设备节点
sudo mknod -m 666 dev/null c 1 3
sudo mknod -m 666 dev/zero c 1 5
sudo mknod -m 666 dev/random c 1 8
sudo mknod -m 666 dev/urandom c 1 9
sudo mknod -m 666 dev/tty c 5 0
sudo mknod -m 600 dev/console c 5 1

# 创建/etc/passwd
cat > etc/passwd << 'EOF'
root:x:0:0:root:/root:/bin/sh
EOF

# 创建/etc/group
cat > etc/group << 'EOF'
root:x:0:
EOF

# 创建/etc/inittab
cat > etc/inittab << 'EOF'
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
EOF

# 创建启动脚本
cat > etc/init.d/rcS << 'EOF'
#!/bin/sh
echo"=== Embedded Linux Booting ==="

# 挂载文件系统
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t tmpfs tmpfs /tmp
mount -t devpts devpts /dev/pts

# 设置主机名
hostname embedded

echo"System ready!"
EOF
chmod +x etc/init.d/rcS

# 创建/etc/fstab
cat > etc/fstab << 'EOF'
proc    /proc   proc    defaults    0   0
sysfs   /sys    sysfs   defaults    0   0
tmpfs   /tmp    tmpfs   defaults    0   0
EOF

4.3 创建rootfs镜像

cd ~/embedded_learning/rootfs

# 创建ext4镜像
ddif=/dev/zero of=rootfs.ext4 bs=1M count=64
mkfs.ext4 rootfs.ext4

# 挂载并复制文件
mkdir -p mnt
sudo mount rootfs.ext4 mnt
sudo cp -a rootfs_arm64/* mnt/
sudo umount mnt

# 或创建initramfs
cd rootfs_arm64
find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz

第五部分:QEMU系统模拟

5.1 启动完整系统

cd ~/embedded_learning

# 使用rootfs镜像启动
qemu-system-aarch64 \
    -M virt \
    -cpu cortex-a72 \
    -m 1024 \
    -kernel kernel/linux-6.6/arch/arm64/boot/Image \
    -append "console=ttyAMA0 root=/dev/vda rw rootwait" \
    -drive file=rootfs/rootfs.ext4,format=raw,id=hd0 \
    -device virtio-blk-device,drive=hd0 \
    -nographic

# 使用initramfs启动
qemu-system-aarch64 \
    -M virt \
    -cpu cortex-a72 \
    -m 1024 \
    -kernel kernel/linux-6.6/arch/arm64/boot/Image \
    -initrd rootfs/initramfs.cpio.gz \
    -append "console=ttyAMA0 rdinit=/linuxrc" \
    -nographic

# 退出: Ctrl+A, X

5.2 添加网络支持

# 用户态网络(简单,可访问外网)
qemu-system-aarch64 \
    -M virt -cpu cortex-a72 -m 1024 \
    -kernel kernel/linux-6.6/arch/arm64/boot/Image \
    -append "console=ttyAMA0 root=/dev/vda rw rootwait" \
    -drive file=rootfs/rootfs.ext4,format=raw,id=hd0 \
    -device virtio-blk-device,drive=hd0 \
    -netdev user,id=net0,hostfwd=tcp::2222-:22 \
    -device virtio-net-device,netdev=net0 \
    -nographic

# Guest内配置网络
ip linkset eth0 up
ip addr add 10.0.2.15/24 dev eth0
ip route add default via 10.0.2.2

# 从主机SSH到Guest
ssh -p 2222 root@localhost

5.3 GDB远程调试

# 启动QEMU并等待GDB连接
qemu-system-aarch64 \
    -M virt -cpu cortex-a72 -m 1024 \
    -kernel kernel/linux-6.6/arch/arm64/boot/Image \
    -append "console=ttyAMA0 nokaslr" \
    -nographic -s -S

# 另一个终端启动GDB
aarch64-linux-gnu-gdb kernel/linux-6.6/vmlinux
(gdb) target remote :1234
(gdb) b start_kernel
(gdb) c

第六部分:设备树

6.1 设备树基础语法

创建 simple.dts

/dts-v1/;

/{
compatible="myvendor,myboard";
#address-cells = <2>;
#size-cells = <2>;

chosen{
bootargs="console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait";
stdout-path="serial0:115200n8";
};

memory@40000000{
        device_type="memory";
reg=<0x00x400000000x00x40000000>;// 1GB @ 0x40000000
};

cpus{
#address-cells = <1>;
#size-cells = <0>;

cpu@0{
            device_type="cpu";
compatible="arm,cortex-a72";
reg=<0>;
};
};

soc{
compatible="simple-bus";
#address-cells = <2>;
#size-cells = <2>;
ranges;

// 串口
        uart0:serial@9000000{
compatible="ns16550a";
reg=<0x00x90000000x00x1000>;
interrupts=<014>;
clock-frequency=<1843200>;
};

// GPIO控制器
        gpio0:gpio@9010000{
compatible="myvendor,my-gpio";
reg=<0x00x90100000x00x1000>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};

// LED设备
leds{
compatible="gpio-leds";

led0{
label="status";
gpios=<&gpio000>;// GPIO0, active high
default-state="off";
};

led1{
label="heartbeat";
gpios=<&gpio010>;
linux,default-trigger="heartbeat";
};
};

// I2C控制器
        i2c0:i2c@9020000{
compatible="myvendor,my-i2c";
reg=<0x00x90200000x00x1000>;
#address-cells = <1>;
#size-cells = <0>;
clock-frequency=<100000>;

// I2C设备
eeprom@50{
compatible="atmel,24c02";
reg=<0x50>;
};

rtc@68{
compatible="dallas,ds1307";
reg=<0x68>;
};
};

// SPI控制器
        spi0:spi@9030000{
compatible="myvendor,my-spi";
reg=<0x00x90300000x00x1000>;
#address-cells = <1>;
#size-cells = <0>;

flash@0{
compatible="jedec,spi-nor";
reg=<0>;
spi-max-frequency=<50000000>;
};
};
};
};

6.2 编译设备树

# 编译dts为dtb
dtc -I dts -O dtb -o simple.dtb simple.dts

# 反编译dtb为dts
dtc -I dtb -O dts -o output.dts simple.dtb

# 查看dtb内容
fdtdump simple.dtb

6.3 设备树驱动示例

创建 dt_driver.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/of.h>
#include<linux/of_device.h>

MODULE_LICENSE("GPL");

structmy_device_data {
void __iomem *base;
int irq;
    u32 clock_freq;
};

staticintmy_probe(struct platform_device *pdev)
{
structmy_device_data *data;
structresource *res;
structdevice_node *np = pdev->dev.of_node;

    pr_info("my_driver: Probing %s\n", pdev->name);

    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) return -ENOMEM;

// 获取寄存器资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) {
        data->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
        pr_info("  Registers: 0x%llx - 0x%llx\n", res->start, res->end);
    }

// 获取中断
    data->irq = platform_get_irq(pdev, 0);
if (data->irq > 0)
        pr_info("  IRQ: %d\n", data->irq);

// 读取设备树属性
if (of_property_read_u32(np, "clock-frequency", &data->clock_freq) == 0)
        pr_info("  Clock: %u Hz\n", data->clock_freq);

    platform_set_drvdata(pdev, data);
    pr_info("my_driver: Probe successful\n");

return0;
}

staticintmy_remove(struct platform_device *pdev)
{
    pr_info("my_driver: Remove\n");
return0;
}

staticconststructof_device_idmy_of_match[] = {
    { .compatible = "myvendor,my-gpio" },
    { .compatible = "myvendor,my-i2c" },
    { .compatible = "myvendor,my-spi" },
    { }
};
MODULE_DEVICE_TABLE(of, my_of_match);

staticstructplatform_drivermy_driver = {
    .probe = my_probe,
    .remove = my_remove,
    .driver = {
        .name = "my_driver",
        .of_match_table = my_of_match,
    },
};

module_platform_driver(my_driver);

第七部分:Buildroot构建系统

7.1 下载和配置

cd ~/embedded_learning

# 下载Buildroot
git clone https://github.com/buildroot/buildroot.git --depth=1
cd buildroot

# 配置
make menuconfig

7.2 关键配置

Target options
    Target Architecture: AArch64 (little endian)
    Target Architecture Variant: cortex-A72

Toolchain
    Toolchain type: External toolchain
    Toolchain: Linaro AArch64

System configuration
    System hostname: embedded
    System banner: Welcome to Embedded Linux
    Root password: root
    /bin/sh: busybox

Kernel
    [*] Linux Kernel
    Kernel version: Latest LTS
    Kernel configuration: Use defconfig
    Defconfig name: defconfig

Target packages
    [*] BusyBox
    Networking applications
        [*] dropbear (SSH)
        [*] iperf3
    System tools
        [*] htop

Filesystem images
    [*] ext4 root filesystem
    [*] cpio the root filesystem

7.3 编译

# 编译(首次可能需要1-2小时)
make -j$(nproc)

# 产物位置
ls output/images/
# Image         - 内核
# rootfs.ext4   - 根文件系统
# rootfs.cpio   - initramfs

# 使用QEMU测试
qemu-system-aarch64 \
    -M virt -cpu cortex-a72 -m 1024 \
    -kernel output/images/Image \
    -append "console=ttyAMA0 root=/dev/vda rw" \
    -drive file=output/images/rootfs.ext4,format=raw,id=hd0 \
    -device virtio-blk-device,drive=hd0 \
    -nographic

第八部分:简单GPIO驱动

8.1 虚拟GPIO驱动

创建 virt_gpio.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/gpio/driver.h>
#include<linux/platform_device.h>

MODULE_LICENSE("GPL");

#define NUM_GPIOS 8

structvirt_gpio {
structgpio_chipgc;
    u8 data;
    u8 direction;  // 0=output, 1=input
};

staticintvirt_gpio_get(struct gpio_chip *gc, unsigned offset)
{
structvirt_gpio *vg = gpiochip_get_data(gc);
return (vg->data >> offset) & 1;
}

staticvoidvirt_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
{
structvirt_gpio *vg = gpiochip_get_data(gc);
if (value)
        vg->data |= (1 << offset);
else
        vg->data &= ~(1 << offset);
    pr_info("virt_gpio: Set GPIO%d = %d (data=0x%02x)\n"
            offset, value, vg->data);
}

staticintvirt_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
structvirt_gpio *vg = gpiochip_get_data(gc);
    vg->direction |= (1 << offset);
return0;
}

staticintvirt_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int value)
{
structvirt_gpio *vg = gpiochip_get_data(gc);
    vg->direction &= ~(1 << offset);
    virt_gpio_set(gc, offset, value);
return0;
}

staticintvirt_gpio_probe(struct platform_device *pdev)
{
structvirt_gpio *vg;
int ret;

    vg = devm_kzalloc(&pdev->dev, sizeof(*vg), GFP_KERNEL);
if (!vg) return -ENOMEM;

    vg->gc.label = "virt-gpio";
    vg->gc.ngpio = NUM_GPIOS;
    vg->gc.base = -1;  // 动态分配
    vg->gc.parent = &pdev->dev;
    vg->gc.owner = THIS_MODULE;
    vg->gc.get = virt_gpio_get;
    vg->gc.set = virt_gpio_set;
    vg->gc.direction_input = virt_gpio_direction_input;
    vg->gc.direction_output = virt_gpio_direction_output;

    ret = devm_gpiochip_add_data(&pdev->dev, &vg->gc, vg);
if (ret) return ret;

    platform_set_drvdata(pdev, vg);
    pr_info("virt_gpio: Registered %d GPIOs\n", NUM_GPIOS);

return0;
}

staticstructplatform_drivervirt_gpio_driver = {
    .probe = virt_gpio_probe,
    .driver = { .name = "virt-gpio" },
};

staticstructplatform_device *pdev;

staticint __init virt_gpio_init(void)
{
int ret;
    ret = platform_driver_register(&virt_gpio_driver);
if (ret) return ret;

    pdev = platform_device_register_simple("virt-gpio"-1NULL0);
if (IS_ERR(pdev)) {
        platform_driver_unregister(&virt_gpio_driver);
return PTR_ERR(pdev);
    }
return0;
}

staticvoid __exit virt_gpio_exit(void)
{
    platform_device_unregister(pdev);
    platform_driver_unregister(&virt_gpio_driver);
}

module_init(virt_gpio_init);
module_exit(virt_gpio_exit);

8.2 测试GPIO

# 加载模块
sudo insmod virt_gpio.ko

# 查看GPIO
cat /sys/kernel/debug/gpio

# 导出GPIO
echo 0 > /sys/class/gpio/export

# 设置方向
echo out > /sys/class/gpio/gpio0/direction

# 设置值
echo 1 > /sys/class/gpio/gpio0/value
echo 0 > /sys/class/gpio/gpio0/value

# 取消导出
echo 0 > /sys/class/gpio/unexport

第九部分:Makefile

obj-m += cross_module.o
obj-m += dt_driver.o
obj-m += virt_gpio.o

ARCH ?= arm64
CROSS_COMPILE ?= aarch64-linux-gnu-
KDIR ?= ~/embedded_learning/kernel/linux-6.6

all:
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) M=$(PWD) modules

clean:
make -C $(KDIR) M=$(PWD) clean

# 本地测试(x86)
local:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

实操检查清单

交叉编译

  • [ ] 能交叉编译用户程序
  • [ ] 能交叉编译内核
  • [ ] 能交叉编译内核模块

Bootloader

  • [ ] 能编译U-Boot
  • [ ] 理解U-Boot启动流程
  • [ ] 能配置U-Boot环境变量

根文件系统

  • [ ] 能使用BusyBox构建rootfs
  • [ ] 能创建ext4/initramfs镜像
  • [ ] 能使用Buildroot

QEMU

  • [ ] 能启动ARM64系统
  • [ ] 能配置网络
  • [ ] 能使用GDB远程调试

设备树

  • [ ] 理解设备树语法
  • [ ] 能编写设备树驱动
  • [ ] 能编译和反编译dtb

扩展一:真实开发板实践(树莓派)

1.1 树莓派准备

# 下载树莓派OS镜像
wget https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2024-03-15/2024-03-15-raspios-bookworm-arm64-lite.img.xz

# 解压
xz -d 2024-03-15-raspios-bookworm-arm64-lite.img.xz

# 烧录到SD卡(替换/dev/sdX为实际设备)
sudo ddif=2024-03-15-raspios-bookworm-arm64-lite.img of=/dev/sdX bs=4M status=progress
sync

# 启用SSH(挂载boot分区后)
sudo mount /dev/sdX1 /mnt
sudo touch /mnt/ssh
sudo umount /mnt

1.2 交叉编译树莓派内核

# 克隆树莓派内核
git clone --depth=1 https://github.com/raspberrypi/linux.git rpi-linux
cd rpi-linux

# 安装交叉编译工具链
sudo apt install crossbuild-essential-arm64

# 配置(树莓派4/5 64位)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig

# 自定义配置
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

# 编译
make -j$(nproc) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image modules dtbs

# 安装模块到临时目录
mkdir -p ../rpi-modules
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
    INSTALL_MOD_PATH=../rpi-modules modules_install

1.3 部署到树莓派

# 挂载SD卡分区
sudo mount /dev/sdX1 /mnt/boot
sudo mount /dev/sdX2 /mnt/rootfs

# 备份原内核
sudo cp /mnt/boot/kernel8.img /mnt/boot/kernel8.img.bak

# 复制新内核
sudo cparch/arm64/boot/Image /mnt/boot/kernel8.img

# 复制设备树
sudo cparch/arm64/boot/dts/broadcom/*.dtb /mnt/boot/
sudo cparch/arm64/boot/dts/overlays/*.dtb* /mnt/boot/overlays/

# 复制模块
sudo cp -r ../rpi-modules/lib/modules/* /mnt/rootfs/lib/modules/

# 卸载
sudo umount /mnt/boot /mnt/rootfs

1.4 树莓派GPIO驱动

创建 rpi_gpio_led.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/gpio.h>
#include<linux/timer.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RPi GPIO LED Blink");

#define LED_GPIO 17  // BCM GPIO17 (Pin 11)

staticstructtimer_listblink_timer;
staticint led_state = 0;

staticvoidblink_timer_callback(struct timer_list *t)
{
    led_state = !led_state;
    gpio_set_value(LED_GPIO, led_state);
    mod_timer(&blink_timer, jiffies + HZ);  // 1秒后再触发
}

staticint __init rpi_led_init(void)
{
int ret;

    ret = gpio_request(LED_GPIO, "led_gpio");
if (ret) {
        pr_err("Failed to request GPIO %d\n", LED_GPIO);
return ret;
    }

    gpio_direction_output(LED_GPIO, 0);

    timer_setup(&blink_timer, blink_timer_callback, 0);
    mod_timer(&blink_timer, jiffies + HZ);

    pr_info("RPi LED blink started on GPIO %d\n", LED_GPIO);
return0;
}

staticvoid __exit rpi_led_exit(void)
{
    del_timer_sync(&blink_timer);
    gpio_set_value(LED_GPIO, 0);
    gpio_free(LED_GPIO);
    pr_info("RPi LED blink stopped\n");
}

module_init(rpi_led_init);
module_exit(rpi_led_exit);

1.5 树莓派I2C设备驱动

创建 rpi_i2c_sensor.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/i2c.h>
#include<linux/device.h>

MODULE_LICENSE("GPL");

#define SENSOR_REG_ID     0x00
#define SENSOR_REG_DATA   0x01

structsensor_data {
structi2c_client *client;
structdevice *dev;
};

staticintsensor_read_reg(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}

staticssize_tsensor_id_show(struct device *dev,
struct device_attribute *attr, char *buf)

{
structsensor_data *data = dev_get_drvdata(dev);
int id = sensor_read_reg(data->client, SENSOR_REG_ID);
returnsprintf(buf, "0x%02x\n", id);
}

staticssize_tsensor_value_show(struct device *dev,
struct device_attribute *attr, char *buf)

{
structsensor_data *data = dev_get_drvdata(dev);
int value = sensor_read_reg(data->client, SENSOR_REG_DATA);
returnsprintf(buf, "%d\n", value);
}

staticDEVICE_ATTR(sensor_id, 0444, sensor_id_show, NULL);
staticDEVICE_ATTR(sensor_value, 0444, sensor_value_show, NULL);

staticstructattribute *sensor_attrs[] = {
    &dev_attr_sensor_id.attr,
    &dev_attr_sensor_value.attr,
NULL,
};

staticstructattribute_groupsensor_attr_group = {
    .attrs = sensor_attrs,
};

staticintsensor_probe(struct i2c_client *client)
{
structsensor_data *data;
int ret;

    data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data) return -ENOMEM;

    data->client = client;
    data->dev = &client->dev;
    i2c_set_clientdata(client, data);

    ret = sysfs_create_group(&client->dev.kobj, &sensor_attr_group);
if (ret) return ret;

    dev_info(&client->dev, "I2C sensor probed at 0x%02x\n", client->addr);
return0;
}

staticvoidsensor_remove(struct i2c_client *client)
{
    sysfs_remove_group(&client->dev.kobj, &sensor_attr_group);
    dev_info(&client->dev, "I2C sensor removed\n");
}

staticconststructi2c_device_idsensor_id[] = {
    { "mysensor"0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, sensor_id);

staticconststructof_device_idsensor_of_match[] = {
    { .compatible = "myvendor,mysensor" },
    { }
};
MODULE_DEVICE_TABLE(of, sensor_of_match);

staticstructi2c_driversensor_driver = {
    .driver = {
        .name = "mysensor",
        .of_match_table = sensor_of_match,
    },
    .probe = sensor_probe,
    .remove = sensor_remove,
    .id_table = sensor_id,
};

module_i2c_driver(sensor_driver);

扩展二:Yocto构建系统

2.1 Yocto概述

┌─────────────────────────────────────────────────┐
│                  Yocto Project                   │
├─────────────────────────────────────────────────┤
│  ┌───────────┐  ┌───────────┐  ┌───────────┐   │
│  │  Poky     │  │  Layers   │  │  BSP      │   │
│  │ (参考发行) │  │  (元数据)  │  │  (板级包)  │   │
│  └───────────┘  └───────────┘  └───────────┘   │
├─────────────────────────────────────────────────┤
│  ┌───────────────────────────────────────────┐  │
│  │              BitBake                       │  │
│  │           (构建引擎)                        │  │
│  └───────────────────────────────────────────┘  │
├─────────────────────────────────────────────────┤
│  ┌─────────┐  ┌─────────┐  ┌─────────┐         │
│  │ Recipes │  │ Classes │  │ Configs │         │
│  │  (.bb)  │  │ (.bbclass)│ │ (.conf) │         │
│  └─────────┘  └─────────┘  └─────────┘         │
└─────────────────────────────────────────────────┘

2.2 安装Yocto

# 安装依赖
sudo apt install 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 \
    xterm python3-subunit mesa-common-dev zstd liblz4-tool

# 克隆Poky(Yocto参考发行版)
git clone git://git.yoctoproject.org/poky -b scarthgap
cd poky

# 初始化构建环境
source oe-init-build-env build-rpi

2.3 配置构建

编辑 conf/local.conf

# 目标机器
MACHINE = "raspberrypi4-64"

# 并行构建
BB_NUMBER_THREADS = "8"
PARALLEL_MAKE = "-j 8"

# 下载目录(避免重复下载)
DL_DIR = "${TOPDIR}/../downloads"

# 共享状态缓存
SSTATE_DIR = "${TOPDIR}/../sstate-cache"

# 添加功能
DISTRO_FEATURES:append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"

# 添加额外软件包
IMAGE_INSTALL:append = " openssh htop vim"

# 启用调试信息
EXTRA_IMAGE_FEATURES = "debug-tweaks tools-debug"

编辑 conf/bblayers.conf

BBLAYERS ?= " \
  /path/to/poky/meta \
  /path/to/poky/meta-poky \
  /path/to/poky/meta-yocto-bsp \
  /path/to/meta-raspberrypi \
  /path/to/meta-openembedded/meta-oe \
"

2.4 添加树莓派BSP层

cd ..
git clone git://git.yoctoproject.org/meta-raspberrypi -b scarthgap
git clone https://github.com/openembedded/meta-openembedded.git -b scarthgap

cd build-rpi
bitbake-layers add-layer ../meta-raspberrypi
bitbake-layers add-layer ../meta-openembedded/meta-oe

2.5 编写自定义Recipe

创建 meta-mylayer/recipes-apps/myapp/myapp_1.0.bb

SUMMARY = "My Custom Application"
DESCRIPTION = "A simple application for embedded Linux"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://myapp.c \
           file://Makefile"

S = "${WORKDIR}"

do_compile() {
    oe_runmake
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 myapp ${D}${bindir}
}

FILES:${PN} = "${bindir}/myapp"

创建源文件目录结构:

meta-mylayer/
├── conf/
│   └── layer.conf
├── recipes-apps/
│   └── myapp/
│       ├── myapp_1.0.bb
│       └── files/
│           ├── myapp.c
│           └── Makefile

2.6 编写内核模块Recipe

创建 meta-mylayer/recipes-kernel/mymodule/mymodule_1.0.bb

SUMMARY = "My Kernel Module"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6"

inherit module

SRC_URI = "file://mymodule.c \
           file://Makefile"

S = "${WORKDIR}"

RPROVIDES:${PN} += "kernel-module-mymodule"

2.7 构建镜像

# 构建最小镜像
bitbake core-image-minimal

# 构建带开发工具的镜像
bitbake core-image-full-cmdline

# 构建SDK
bitbake -c populate_sdk core-image-minimal

# 输出位置
ls tmp/deploy/images/raspberrypi4-64/

扩展三:实际硬件驱动开发

3.1 SPI设备驱动

创建 spi_display.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/spi/spi.h>
#include<linux/of.h>
#include<linux/delay.h>
#include<linux/fb.h>

MODULE_LICENSE("GPL");

#define DISPLAY_WIDTH   128
#define DISPLAY_HEIGHT  64

structdisplay_data {
structspi_device *spi;
structfb_info *fb;
    u8 *buffer;
};

staticintdisplay_send_cmd(struct spi_device *spi, u8 cmd)
{
// 假设DC引脚在传输前设置为低
return spi_write(spi, &cmd, 1);
}

staticintdisplay_send_data(struct spi_device *spi, u8 *data, size_t len)
{
// 假设DC引脚在传输前设置为高
return spi_write(spi, data, len);
}

staticvoiddisplay_init_hw(struct spi_device *spi)
{
// OLED初始化序列示例
    display_send_cmd(spi, 0xAE);  // Display off
    display_send_cmd(spi, 0xD5);  // Set display clock
    display_send_cmd(spi, 0x80);
    display_send_cmd(spi, 0xA8);  // Set multiplex ratio
    display_send_cmd(spi, 0x3F);
    display_send_cmd(spi, 0xD3);  // Set display offset
    display_send_cmd(spi, 0x00);
    display_send_cmd(spi, 0x40);  // Set start line
    display_send_cmd(spi, 0xAF);  // Display on
}

staticssize_tdisplay_write(struct file *file, constchar __user *buf,
size_t count, loff_t *ppos)

{
// Framebuffer写入处理
return count;
}

staticstructfb_opsdisplay_ops = {
    .owner = THIS_MODULE,
    .fb_write = display_write,
    .fb_fillrect = cfb_fillrect,
    .fb_copyarea = cfb_copyarea,
    .fb_imageblit = cfb_imageblit,
};

staticintdisplay_probe(struct spi_device *spi)
{
structdisplay_data *data;
structfb_info *fb;
int ret;

    data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
if (!data) return -ENOMEM;

    data->spi = spi;
    spi_set_drvdata(spi, data);

// 配置SPI
    spi->mode = SPI_MODE_0;
    spi->bits_per_word = 8;
    spi->max_speed_hz = 10000000;  // 10MHz
    ret = spi_setup(spi);
if (ret) return ret;

    display_init_hw(spi);

// 分配framebuffer
    fb = framebuffer_alloc(sizeof(struct display_data), &spi->dev);
if (!fb) return -ENOMEM;

    fb->fbops = &display_ops;
    fb->var.xres = DISPLAY_WIDTH;
    fb->var.yres = DISPLAY_HEIGHT;
    fb->var.bits_per_pixel = 1;
    fb->fix.line_length = DISPLAY_WIDTH / 8;

    data->buffer = devm_kzalloc(&spi->dev, 
                                DISPLAY_WIDTH * DISPLAY_HEIGHT / 8
                                GFP_KERNEL);
    fb->screen_base = data->buffer;
    fb->screen_size = DISPLAY_WIDTH * DISPLAY_HEIGHT / 8;

    ret = register_framebuffer(fb);
if (ret) {
        framebuffer_release(fb);
return ret;
    }

    data->fb = fb;
    dev_info(&spi->dev, "SPI display probed\n");
return0;
}

staticvoiddisplay_remove(struct spi_device *spi)
{
structdisplay_data *data = spi_get_drvdata(spi);
    unregister_framebuffer(data->fb);
    framebuffer_release(data->fb);
}

staticconststructof_device_iddisplay_of_match[] = {
    { .compatible = "myvendor,spi-display" },
    { }
};
MODULE_DEVICE_TABLE(of, display_of_match);

staticstructspi_driverdisplay_driver = {
    .driver = {
        .name = "spi-display",
        .of_match_table = display_of_match,
    },
    .probe = display_probe,
    .remove = display_remove,
};

module_spi_driver(display_driver);

3.2 PWM驱动

创建 pwm_motor.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/pwm.h>
#include<linux/of.h>

MODULE_LICENSE("GPL");

structmotor_data {
structpwm_device *pwm;
unsignedint duty_cycle;  // 0-100%
unsignedint period_ns;
};

staticssize_tspeed_show(struct device *dev,
struct device_attribute *attr, char *buf)

{
structmotor_data *data = dev_get_drvdata(dev);
returnsprintf(buf, "%u\n", data->duty_cycle);
}

staticssize_tspeed_store(struct device *dev,
struct device_attribute *attr,
constchar *buf, size_t count)

{
structmotor_data *data = dev_get_drvdata(dev);
unsignedint speed;
int ret;

    ret = kstrtouint(buf, 10, &speed);
if (ret) return ret;

if (speed > 100) speed = 100;

    data->duty_cycle = speed;

    ret = pwm_config(data->pwm, 
                     data->period_ns * speed / 100,
                     data->period_ns);
if (ret) return ret;

if (speed > 0)
        pwm_enable(data->pwm);
else
        pwm_disable(data->pwm);

return count;
}

staticDEVICE_ATTR_RW(speed);

staticintmotor_probe(struct platform_device *pdev)
{
structmotor_data *data;
int ret;

    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) return -ENOMEM;

    data->pwm = devm_pwm_get(&pdev->dev, NULL);
if (IS_ERR(data->pwm)) {
        dev_err(&pdev->dev, "Failed to get PWM\n");
return PTR_ERR(data->pwm);
    }

// 默认20kHz PWM
    data->period_ns = 50000;  // 50us = 20kHz
    data->duty_cycle = 0;

    platform_set_drvdata(pdev, data);

    ret = device_create_file(&pdev->dev, &dev_attr_speed);
if (ret) return ret;

    dev_info(&pdev->dev, "PWM motor driver probed\n");
return0;
}

staticintmotor_remove(struct platform_device *pdev)
{
structmotor_data *data = platform_get_drvdata(pdev);

    device_remove_file(&pdev->dev, &dev_attr_speed);
    pwm_disable(data->pwm);

return0;
}

staticconststructof_device_idmotor_of_match[] = {
    { .compatible = "myvendor,pwm-motor" },
    { }
};
MODULE_DEVICE_TABLE(of, motor_of_match);

staticstructplatform_drivermotor_driver = {
    .driver = {
        .name = "pwm-motor",
        .of_match_table = motor_of_match,
    },
    .probe = motor_probe,
    .remove = motor_remove,
};

module_platform_driver(motor_driver);

设备树节点:

motor:motor@0{
compatible="myvendor,pwm-motor";
pwms=<&pwm050000>;// PWM0, 50us period
status="okay";
};

3.3 中断处理驱动

创建 gpio_irq_button.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/gpio/consumer.h>
#include<linux/interrupt.h>
#include<linux/input.h>

MODULE_LICENSE("GPL");

structbutton_data {
structgpio_desc *gpio;
structinput_dev *input;
int irq;
int key_code;
};

staticirqreturn_tbutton_irq_handler(int irq, void *dev_id)
{
structbutton_data *data = dev_id;
int state = gpiod_get_value(data->gpio);

    input_report_key(data->input, data->key_code, !state);
    input_sync(data->input);

return IRQ_HANDLED;
}

staticintbutton_probe(struct platform_device *pdev)
{
structbutton_data *data;
int ret;

    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) return -ENOMEM;

// 获取GPIO
    data->gpio = devm_gpiod_get(&pdev->dev, "button", GPIOD_IN);
if (IS_ERR(data->gpio)) {
        dev_err(&pdev->dev, "Failed to get GPIO\n");
return PTR_ERR(data->gpio);
    }

// 获取中断号
    data->irq = gpiod_to_irq(data->gpio);
if (data->irq < 0) {
        dev_err(&pdev->dev, "Failed to get IRQ\n");
return data->irq;
    }

// 创建input设备
    data->input = devm_input_allocate_device(&pdev->dev);
if (!data->input) return -ENOMEM;

    data->input->name = "gpio-button";
    data->key_code = KEY_ENTER;

    input_set_capability(data->input, EV_KEY, data->key_code);

    ret = input_register_device(data->input);
if (ret) return ret;

// 请求中断
    ret = devm_request_irq(&pdev->dev, data->irq, button_irq_handler,
                           IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"gpio-button", data);
if (ret) {
        dev_err(&pdev->dev, "Failed to request IRQ\n");
return ret;
    }

    platform_set_drvdata(pdev, data);
    dev_info(&pdev->dev, "Button driver probed, IRQ=%d\n", data->irq);

return0;
}

staticconststructof_device_idbutton_of_match[] = {
    { .compatible = "myvendor,gpio-button" },
    { }
};
MODULE_DEVICE_TABLE(of, button_of_match);

staticstructplatform_driverbutton_driver = {
    .driver = {
        .name = "gpio-button",
        .of_match_table = button_of_match,
    },
    .probe = button_probe,
};

module_platform_driver(button_driver);

设备树节点:

button:button@0{
compatible="myvendor,gpio-button";
button-gpios=<&gpio17 GPIO_ACTIVE_LOW>;
status="okay";
};

扩展四:内核启动流程优化

4.1 启动时间分析

# 在内核命令行添加
initcall_debug printk.time=1

# 启动后分析
dmesg | grep "initcall" | sort -k2 -n -t'=' | tail -20

# 使用bootchart
sudo apt install bootchart pybootchartgui
# 重启后查看 /var/log/bootchart.png

# 使用systemd-analyze
systemd-analyze
systemd-analyze blame
systemd-analyze critical-chain

4.2 内核配置优化

# 最小化内核配置
make tinyconfig  # 极简配置起点

# 或从当前配置裁剪
make localmodconfig  # 只保留当前使用的模块

# 禁用不需要的功能
scripts/config --disable DEBUG_INFO
scripts/config --disable PRINTK
scripts/config --disable BUG
scripts/config --disable KALLSYMS
scripts/config --disable FTRACE
scripts/config --disable KPROBES

# 启用快速启动选项
scripts/config --enable CC_OPTIMIZE_FOR_SIZE
scripts/config --enable EMBEDDED
scripts/config --enable EXPERT

4.3 initcall优化

创建 fast_init.c

#include<linux/init.h>
#include<linux/module.h>

MODULE_LICENSE("GPL");

// 使用不同的初始化级别
// pure_initcall     - 0
// core_initcall     - 1
// postcore_initcall - 2
// arch_initcall     - 3
// subsys_initcall   - 4
// fs_initcall       - 5
// device_initcall   - 6
// late_initcall     - 7

staticint __init early_init(void)
{
    pr_info("Early init (core level)\n");
return0;
}
core_initcall(early_init);

staticint __init normal_init(void)
{
    pr_info("Normal init (device level)\n");
return0;
}
device_initcall(normal_init);

staticint __init late_init(void)
{
    pr_info("Late init\n");
return0;
}
late_initcall(late_init);

// 异步初始化
staticint __init async_init(void)
{
    pr_info("Async init starting\n");
return0;
}
// 标记为可异步执行
module_init(async_init);

4.4 延迟加载模块

创建 deferred_probe.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/of.h>

MODULE_LICENSE("GPL");

staticintmydev_probe(struct platform_device *pdev)
{
structdevice *dependency;

// 检查依赖设备是否就绪
    dependency = bus_find_device_by_name(&platform_bus_type, 
NULL"i2c-controller");
if (!dependency) {
        dev_info(&pdev->dev, "Dependency not ready, deferring\n");
return -EPROBE_DEFER;  // 请求稍后重试
    }
    put_device(dependency);

    dev_info(&pdev->dev, "Device probed successfully\n");
return0;
}

staticconststructof_device_idmydev_of_match[] = {
    { .compatible = "myvendor,mydev" },
    { }
};

staticstructplatform_drivermydev_driver = {
    .driver = {
        .name = "mydev",
        .of_match_table = mydev_of_match,
    },
    .probe = mydev_probe,
};

module_platform_driver(mydev_driver);

4.5 压缩内核优化

# 内核压缩选项比较
# CONFIG_KERNEL_GZIP  - 兼容性好,解压慢
# CONFIG_KERNEL_LZMA  - 压缩率高,解压慢
# CONFIG_KERNEL_XZ    - 压缩率高,解压较慢
# CONFIG_KERNEL_LZO   - 压缩率低,解压快
# CONFIG_KERNEL_LZ4   - 压缩率低,解压最快

# 快速启动推荐LZ4
scripts/config --disable KERNEL_GZIP
scripts/config --enable KERNEL_LZ4

4.6 启动时间测量模块

创建 boot_time_measure.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/proc_fs.h>
#include<linux/seq_file.h>
#include<linux/sched/clock.h>

MODULE_LICENSE("GPL");

static u64 module_load_time;

staticintboottime_show(struct seq_file *m, void *v)
{
    u64 now = local_clock();
    u64 uptime_ns = now;
    u64 uptime_ms = uptime_ns / 1000000;
    u64 module_time_ms = module_load_time / 1000000;

    seq_printf(m, "=== Boot Time Analysis ===\n\n");
    seq_printf(m, "Current uptime: %llu.%03llu seconds\n",
               uptime_ms / 1000, uptime_ms % 1000);
    seq_printf(m, "Module loaded at: %llu.%03llu seconds\n",
               module_time_ms / 1000, module_time_ms % 1000);

// 从/proc/stat读取启动时间
    seq_printf(m, "\n--- Optimization Tips ---\n");
    seq_printf(m, "1. Use initcall_debug to find slow initcalls\n");
    seq_printf(m, "2. Use CONFIG_KERNEL_LZ4 for faster decompression\n");
    seq_printf(m, "3. Defer non-critical driver loading\n");
    seq_printf(m, "4. Use async probe where possible\n");
    seq_printf(m, "5. Minimize kernel size with localmodconfig\n");

return0;
}

staticintboottime_open(struct inode *inode, struct file *file)
{
return single_open(file, boottime_show, NULL);
}

staticconststructproc_opsboottime_ops = {
    .proc_open = boottime_open,
    .proc_read = seq_read,
    .proc_lseek = seq_lseek,
    .proc_release = single_release,
};

staticstructproc_dir_entry *proc_entry;

staticint __init boottime_init(void)
{
    module_load_time = local_clock();
    proc_entry = proc_create("boot_time_analysis"0444NULL, &boottime_ops);
    pr_info("Boot time module loaded at %llu ns\n", module_load_time);
return proc_entry ? 0 : -ENOMEM;
}

staticvoid __exit boottime_exit(void)
{
    proc_remove(proc_entry);
}

module_init(boottime_init);
module_exit(boottime_exit);

扩展Makefile

ARCH ?= arm64
CROSS_COMPILE ?= aarch64-linux-gnu-

KDIR ?= /path/to/linux-kernel

obj-m += rpi_gpio_led.o
obj-m += rpi_i2c_sensor.o
obj-m += spi_display.o
obj-m += pwm_motor.o
obj-m += gpio_irq_button.o
obj-m += boot_time_measure.o

all:
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) \
   -C $(KDIR) M=$(PWD) modules

clean:
make -C $(KDIR) M=$(PWD) clean

# 本地编译(x86测试)
local:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

# 部署到树莓派
deploy:
scp *.ko pi@raspberrypi.local:~/modules/

# 安装到rootfs
install:
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) \
   -C $(KDIR) M=$(PWD) \
   INSTALL_MOD_PATH=$(DESTDIR) modules_install

扩展检查清单

树莓派开发

  • [ ] 能交叉编译树莓派内核
  • [ ] 能部署内核到SD卡
  • [ ] 能编写GPIO驱动
  • [ ] 能编写I2C设备驱动

Yocto构建

  • [ ] 理解Yocto层次结构
  • [ ] 能配置local.conf
  • [ ] 能编写应用Recipe
  • [ ] 能编写内核模块Recipe
  • [ ] 能构建自定义镜像

硬件驱动

  • [ ] 能编写SPI驱动
  • [ ] 能编写PWM驱动
  • [ ] 能处理GPIO中断
  • [ ] 能使用设备树配置

启动优化

  • [ ] 能分析启动时间
  • [ ] 能优化内核配置
  • [ ] 理解initcall级别
  • [ ] 能使用延迟探测
  • [ ] 能选择合适的压缩算法

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-07 13:49:00 HTTP/2.0 GET : https://f.mffb.com.cn/a/474112.html
  2. 运行时间 : 0.085411s [ 吞吐率:11.71req/s ] 内存消耗:5,282.69kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=7bc77a379b4c115f8639d50818a0d5a0
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000710s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000837s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000309s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000278s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000695s ]
  6. SELECT * FROM `set` [ RunTime:0.000288s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000550s ]
  8. SELECT * FROM `article` WHERE `id` = 474112 LIMIT 1 [ RunTime:0.000941s ]
  9. UPDATE `article` SET `lasttime` = 1770443340 WHERE `id` = 474112 [ RunTime:0.008467s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000273s ]
  11. SELECT * FROM `article` WHERE `id` < 474112 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000334s ]
  12. SELECT * FROM `article` WHERE `id` > 474112 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000472s ]
  13. SELECT * FROM `article` WHERE `id` < 474112 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000640s ]
  14. SELECT * FROM `article` WHERE `id` < 474112 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.000837s ]
  15. SELECT * FROM `article` WHERE `id` < 474112 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.001773s ]
0.086896s