我刚刚开始探索Linux字符设备驱动程序。我做了一个简单的内核模块,我用register_chrdev()函数注册设备。我已经传递了0作为函数的参数,内核返回给我可用的自由主号。之后,我使用mknod命令创建一个字符设备文件与返回的主要号码,我成功地做到了这一点。我已经将驱动程序加载到内核中,并且驱动程序、设备文件和用户空间应用程序之间的通信正常。
问题是,当我重新启动系统时,字符设备文件(使用mknod创建)不在/dev目录中。
所以请建议解决这个问题,这样我的字符设备文件将出现在/dev目录,即使重新启动后。
一个解决方案是让驱动程序动态地创建/dev
中的文件,而不是使用mknod
命令创建它们。基本思想是通过调用class_create
从模块初始化函数创建自定义设备类,然后通过调用device_create
向该类添加设备。
您需要一个struct class *
类型的变量来保存指向自定义类的指针。该变量需要由模块中的各种函数访问,因此需要在任何函数之外声明,并且通常会像这样声明static
:
static struct class *foo_class;
你的模块init函数需要创建类并检查错误:
foo_class = class_create(THIS_MODULE, "foo");
if (IS_ERR(foo_class)) {
/* Failed to create class. */
rc = PTR_ERR(foo_class);
goto fail_class_create;
}
(在这里,goto fail_class_create
跳转到一个标签,在返回错误之前清理到目前为止所做的一切。如果你不喜欢这种"on error goto"模式,可以在返回错误之前在这里显式地清理。
如果class_create
函数成功,当模块退出函数中不再需要它时,它应该被销毁,并且如果在模块初始化函数中有进一步的错误,也可以作为清理的一部分:
class_destroy(foo_class);
创建类时,您可以创建(和销毁)属于该类的设备(我称之为"类设备"),通过调用device_create
来创建设备,调用device_destroy
来销毁设备。这两个函数都使用设备节点号(主设备号和副设备号的组合)来指定要创建或销毁的类设备。例如,可以这样创建一个类设备:
struct device *csdev;
/* ... */
csdev = device_create(foo_class, hwdev, MKDEV(foo_major, minor), privdata, "foo%u", minor);
if (IS_ERR(csdev)) {
/* Failed to create device. */
rc = PTR_ERR(csdev);
/* Do any clean-up here. */
}
(这里,foo_class
指向前面创建的自定义类;hwdev
指向底层"硬件设备",如果没有底层硬件设备,可以设置为NULL
;foo_major
是你的主要设备号(由register_chrdev
分配,minor
是你想要创建的设备的次要设备号,privdata
是一个私有数据指针,通常指向你的设备的一些私有数据结构,但它可以是NULL
;其余参数由printf样式的格式字符串加上格式字符串创建设备名称所需的任何额外参数组成。
在上面的例子中,如果minor
为0,设备将被动态创建为/dev/foo0
。
销毁设备,调用device_destroy
如下:
device_destroy(foo_class, MKDEV(foo_major, minor));
(此处,foo_class
、foo_major
和minor
与传递给device_create
的相同)
以上函数仅作为GPL导出,因此如果您想使用它们,您的模块将需要使用以下声明来声明其许可证:
MODULE_LICENSE("GPL");