Lazy loaded image
🥳嵌入式 Linux开发
Linux驱动基础03-字符设备
Words 1901Read Time 5 min
2024-9-11
2025-3-25
type
date
slug
category
icon
password
 
linux中,我们使用设备编号来表示设备,主设备号区分设备类别,次设备号标识具体的设备。cdev结构体被内核用来记录设备号,使用设备时,通常会打开设备节点,通过设备节点inode结构体、file结构体最终找到 file_operation结构体,并从 file_operation结构体获得操作设备的具体方法。

设备号

每一行表示一个设备,每一行的第一个字符表示设备的类型。
  • c 用来标识字符设备
  • b 用来标识块设备
autofs是一个字符设备c, 它的主设备号是10(指向设备驱动程序),次设备号是235(指向某个具体设备)。

内核中设备编号的含义

内核中,设备编号用 dev_t表示,dev_t是一个32位的数,其中,高12位表示主设备号,低20位表示次设备号。
通过移位操作,从设备编号中得到主、次设备号。同样可以通过主次设备号位运算变成dev_t 类型的设备编号。

cdev结构体

内核通过一个散列表(哈希表)来记录设备编号。 哈希表由数组和链表组成,吸收数组查找快,链表增删效率高,容易拓展等优点。
以主设备号为cdev_map编号,使用哈希函数f(major)=major%255来计算组数下标(使用哈希函数是为了链表节点尽量平均分布在各个数组元素中,提高查询效率); 主设备号冲突,则以次设备号为比较值来排序链表节点。
notion image
  • struct kobject kobj:内嵌内核对象,将设备同意加入到“linux设备驱动模型‘’中管理(如对象的引用技术、电源管理、热插拔、生命周期、与用户通信等)
  • struct module *owner:字符设备驱动程序所在的内核模块对象的指针。
  • const struct file_operations *ops:文件操作,是字符设备驱动中非常重要的数据结构。应用程序通过文件系统(VFS)呼叫设备驱动程序中实现的文件操作类函数过程中,ops起着桥梁纽带作用。VFS与设备文件之间的接口是file_operations结构体成员函数,这个结构体包含了对文件进行打开、关闭、读写、控制等一系列成员函数。
  • struct list_head list:用于将系统中的字符设备形成链表(这是个内核链表的一个链接,可以再内核很多结构体中看到这种结构的身影)
  • dev_t dev:字符设备的设备号,有主设备和次设备号构成。
  • unsigned int count: 属于同一主设备号下次设备号的个数,用于表示驱动控制的同类设备的实际数量。

设备节点

设备节点(设备文件):Linux中通过 mknod命令来创建设备节点,设备节点是Linux内核对设备的抽象。
设备节点创建在/dev下,是连接内核与用户层的枢纽,设备是接到哪种接口的哪个ID 上。

数据结构

序号
结构体
描述
备注
1
file_operations
文件操作
关联系统调用和驱动程序
2
file
文件描述
已经打开的文件描述符
3
inode
索引节点
存储文件元数据
file_operations 结构体
include/linux/fs.h
include/linux/fs.h
file_operations结构体向系统提供设备驱动程序接口,结构体中每个成员对应一个系统调用。以下代码中只列出本章使用到的部分函数。
  • llseek: 修改文件的当前读写位置,并返回偏移后的位置。
    • 参数file:对应文件指针;
    • 参数loff_t:指定偏移量的大小;
    • 参数int:进行偏移位置,SEEK_SET,SEEK_CUR,SEEK_END。
  • read:读取设备中数据
    • 参数file:对应文件指针;
    • 参数char __user*: 数据缓冲区,地址空间是用户空间的,内核模块不能直接使用,需要使用copy_to_user函数操作;
    • 参数loff_t:指定偏移量的大小。
  • write:向设备写入数据
    • 参数file:对应文件指针;
    • 参数char __user*: 数据缓冲区,地址空间是用户空间的,内核模块不能直接使用,需要使用copy_from_user函数操作;
    • 参数loff_t:指定偏移量的大小。
  • unlocked_ioctl: 提供设备执行相关控制命令的实现方法,它对应于应用程序的fcntl函数以及ioctl函数。在 kernel 3.0 中已经完全删除了 struct file_operations 中的 ioctl 函数指针。
  • open: 设备驱动第一个被执行的函数,一般用于硬件的初始化。
  • release: 当file结构体被释放时,将会调用该函数。与open函数相反,该函数可以用于释放
 
我们提到read和write函数时,需要使用copy_to_user函数以及copy_from_user函数来进行数据访问,写入/读取成 功函数返回0,失败则会返回未被拷贝的字节数。
 
file结构体
每打开一个文件,内核会创建一个结构体,内核中用file结构体来表示每个打开的文件。
文件的操作函数是该结构体的成员变量 f_op
  • f_op:存放与文件操作相关的一系列函数指针,如open、read、wirte等函数。
  • private_data:该指针变量只会用于设备驱动程序中,内核并不会对该成员进行操作。因此,在驱动程序中,通常用于指向描述设备的结构体。
inode结构体
包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。
一个文件描述符(file文件结构)表示一个已经打开的设备,多个文件描述符可以表示同个文件多个打开实例。但此时这些file文件全部只能指向同一个inode结构体。
  • dev_t i_rdev 表示设备文件的结点,这个域实际上包含了设备号。
  • struct cdev *i_cdev struct cdev是内核的一个内部结构,它是用来表示字符设备的,当inode结点指向一个字符设备文件时,此域为一个指向inode结构的指针。
 
驱动编程 - GPIO
上一篇
Linux驱动基础02-第一个内核模块
下一篇
Linux驱动基础04-总线设备驱动模型

Comments
Loading...