当前位置:首页>Linux>嵌入式 Linux — platform driver 框架完整实战

嵌入式 Linux — platform driver 框架完整实战

  • 2026-06-20 09:34:58
嵌入式 Linux — platform driver 框架完整实战

板块:嵌入式 Linux


上一篇把 platform driver 的骨架讲了一遍,这篇把内容填上:驱动和设备怎么匹配、用户空间怎么和驱动交互、read/write/ioctl 怎么实现、不同类型的外设该挂进哪个框架、GPIO 中断怎么接、数据在内核和用户空间之间怎么传——把这几件事合在一起讲清楚。


一、四种匹配方式

内核里 platform_match() 函数按固定优先级依次尝试四种匹配方式,找到一种成功就停下来。[¹]

来源:drivers/base/platform.cplatform_match() 函数:

static int platform_match(struct device *dev, struct device_driver *drv){struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);    /* 1. driver_override:强制绑定,优先级最高 */    if (pdev->driver_override)        return !strcmp(pdev->driver_override, drv->name);    /* 2. OF style:设备树 compatible 字符串匹配 */    if (of_driver_match_device(dev, drv))        return 1;    /* 3. ACPI style:x86/ACPI 平台用 */    if (acpi_driver_match_device(dev, drv))        return 1;    /* 4. id_table:按设备名查表 */    if (pdrv->id_table)        return platform_match_id(pdrv->id_table, pdev) != NULL;    /* 5. 最后兜底:直接比较 name 字符串 */    return (strcmp(pdev->name, drv->name) == 0);}

嵌入式开发常用的是方式 2 和方式 4,方式 5 在没有设备树的老平台或者调试时用得到。

方式 2:of_match_table(设备树)

现代嵌入式 Linux 的标准做法,上一篇已经讲过。设备树节点的 compatible 和驱动的 of_match_table 对上即匹配:

static conststruct of_device_id mydev_of_match[] = {    { .compatible = "myvendor,sensor-v1" },    { .compatible = "myvendor,sensor-v2", .data = &v2_config }, /* .data 可以传版本特定配置 */    { }};MODULE_DEVICE_TABLE(of, mydev_of_match);

of_match_table 里每一项的 .data 字段可以存一个指针,probe() 里通过 of_device_get_match_data() 取回来,实现"同一个驱动支持多个硬件版本,行为略有不同":

static int mydev_probe(struct platform_device *pdev){    conststruct mydev_config *cfg;    cfg = of_device_get_match_data(&pdev->dev);    /* cfg 指向匹配到的那一行的 .data */    if (cfg && cfg->has_dma)        /* ... 走 DMA 路径 ... */}

方式 4:id_table(按名称查表,无设备树时用)

没有设备树的老平台,或者在代码里手动注册 platform_device 时,用 id_table 按名字匹配:

/* 驱动侧 */static conststruct platform_device_id mydev_id_table[] = {    { "mydevice-A", (kernel_ulong_t)&config_A },    { "mydevice-B", (kernel_ulong_t)&config_B },    { }  /* sentinel */};MODULE_DEVICE_TABLE(platform, mydev_id_table);staticstruct platform_driver mydev_driver = {    .probe    = mydev_probe,    .remove   = mydev_remove,    .id_table = mydev_id_table,    .driver   = { .name = "mydevice" },};
/* 设备侧(board file 或测试时手动注册)*/staticstruct platform_device mydev_A = {    .name = "mydevice-A",    .id   = -1,};platform_device_register(&mydev_A);

probe() 里拿到的 pdev->id_entry->driver_data 就是 id_table 里对应行的 driver_data 字段。

方式 5:名称直接比较

最简单,驱动的 .driver.name 和 platform_device 的 .name 一致就匹配。调试用,或者极简场景下用。不推荐在正式驱动里依赖这个。

[¹] 参考:内核源码 drivers/base/platform.cplatform_match() 函数;O'Reilly "Linux Device Drivers Development",Chapter 7


二、用户空间与驱动交互的全貌

