05-自动生成设备节点的字符设备驱动

在前面的04-最简单的字符设备驱动中,如果要通过设备节点控制设备,还需要我们手动执行sudo mknode /dev/mychardev c 237,对于我们开发人员可能顶多是麻烦一点,敲下命令,但是如果是一个普通使用者,他要使用打印机,难道还要他敲命令?当然不是,linux有一套机制可以自动创建设备节点,称为udev + devtmpfs + sysfs的机制,稍后详细介绍它。

自动生成设备节点

测试代码

自动生成设备节点的驱动代码如下:

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

#define DEVICE_NAME "mychardev"
static int major;
static struct class *mychardev_class;
static struct device *mychardev_device;

static struct file_operations fops = {
    .owner = THIS_MODULE,
};

static int __init mychardev_drv_init(void)
{
    major = register_chrdev(0, DEVICE_NAME, &fops);
    //创建设备所属类
    mychardev_class = class_create(DEVICE_NAME);
    //创建设备,这一步是自动生成设备节点的关键
    mychardev_device = device_create(mychardev_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);
    return 0;
}

static void __exit mychardev_drv_exit(void)
{
    // 模块卸载时,相应的删除设备节点,清理设备所属类
    device_destroy(mychardev_class, MKDEV(major, 0));
    class_destroy(mychardev_class);
    unregister_chrdev(major, DEVICE_NAME);
}

module_init(mychardev_drv_init);
module_exit(mychardev_drv_exit);
MODULE_LICENSE("GPL");

代码中我删除了错误处理的相关代码,因为它对我们了解机制没有任何用处,只会起到干扰作用,不过正式的代码千万别这样搞,毕竟稳定才是关键。

编译,insmod内核模块后,可以在/dev下看到设备节点/dev/mychardev已经生成了:

img

并且在/sys/class/sys/devices/virtual目录下都生成的相关的目录和文件:

img

原理机制

文章开头提到了,这套机制是通过udev + devtmpfs + sysfs实现的。udev是一个用户空间的守护程序,像在我的开发电脑上它叫/usr/lib/systemd/systemd-udevd。它通过工作于netlink之上的uevent来传递设备的插、拔等状态的改变,由udev在用户空间接收uevent消息,依据提前配置好的策略,实现设备节点的创建,权限设置等操作。netlinkueventudev三者的关系我觉得类似于sockethttpweb浏览器

  • netlink

    • netlink 是 Linux 内核提供的一种用于用户空间进程和内核空间进程之间进行通信的机制。它是一种基于消息的通信方式,允许用户态程序与内核态模块高效地交换数据。实际就是套接字的一种,不过用在特定的地方。
  • uevent

    • uevent主要用于在内核与用户空间之间传递设备(如热插拔事件)、模块、固件等相关的状态变更通知,它工作于netlink之上,可以理解为一套通讯协议。它的格式通常也比较简单,是以键值对表示,比如:
    ACTION=add
    DEVPATH=/devices/virtual/mychardev/mychardev
    SUBSYSTEM=mychardev
    DEVNAME=/dev/mychardev
    SEQNUM=5580
    MAJOR=507
    MINOR=0
    
  • udev
    uevent通过netlink广播至用户空间,被udev接收处理。比如上面的测试代码的驱动模块被insmod时,mychardev_drv_init中调用了device_create,在device_create的调用栈中,会调用到kobject_uevent(&dev->kobj, KOBJ_ADD);一句这样的代码,于是它就通过netlink发出上面那个uevent的消息,udev就按照默认的处理创建了设备节点。如果你想改变这种行为,可以在/etc/udev/rules.d目录下,创建你自己的匹配规则,比如修改设备节点的访问权限(使用过usb转串口的大概率都有这种需求,想改变设备节点的权限,让普通用户也可以读写串口。)

讲到这里好像还没提及devtmpfssysfs。实际uevent这套机制在内核侧的实现就是sysfs背后的内核设备模型实现的,sysfs是一个伪文件系统,它是内核设备模型的具体呈现,具体表现为/sys目录下的目录层次结构,/sys目录挂载的类型就是sysfs/dev目录的挂载类型就是devtmpfs
img

---未完

posted @ 2025-11-29 17:24  thammer  阅读(4)  评论(0)    收藏  举报