设备节点是用来进行设备访问的文件,创建在/dev,类似于硬盘的inode。中文解释比较难懂,我找了一下英文解释
Device nodes (often referred to simply as “devices” in casual conversation) are the files that the kernel, applications, and even command-line tools use when they need to access the hardware. You can think of the device node (or file) as providing an interface similar to a telephone jack.
一、手动创建设备节点
调用mknod NAME TYPE MAJOR MINOR
NAME: 要创建的节点名称
TYPE: b 表示块设备, c 表示字符设备, p 表示管道
MAJOR: 要链接设备的主设备号 MINOR: 要链接设备的从设备号
如 mknod /dev/device1 c 236 0
不过一般不用手动模式。
二、自动创建设备节点
自动创建设备节点是利用 udev 机制来实现的。 udev 通过检测系统中硬件设备状态, 可以根据系统中硬件设备状态来创建或者删除设备文件。 自动创建设备节点需要在驱动中首先使用class_create()函数创建一个类, 创建的类位于于/sys/class/ 目录下, 之后使用 device_create()函数在这个类下创建相应的设备, 在加载驱动模块时, 用户空间中的 udev 会自动响应根据并/sys/class/ 下的信息创建设备节点
class_create()函数
定义在/include/linux/device.h中,是一个宏定义函数
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
函数作用:
用于动态创建设备类。 参数含义: owner: struct module 结构体类型的指针。 一般赋值为 THIS_MODULE。name: char 类型的指针, 代表即将创建的 struct class 变量的名字。返回值: struct class * 类型的结构体。
static struct lock_class_key __key定义了一个静态的锁类键,并将其地址传递给__class_create函数。这种设计的主要目的是确保每个类拥有独立的锁,避免多个类实例共享同一个锁导致竞争条件。
device_create()
struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, void *drvdata,
const char *fmt, ...);
函数作用:
用来在 class 类下创建一个设备文件。 参数含义: cls: 指定所要创建的设备所从属的类。
parent:指定该设备的父设备, 如果没有就指定为 NULL。
devt:指定创建设备的设备号。
drvdata:被添加到该设备回调的数据, 没有则指定为 NULL。
fmt: 添加到系统的设备节点名称。返回值: struct device * 类型结构体
device_destory()
extern void device_destroy(struct class *cls, dev_t devt);
函数作用:
用来删除 class 类中的设备。 参数含义: cls: 指定所要创建的设备所从属的类。devt:指定创建设备的设备号。返回值: 无
三、节点创建代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
static dev_t dev_num;//定义 dev_t 类型变量 dev_num 来表示设备号
struct cdev cdev_test;//定义 struct cdev 类型结构体变量 cdev_test, 表示要注册的字符设备
struct file_operations cdev_fops_test = {
.owner = THIS_MODULE,//将 owner 字段指向本模块, 可以避免在模块的操作正在被使用时卸载该模块
};//定义 file_operations 结构体类型的变量 cdev_test_ops
struct class *class_test;//定于 struct class *类型结构体变量 class_test, 表示要创建的类
static int __init chrdev_fops_init(void)//驱动入口函数
{
int ret;//定义 int 类型的变量 ret, 用来对函数返回值进行判断
int major,minor;//定义 int 类型的主设备号 major 和次设备号 minor
ret = alloc_chrdev_region(&dev_num,0,1,"chrdev_name");//自动获取设备号, 设备名 chrdev_name
if (ret < 0){
printk("alloc_chrdev_region is error \n");
}
printk("alloc_chrdev_region is ok \n");
major = MAJOR(dev_num);//使用 MAJOR()函数获取主设备号
minor = MINOR(dev_num);//使用 MINOR()函数获取次设备号
printk("major is %d\n minor is %d \n",major,minor);
cdev_init(&cdev_test,&cdev_fops_test);//使用 cdev_init()函数初始化 cdev_test 结构体, 并链接到
cdev_test_ops 结构体
cdev_test.owner = THIS_MODULE;//将 owner 字段指向本模块, 可以避免在模块的操作正在被使用时卸载该模块
ret = cdev_add(&cdev_test,dev_num,1); //使用 cdev_add()函数进行字符设备的添加
if (ret < 0){
printk("cdev_add is error \n");
}
printk("cdev_add is ok \n");
class_test = class_create(THIS_MODULE,"class_test");//使用 class_create 进行类的创建, 类名称为class_test
device_create(class_test,NULL,dev_num,NULL,"device_test");//使用 device_create 进行设备的创建,设备名称为 device_test
return 0;
}
static void __exit chrdev_fops_exit(void)//驱动出口函数
{
cdev_del(&cdev_test);//删除添加的字符设备 cdev_test
unregister_chrdev_region(dev_num,1);//释放字符设备所申请的设备号
device_destroy(class_test,dev_num);//删除创建的设备
class_destroy(class_test);//删除创建的类
printk("module exit \n");
} m
odule_init(chrdev_fops_init);//注册入口函数
module_exit(chrdev_fops_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//同意 GPL 开源协议
MODULE_AUTHOR("topeet");//作者信息
四、字符设备和类的关系
设备类与字符设备通过device_create()关联。调用时需指定所属的类和设备号,内核会在/dev/和/sys/class/中生成对应节点。注意到device_create()函数中传入的是一个class,和设备号,设备号代表了一个设备实例。
典型流程是
- 调用
class_create()创建设备类(如/sys/class/my_device_class)。 - 调用
alloc_chrdev_region()或register_chrdev_region()分配设备号。 - 初始化
cdev并注册到内核(cdev_add())。 - 调用
device_create()将字符设备绑定到类,生成设备节点(如/dev/mydev)
驱动代码中需要保证第四步必须在最后,至于类的创建函数通常会放在设备注册后,但是其变量会先定义。
656

被折叠的 条评论
为什么被折叠?