用户空间 (app)    │    │  open("/dev/mydevice", O_RDWR)    │  read(fd, buf, len)    │  write(fd, buf, len)    │  ioctl(fd, CMD, arg)    │  close(fd)    │    ▼  ─── 系统调用边界 ─── 内核空间 ───    │    ▼VFS 层(虚拟文件系统)    │    ▼字符设备层 → file_operations 函数表    │           .open    → mydev_open()    │           .read    → mydev_read()    │           .write   → mydev_write()    │           .unlocked_ioctl → mydev_ioctl()    │           .release → mydev_release()    │    ▼platform driver 的私有数据(priv->base 寄存器)    │    ▼硬件寄存器(通过 ioread32/iowrite32 操作)

用户空间的每一个系统调用,最终都落到驱动里注册的 file_operations 函数上。


三、内核空间和用户空间的数据传递

这是新手最容易踩坑的地方:内核空间和用户空间不能直接用 memcpy 互相访问,必须用专用函数。[²]

原因:用户空间指针传进内核后不能直接解引用——指针可能无效,可能指向换出的页,也可能是恶意构造的地址,直接访问会导致内核 oops 或安全漏洞。

/* 从用户空间读数据到内核 */unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);/* 返回值:未能复制的字节数,成功返回 0 *//* 从内核写数据到用户空间 */unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);/* 返回值:未能复制的字节数,成功返回 0 *//* 单个简单类型的快捷版本 */put_user(kernel_val, user_ptr);   /* 写一个值到用户空间 */get_user(kernel_var, user_ptr);   /* 从用户空间读一个值 */

__user 是给 sparse 静态分析工具看的注解,告诉它这个指针来自用户空间,运行时没有实际影响但有助于发现代码里的错误用法。

在 read()write()ioctl() 三个函数里,所有涉及用户空间缓冲区的地方都必须用这套函数:

static ssize_t mydev_read(struct file *file, char __user *buf,                          size_t count, loff_t *ppos){struct mydev_priv *priv = file->private_data;    u32 val;    /* 从寄存器读一个值 */    val = ioread32(priv->base + DATA_REG);    /* 把这个值写到用户空间的 buf */    if (copy_to_user(buf, &val, sizeof(val)))        return -EFAULT;    return sizeof(val);}static ssize_t mydev_write(struct file *file, const char __user *buf,                           size_t count, loff_t *ppos){struct mydev_priv *priv = file->private_data;    u32 val;    /* 从用户空间的 buf 读一个值 */    if (copy_from_user(&val, buf, sizeof(val)))        return -EFAULT;    /* 写到寄存器 */    iowrite32(val, priv->base + CTRL_REG);    return sizeof(val);}

[²] 参考:内核文档 "copy_to_user / copy_from_user";Linux kernel labs,https://linux-kernel-labs.github.io/refs/pull/189/merge/labs/device_drivers.html


四、ioctl:发控制命令

read 和 write 只能传数据流,控制命令用 ioctl。比如配置 ADC 量程、让屏幕旋转 90 度、清空 DAC 输出——这些用 ioctl 比用 read/write 更清晰。

ioctl 命令号的定义

内核提供了宏来生成不冲突的 ioctl 命令号,定义在共享头文件里(驱动和 app 都 include 这个文件):[²]

/* my_ioctl.h —— 驱动和用户空间 app 共享这个头文件 */#include <linux/ioctl.h>#define MYDEV_MAGIC  'M'   /* 幻数,选一个你的驱动专用的字符,参考内核 Documentation/userspace-api/ioctl/ioctl-number.rst *//* _IO:无数据传输的命令 */#define MYDEV_RESET         _IO(MYDEV_MAGIC,  0)/* _IOR:从内核读数据到用户空间(Read from kernel) */#define MYDEV_GET_STATUS    _IOR(MYDEV_MAGIC, 1, u32)/* _IOW:用户空间写数据到内核(Write to kernel) */#define MYDEV_SET_FREQ      _IOW(MYDEV_MAGIC, 2, u32)/* _IOWR:双向传输 */#define MYDEV_XFER          _IOWR(MYDEV_MAGIC, 3, struct mydev_xfer_data)

驱动侧的 ioctl 实现

