嵌入式linux学习记录十四、术语
2026/6/24 12:51:32 网站建设 项目流程
  1. 大管家:

    struct keyinput_dev

    {

    dev_t devid; /* 设备号 */

    struct cdev cdev; /* cdev */

    struct class *class; /* 类 */

    struct device *device; /* 设备 */

    struct device_node *nd; /* 设备节点 */

    struct timer_list timer; /* 定义一个定时器*/

    struct irq_keydesc *irqkeydesc; /* 按键描述数组 */

    unsigned char curkeynum; /* 当前的按键号 */

    int key_nums; /* 账本:记录设备树里实际配置的按键数量 */

    struct input_dev *inputdev; /* input结构体 */

    };

    1. dev_t dev_id(门牌号):主次设备号。就是一个本质上就是一个u32类型的数字。高 12 位是主设备号,低 20 位是次设备号。它仅仅是个数字,用来在内核登记备案。
    2. struct cdev cdev(灵魂/行为):内核里代表“字符设备”的核心结构体。它里面最重要的成员是file_operations(函数指针集)。应用层调用read/write时,内核就是通过它找到你的 C 语言驱动函数的。
      1. 对下绑定身份:dev(设备号)

        字符设备必须要有一个唯一的身份证号。通过cdev_add函数,内核把设备号dev与这个cdev结构体进行哈希绑定。
        物理意义:当应用层去打开/dev/ap3216c文件时,内核通过文件自带的设备号,就能在工商大网(哈希表)里瞬间定位到这个cdev结构体。

      2. 对上绑定业务:opsstruct file_operations

        光找到设备还不够,应用层接下来还要调用read/write/opencdev->ops指针就直接指向了你自己在驱动里实现的“业务函数大盘”。
        物理意义:内核顺着设备号找到cdev后,会立刻翻开它的ops菜单,看看里面有没有写着.open = ap3216c_open。如果有,就把控制权交过去。

    3. struct class *class(家族/分类):高层管理概念。比如创建一个叫"my_led_class"的类,在系统的/sys/class/目录下就会多一个文件夹。它是为了批量管理属于同一类的设备。
    4. struct device *device(窗户/文件):它是类(class)的孩子。它的诞生会直接触发内核在/dev/目录下生成用户可见的设备文件(比如/dev/led)。应用层就是通过这个窗户和驱动说话的。
    5. struct device_node *nd(硬件图纸):设备树专属结构体。它不参与上面的任何软件分配,它唯一的任务就是精准指向设备树(.dts)里的那个节点,供你解析 GPIO 编号、中断号等硬件资源。
  2. 小管家:

    struct irq_keydesc

    {

    int gpio; /* gpio */

    int irqnum; /* 中断号 */

    unsigned char value; /* 按键对应的键值 */

    char name[10]; /* 名字 */

    struct keyinput_dev *back_dev; /* 【关键】反向指针,指向它的大管家 */

    // irqreturn_t (*handler)(int, void *); /* 中断服务函数 */

    };

    1. gpio:“我负责盯着哪根硬件引脚?(比如 GPIO1_IO18)”

    2. irqnum:“如果有人拍这根引脚,我该通过哪个特定的电话线(中断号)向大管家汇报?”

    3. value:“如果确认这个人真的按下了,我该给 Input 大集团上报什么数字键值?(比如上报KEY_0还是KEY_ENTER?)”

    4. name:“我的工牌名字叫什么?(比如"KEY0",申请中断时给内核看的)”

    5. handler:“当我的电话响了(中断触发),我该用哪个具体的动作(中断服务函数指针)去接电话?”,

  3. struct inode—— 停在车库里的“物理实体车”

    1. 比喻:它是车库里那辆实实在在、有车架号、有固定排量的真车(比如一辆具体的五菱宏光)

    2. 特点:不管有没有人来租,这辆车都铁打不动地停在车库里。在整个系统里,一辆物理车(一个文件)有且仅有一个inode结构体。在 Linux “一切皆文件” 的大盘下,磁盘上的每一个文件、/dev/下的每一个设备节点,在内核内存里都有且仅有一份专属的struct inode结构体来记录它的元数据(比如文件大小、创建时间、文件类型,以及我们最关心的设备号)。

  4. struct file (filp)—— 客户签下的“租车合同”

    1. 比喻:当一个客户(应用层进程)跑来调用open()说“我要租车”时,前台营业员不会把整辆车直接塞进客户的口袋,而是会当场打印一份“租车合同”,这就是filp

    2. 特点

      • 如果有 3 个客户同时open了同一个设备文件,车库里依然只有一辆车(1个inode),但前台会打印出 3 份独立的合同(3个filp)。

      • 合同(filp)里记录的是只有这次租车才关心的动态信息:比如你开到哪了(f_pos读写偏移量)、你是只读还是读写(f_flags权限)、以及合同特有的备注栏(private_data

  5. struct inodestruct device区别和联系:
    在 Linux 内核中,struct inodestruct device都是极其核心的底层结构体,但它们属于完全不同的两个管理维度

    简单来说:inode属于“文件系统维度”(软件抽象),而device属于“设备驱动模型维度”(硬件抽象)。为了让你彻底搞懂它们的关系,我们先看两者的核心区别,再看它们在字符设备驱动中是如何交汇连接的。

    1. 核心区别:它们各自管什么?

      1. struct device—— 驱动模型的“硬件护照”

        1. 所属领域:设备驱动模型(Linux Device Model)。

        2. 它是什么:它是内核用来抽象、拓扑物理硬件的基类。无论是底层的 GPIO 控制器、I2C 总线,还是你通过平台总线注册的按键(pdev->dev),在内核眼里都是一个struct device

        3. 核心职责:建立内核的硬件树状拓扑结构(展示在/sys/devices/体系中)。它记录了硬件的总线类型(bus)、电源管理状态(power)、父设备(parent)、以及驱动私有数据(driver_data)。

        4. 生命周期:由设备总线(Platform/I2C/SPI 等)或驱动框架管理。当你在probe阶段或通过device_create()注册硬件时,它才在内存中被创建。

      2. struct inode—— 文件系统的“档案实体”

        1. 所属领域:VFS(虚拟文件系统)。

        2. 它是什么:它是磁盘或内存文件系统里,某个文件/节点的物理唯一标识。在 Linux “一切皆文件”的哲学下,每个文件(无论是普通文本、目录,还是/dev/key这样的设备文件)在内核中都有且仅有一个struct inode

        3. 核心职责:记录文件的元数据。比如:文件大小、访问权限、所有者、修改时间、以及该文件对应的主次设备号(dev_t i_rdev)和字符设备指针(struct cdev *i_cdev)。

        4. 生命周期:由文件系统管理。当你在/dev/下看到一个节点,哪怕没有任何驱动绑定它,它的inode也已经存在于文件系统中了。

    2. 核心联系:它们在何处交汇?

      虽然它们一个管“文件”,一个管“硬件”,但在字符设备驱动(或者是你正在写的 Input 子系统驱动)中,它们会经历一场完美的接力与交汇。

      我们通过你在应用层执行open("/dev/input/event1")时的底层流动,来看它们是如何发生联系的:

      1. 桥梁一:设备号(dev_t)这是它们产生联系的纽带。

        1. 硬件端(struct device:你在probe阶段调用device_create()或者input_register_device()时,内核会为你分配一个主次设备号,并把这个设备号打上struct device的标签。

        2. 软件端(struct inode:内核的udevmdev守护进程感知到新硬件加入后,会在/dev/下创建一个对应的设备文件。这个文件的inode->i_rdev里,就死死地写入了和硬件端一模一样的设备号

      2. 桥梁二:通用open的寻路过程。当应用层通过打开文件找到硬件大管家时,两条线彻底拧成一股绳:

        1. 第一步(通过inode抓到 cdev): 应用层调用open("/dev/input/event1")。内核顺着路径找到该文件的struct inode。内核看了一眼inode->i_cdev,噢!它指向了你之前在probe里注册的字符设备(cdev)。

        2. 第二步(通过 cdev 跨界到device: 在标准驱动中,你的大管家结构体通常把cdevstruct device *封装在一起(或者在 Input 子系统里,evdev结构体同时关联了cdevstruct device)。

        3. 第三步(落脚到device抽屉): 通用open函数通过container_ofinode->i_cdev捞出大管家,再从大管家里拿到struct device,最终调用dev_get_drvdata()或者input_get_drvdata()提取出你寄存的内存指针,塞进filp->private_data

  6. struct device_node和struct device关系:
    .dts 文件(你写的设备树源码)
    ↓ 编译
    .dtb(二进制设备树)
    ↓ 内核启动时解析
    struct device_node(内存中的树状结构,描述硬件"长什么样")
    ↓ 驱动 match & probe 成功后
    struct device(驱动模型中的设备对象,代表"这个硬件正在被管理")

  7. inode->i_cdev = 查找到的struct cdev的内存地址;

  8. 在 Linux 中,万物皆文件。为了让用户空间了解内核的情况,内核已经有了两个大名鼎鼎的虚拟文件系统:

    1. Procfs(挂载在/proc:主要用于展示与系统进程、内存、CPU、中断等相关的核心运行状态。它的管制非常严格,不准乱放驱动的私有调试信息。

    2. Sysfs(挂载在/sys:主要用于展示设备的拓扑模型与标准的硬件属性(比如你的触摸屏灵敏度、LED 的亮灭)。它有着严格的设备树和分类哲学,每一个文件的命名和存放位置都要符合内核规范。

    3. Debugfs(挂载在/sys/kernel/debug/是 Linux 内核提供的一种专门用于开发调试的虚拟文件系统(Virtual File System)。简单来说,它是内核空间与应用层(用户空间)之间的一条无限制、高自由度的“秘密通道”。内核开发者可以通过它,把驱动内部的任意变量、状态或大块的原始数据,以“文件”的形式暴露给用户空间,方便在终端进行查看或修改。Debugfs 的工作机制高度依赖内核提供的标准 API。在你的edt-ft5x06触摸屏驱动里,就是一套教科书式的用法:

      1. 建大门(创建目录): 驱动调用debugfs_create_dir("edt-ft5x06", NULL),内核在/sys/kernel/debug/下划出一块底盘。

      2. 摆摊位(创建文件并绑定变量/操作)

        • 绑定简单变量:调用debugfs_create_u16("num_x", ...)。内核会自动打通,当你cat num_x时,内核直接把结构体里的tsdata->num_x变量值转成字符串吐给你。

        • 绑定复杂行为:调用debugfs_create_file("mode", ..., &debugfs_mode_fops)。将文件节点与自定义的文件操作结构体(包含读写回调函数)死死绑定在一起。

      3. 撤摊撤场(驱动卸载): 当驱动被移除(remove)时,调用debugfs_remove_recursive(tsdata->debug_dir),一键把该目录下所有的虚拟文件和文件夹安全地从内存中抹去。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询