static long mydev_ioctl(struct file *file, unsigned int cmd, unsigned long arg){struct mydev_priv *priv = file->private_data;    u32 val;    int ret = 0;    /* 验证命令号来自本驱动(检查幻数和编号范围) */    if (_IOC_TYPE(cmd) != MYDEV_MAGIC)        return -ENOTTY;    switch (cmd) {    case MYDEV_RESET:        /* 无数据传输,直接操作寄存器 */        iowrite32(0x1, priv->base + RESET_REG);        break;    case MYDEV_GET_STATUS:        /* 从寄存器读值,写回用户空间 */        val = ioread32(priv->base + STATUS_REG);        if (copy_to_user((void __user *)arg, &val, sizeof(val)))            return -EFAULT;        break;    case MYDEV_SET_FREQ:        /* 从用户空间读值,写进寄存器 */        if (copy_from_user(&val, (void __user *)arg, sizeof(val)))            return -EFAULT;        iowrite32(val, priv->base + FREQ_REG);        break;    default:        ret = -ENOTTY;  /* 命令不支持 */        break;    }    return ret;}

用户空间 app 调用

/* app.c */#include <fcntl.h>#include <sys/ioctl.h>#include "my_ioctl.h"   /* 和驱动共享的头文件 */int main(void){    int fd = open("/dev/mydevice", O_RDWR);    if (fd < 0) { perror("open"); return 1; }    /* 复位设备 */    ioctl(fd, MYDEV_RESET);    /* 读状态 */    uint32_t status;    ioctl(fd, MYDEV_GET_STATUS, &status);    printf("status = 0x%08x\n", status);    /* 设置频率 */    uint32_t freq = 1000000;    ioctl(fd, MYDEV_SET_FREQ, &freq);    close(fd);    return 0;}

五、完整的字符设备驱动:从 probe 到 file_operations

把上面所有东西串成一个完整的驱动,带 misc 设备注册(misc 是最简单的字符设备注册方式,不需要手动申请主设备号):

/* mydevice.c */#include <linux/module.h>#include <linux/platform_device.h>#include <linux/miscdevice.h>#include <linux/fs.h>#include <linux/of.h>#include <linux/io.h>#include <linux/interrupt.h>#include <linux/uaccess.h>#include "my_ioctl.h"/* 寄存器偏移(对应你的硬件手册) */#define DATA_REG    0x00#define CTRL_REG    0x04#define STATUS_REG  0x08#define RESET_REG   0x0C#define FREQ_REG    0x10struct mydev_priv {    void __iomem    *base;    int              irq;struct miscdevice miscdev;   /* misc 设备,内嵌在私有数据里 */struct device   *dev;};/* ── file_operations ── */static int mydev_open(struct inode *inode, struct file *file){    /* 从 misc 设备找回私有数据,存到 file->private_data */struct mydev_priv *priv = container_of(file->private_data,                                           struct mydev_priv, miscdev);    file->private_data = priv;    dev_dbg(priv->dev, "opened\n");    return 0;}static int mydev_release(struct inode *inode, struct file *file){    return 0;}static ssize_t mydev_read(struct file *file, char __user *buf,                          size_t count, loff_t *ppos){struct mydev_priv *priv = file->private_data;    u32 val;    if (count < sizeof(val))        return -EINVAL;    val = ioread32(priv->base + DATA_REG);    if (copy_to_user(buf, &val, sizeof(val)))        return -EFAULT;    return sizeof(val);}static ssize_t mydev_write(struct file *file, const char __user *buf,                           size_t count, loff_t *ppos){struct mydev_priv *priv = file->private_data;    u32 val;    if (count < sizeof(val))        return -EINVAL;    if (copy_from_user(&val, buf, sizeof(val)))        return -EFAULT;    iowrite32(val, priv->base + CTRL_REG);    return sizeof(val);}static long mydev_ioctl(struct file *file, unsigned int cmd, unsigned long arg){struct mydev_priv *priv = file->private_data;    u32 val;    if (_IOC_TYPE(cmd) != MYDEV_MAGIC)        return -ENOTTY;    switch (cmd) {    case MYDEV_RESET:        iowrite32(0x1, priv->base + RESET_REG);        break;    case MYDEV_GET_STATUS:        val = ioread32(priv->base + STATUS_REG);        if (copy_to_user((void __user *)arg, &val, sizeof(val)))            return -EFAULT;        break;    case MYDEV_SET_FREQ:        if (copy_from_user(&val, (void __user *)arg, sizeof(val)))            return -EFAULT;        iowrite32(val, priv->base + FREQ_REG);        break;    default:        return -ENOTTY;    }    return 0;}static conststruct file_operations mydev_fops = {    .owner          = THIS_MODULE,    .open           = mydev_open,    .release        = mydev_release,    .read           = mydev_read,    .write          = mydev_write,    .unlocked_ioctl = mydev_ioctl,};/* ── 中断处理 ── */static irqreturn_t mydev_isr(int irq, void *data){struct mydev_priv *priv = data;    u32 status;    status = ioread32(priv->base + STATUS_REG);    /* 清中断标志(具体寄存器操作看你的硬件手册) */    iowrite32(status, priv->base + STATUS_REG);    dev_dbg(priv->dev, "IRQ: status=0x%08x\n", status);    return IRQ_HANDLED;}/* ── probe / remove ── */static int mydev_probe(struct platform_device *pdev){struct mydev_priv *priv;struct resource *res;    int ret;    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);    if (!priv)        return -ENOMEM;    priv->dev = &pdev->dev;    /* 获取并映射寄存器 */    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    priv->base = devm_ioremap_resource(&pdev->dev, res);    if (IS_ERR(priv->base))        return PTR_ERR(priv->base);    /* 获取中断 */    priv->irq = platform_get_irq(pdev, 0);    if (priv->irq < 0)        return priv->irq;    ret = devm_request_irq(&pdev->dev, priv->irq, mydev_isr,                           0, dev_name(&pdev->dev), priv);    if (ret)        return ret;    /* 注册 misc 设备,内核自动分配次设备号,/dev/mydevice 自动创建 */    priv->miscdev.minor = MISC_DYNAMIC_MINOR;    priv->miscdev.name  = "mydevice";    priv->miscdev.fops  = &mydev_fops;    ret = misc_register(&priv->miscdev);    if (ret) {        dev_err(&pdev->dev, "misc_register failed: %d\n", ret);        return ret;    }    platform_set_drvdata(pdev, priv);    dev_info(&pdev->dev, "probe OK, /dev/mydevice ready\n");    return 0;}static int mydev_remove(struct platform_device *pdev){struct mydev_priv *priv = platform_get_drvdata(pdev);    misc_deregister(&priv->miscdev);    dev_info(&pdev->dev, "removed\n");    return 0;}static conststruct of_device_id mydev_of_match[] = {    { .compatible = "myvendor,mydevice-v1" },    { }};MODULE_DEVICE_TABLE(of, mydev_of_match);staticstruct platform_driver mydev_driver = {    .probe  = mydev_probe,    .remove = mydev_remove,    .driver = {        .name           = "mydevice",        .of_match_table = mydev_of_match,    },};module_platform_driver(mydev_driver);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("Full platform driver with char device example");

六、GPIO 中断触发的驱动

上面用的是 platform_get_irq(),它从设备树的 interrupts 属性取 GIC 直连的中断。但很多外设的中断线接在 GPIO 上,用法稍有不同。

设备树节点:

sensor_irq_node: mysensor@44 {    compatible = "myvendor,mysensor";    reg = <0x44>;    interrupt-parent = <&gpio1>;         /* GPIO1 控制器 */    interrupts = <5 IRQ_TYPE_EDGE_FALLING>; /* GPIO1_IO05,下降沿触发 */};

驱动里申请 GPIO 中断有两种方法。

方法一:用 gpiod 接口(现代推荐写法)

#include <linux/gpio/consumer.h>static int mysensor_probe(struct platform_device *pdev){struct mysensor_priv *priv;struct gpio_desc *irq_gpio;    int irq, ret;    /* 从设备树的 interrupts 属性解析 GPIO,然后申请为中断 */    irq_gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN);    if (IS_ERR(irq_gpio))        return PTR_ERR(irq_gpio);   /* 可能是 -EPROBE_DEFER */    /* 把 GPIO 转换成 Linux IRQ 号 */    irq = gpiod_to_irq(irq_gpio);    if (irq < 0)        return irq;    ret = devm_request_threaded_irq(&pdev->dev, irq,        NULL,               /* 硬中断上半部,NULL 表示不用 */        mysensor_irq_thread, /* 线程化下半部,在内核线程里执行 */        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,        "mysensor-irq", priv);    if (ret)        return ret;    return 0;}/* 线程化中断处理,可以睡眠,适合需要 I2C/SPI 通信的场景 */static irqreturn_t mysensor_irq_thread(int irq, void *data){struct mysensor_priv *priv = data;    /* 这里可以做 I2C 读取,因为是线程上下文,可以睡眠 */    /* i2c_smbus_read_byte_data(priv->client, REG_STATUS); */    return IRQ_HANDLED;}

方法二:直接用 platform_get_irq()

如果设备树写的是 interrupt-parent = <&gic> 直连 GIC,或者内核在解析设备树时已经帮你把 GPIO 中断转换好了(很多平台会这样),就直接用 platform_get_irq(pdev, 0) 拿到 IRQ 号,跟之前一样处理。

线程化中断(request_threaded_irq)和普通中断的区别:

硬中断(request_irq
线程化中断(request_threaded_irq
执行上下文
硬件中断上下文
内核线程
能否睡眠
不能
可以
适用场景
只做寄存器读写、置标志位
需要 I2C/SPI 通信的传感器
延迟
稍高(线程调度延迟)

GPIO 上挂着 I2C 传感器中断的场景,几乎都应该用 request_threaded_irq,因为处理中断必须读 I2C,而 I2C 操作可能睡眠。


七、不同外设对应不同上层框架

platform driver 只是"把驱动和硬件配对、拿到寄存器地址"的机制,拿到硬件资源之后怎么向上层暴露,取决于外设类型。不是所有驱动都要注册字符设备。

SPI ADC / I2C ADC → IIO 框架

ADC、DAC 这类数据采集设备,应该注册进 IIO(Industrial I/O)框架,而不是自己写字符设备。IIO 框架统一管理采样、触发、缓冲,用户空间通过 /sys/bus/iio/devices/iio:device0/in_voltage0_raw 读数据,或者通过 /dev/iio:device0 做连续采样。[³]

/* probe() 里注册 IIO 设备 */static int myadc_probe(struct platform_device *pdev){struct iio_dev *indio_dev;struct myadc_priv *priv;    /* 分配 iio_dev,同时分配驱动私有数据 */    indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));    if (!indio_dev)        return -ENOMEM;    priv = iio_priv(indio_dev);    /* ... 初始化硬件 ... */    indio_dev->name    = "myadc";    indio_dev->modes   = INDIO_DIRECT_MODE;    indio_dev->info    = &myadc_iio_info;     /* 包含 read_raw 回调 */    indio_dev->channels = myadc_channels;    indio_dev->num_channels = ARRAY_SIZE(myadc_channels);    return devm_iio_device_register(&pdev->dev, indio_dev);}

用户空间读 ADC:

# 单次读取通道 0 的原始值cat /sys/bus/iio/devices/iio:device0/in_voltage0_raw# 换算成电压(单位 mV)cat /sys/bus/iio/devices/iio:device0/in_voltage0_scale

不需要也不应该给 ADC 写 read()/ioctl(),IIO 框架已经帮你处理了用户空间接口。

SPI/I2C 屏幕 → framebuffer 或 DRM

屏幕驱动通常注册进 framebuffer 框架(/dev/fb0)或 DRM/KMS 框架。用户空间通过 mmap 把帧缓冲映射到内存直接写像素,或者用 ioctl(fd, FBIOGET_VSCREENINFO, ...) 查询屏幕参数。

屏幕驱动确实需要 ioctl——但这些 ioctl 是 framebuffer 框架或 DRM 框架统一定义的,不是每个屏幕驱动自己定义的。驱动只需要实现框架要求的回调(比如 fb_ops 里的 .fb_fillrect.fb_copyarea),框架处理 ioctl 分发。

简单小尺寸 SPI 屏(如 ST7735、ILI9341)可以用内核的 mipi-dbi 框架(tinydrm 已在 5.4 合并进 drivers/gpu/drm/tiny/),让驱动变得很薄:

/* 基于 mipi-dbi 框架的 ILI9341 驱动骨架,极简 */static int ili9341_probe(struct spi_device *spi){struct mipi_dbi_dev *dbidev;struct mipi_dbi *dbi;    /* ... */    mipi_dbi_init(spi, dc, dbi, &ili9341_pipe_funcs, &ili9341_mode);    return mipi_dbi_dev_init(dbidev, &ili9341_funcs, &ili9341_mode);}

注意 SPI 屏的驱动入口是 spi_device,不是 platform_device,是 SPI 子系统管理的设备,和前面的 platform driver 走的是不同的总线。概念完全一样,换了个壳。

SPI DAC → IIO 框架(写通道)

DAC 也进 IIO,用户空间写 /sys/bus/iio/devices/iio:device1/out_voltage0_raw。实现上和 ADC 一样,iio_info 里提供 .write_raw 回调。

I2C 传感器(温湿度、IMU)→ IIO 或 hwmon

温湿度、气压等环境传感器:

  • • 进 IIO:更通用,支持触发和连续采样
  • • 进 hwmon:/sys/class/hwmon/hwmon0/temp1_input,适合只需要单次读取的简单场景

八、框架不止字符设备

总线-驱动-设备模型的 platform driver,在 probe() 里可以注册进任何内核子框架,不局限于字符设备:

注册进哪个框架
对应外设类型
用户空间接口
misc / cdev
自定义控制器、FPGA 逻辑
/dev/xxx
,open/read/write/ioctl
IIO
ADC、DAC、IMU、温湿度传感器
/sys/bus/iio/
/dev/iio:deviceX
hwmon
电源监控、风扇控制、温度传感器
/sys/class/hwmon/
input
按键、触摸屏、加速度计(作为输入设备)
/dev/input/eventX
regulator
电源管理芯片 PMU 的某路输出
内核内部 API,或 regulator sysfs
clk
外部时钟芯片
内核内部 clk API
rtc
实时时钟芯片
/dev/rtc0
ioctl(RTC_WKALM_SET, ...)
watchdog
看门狗定时器
/dev/watchdog
,ioctl
pwm
PWM 控制器
/sys/class/pwm/
 或 pwm_chip 接口
net
以太网控制器(少见)
标准网络接口

同一个 SoC 上的 SPI 控制器本身是一个 platform driver,probe() 里注册 spi_master,之后挂在这条 SPI 总线上的设备(比如 ADC、屏幕)就变成了 spi_device,由 SPI 子系统管理。platform driver 可以是"总线控制器驱动",也可以是"设备驱动",两者都用同一套框架,只是 probe() 里注册的东西不同。[⁴]

[³] 参考:内核文档 "Industrial I/O Core",https://docs.kernel.org/driver-api/iio/core.html[⁴] 参考:内核文档 "Platform Devices and Drivers",https://www.kernel.org/doc/html/latest/driver-api/driver-model/platform.html


九、驱动编译和加载

Makefile

# 驱动模块obj-m += mydevice.o# 如果驱动由多个 .c 文件组成:# mydevice-objs := mydevice_core.o mydevice_spi.o# obj-m += mydevice.oKDIR ?= /lib/modules/$(shell uname -r)/buildPWD  := $(shell pwd)all:    $(MAKE) -C $(KDIR) M=$(PWD) modulesclean:    $(MAKE) -C $(KDIR) M=$(PWD) clean

本机编译(x86,测试用):

make

交叉编译(目标板是 ARM):

make ARCH=arm \     CROSS_COMPILE=arm-linux-gnueabihf- \     KDIR=/path/to/your/kernel/source

内核源码路径必须是已经配置过(make defconfig 或 menuconfig)并且执行过 make modules_prepare 的,否则头文件不完整。

编译结果是 mydevice.ko,把它 scp 到目标板。

加载和卸载

# 加载模块(需要 root)insmod mydevice.ko# 加载时传参(如果驱动里有 module_param)insmod mydevice.ko debug_level=2# 查看加载是否成功lsmod | grep mydevicedmesg | tail -20   # 看 probe 的打印# 查看设备节点(misc_register 后自动创建)ls -l /dev/mydevice# 查看驱动绑定状态ls -l /sys/bus/platform/drivers/mydevice/# 里面有符号链接指向已绑定的 platform_device# 卸载rmmod mydevice# 持久化加载(放到这个目录,depmod 后开机自动加载)cp mydevice.ko /lib/modules/$(uname -r)/extra/depmod -aecho "mydevice" >> /etc/modules   # 或 /etc/modules-load.d/mydevice.conf

调试信息级别

dev_infodev_dbgdev_err 对应不同级别,dev_dbg 默认不打印,需要开启:

# 方法一:动态开启特定驱动的 debug 打印echo "file mydevice.c +p" > /sys/kernel/debug/dynamic_debug/control# 方法二:编译时加 -DDEBUG# ccflags-y += -DDEBUG

十、完整的用户空间 App 示例

/* app.c —— 和驱动交互的完整示例 */#include <stdio.h>#include <stdlib.h>#include <stdint.h>#include <fcntl.h>#include <unistd.h>#include <sys/ioctl.h>#include "my_ioctl.h"    /* 和驱动共享的头文件,包含 MYDEV_* 命令定义 */int main(void){    int fd;    uint32_t val;    ssize_t n;    fd = open("/dev/mydevice", O_RDWR);    if (fd < 0) {        perror("open /dev/mydevice");        return EXIT_FAILURE;    }    /* 1. 发 ioctl 复位设备 */    if (ioctl(fd, MYDEV_RESET) < 0) {        perror("ioctl MYDEV_RESET");        goto out;    }    /* 2. 用 ioctl 读状态寄存器 */    if (ioctl(fd, MYDEV_GET_STATUS, &val) < 0) {        perror("ioctl MYDEV_GET_STATUS");        goto out;    }    printf("status = 0x%08x\n", val);    /* 3. 用 write 写控制寄存器(对应驱动的 mydev_write) */    val = 0x00000001;   /* 启动 */    n = write(fd, &val, sizeof(val));    if (n < 0) {        perror("write");        goto out;    }    /* 4. 用 read 读数据寄存器(对应驱动的 mydev_read) */    n = read(fd, &val, sizeof(val));    if (n < 0) {        perror("read");        goto out;    }    printf("data = 0x%08x\n", val);out:    close(fd);    return 0;}

编译:

# 本机测试gcc -o app app.c -I./include# 交叉编译arm-linux-gnueabihf-gcc -o app app.c -I./include

把 app scp 到目标板,直接运行即可。


小结

这套框架的完整链路:

设备树 compatible    │ 匹配    ▼platform driver probe()    │ 获取资源(寄存器地址、中断号)    ▼初始化硬件    │ 注册进上层框架    ├──→ misc/cdev → file_operations(read/write/ioctl)    ├──→ IIO → iio_info(read_raw/write_raw)    ├──→ hwmon → hwmon_ops    ├──→ input → input_dev    └──→ framebuffer / DRM → fb_ops              │              ▼ 用户空间         open / read / write / ioctl              │         copy_to_user / copy_from_user              │ 跨越内核/用户空间边界         用户 app

选哪个框架不是随意的——它决定了用户空间用什么工具读数据、内核里哪些子系统能复用这个驱动。ADC 进 IIO,屏幕进 DRM,按键进 input,不要每个外设都自己造字符设备。


参考资料

  1. 1. 内核源码:drivers/base/platform.cplatform_match() — https://github.com/torvalds/linux/blob/master/drivers/base/platform.c
  2. 2. Linux kernel labs:Character device drivers — https://linux-kernel-labs.github.io/refs/pull/189/merge/labs/device_drivers.html
  3. 3. 内核文档:Industrial I/O Core — https://docs.kernel.org/driver-api/iio/core.html
  4. 4. 内核文档:Platform Devices and Drivers — https://www.kernel.org/doc/html/latest/driver-api/driver-model/platform.html
  5. 5. 内核文档:Devres(devm_* 托管资源)— https://docs.kernel.org/driver-api/driver-model/devres.html
  6. 6. LWN.net:IOCTL and unlocked_ioctl — https://lwn.net/Articles/119652/
  7. 7. O'Reilly "Linux Device Drivers Development"(John Madieu,2nd ed.,2022)

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-03 08:11:46 HTTP/2.0 GET : https://f.mffb.com.cn/a/496728.html
  2. 运行时间 : 0.226803s [ 吞吐率:4.41req/s ] 内存消耗:4,839.98kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=6d6e7d8018ed8441482c7cc3c4392235
  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.000387s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000693s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.018914s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.003346s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000708s ]
  6. SELECT * FROM `set` [ RunTime:0.011440s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000701s ]
  8. SELECT * FROM `article` WHERE `id` = 496728 LIMIT 1 [ RunTime:0.001252s ]
  9. UPDATE `article` SET `lasttime` = 1783037506 WHERE `id` = 496728 [ RunTime:0.000714s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.008423s ]
  11. SELECT * FROM `article` WHERE `id` < 496728 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.008823s ]
  12. SELECT * FROM `article` WHERE `id` > 496728 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.005399s ]
  13. SELECT * FROM `article` WHERE `id` < 496728 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.044443s ]
  14. SELECT * FROM `article` WHERE `id` < 496728 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.013357s ]
  15. SELECT * FROM `article` WHERE `id` < 496728 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.027566s ]
0.232293s