cdev_alloc() 与 cdev_init()
Posted
技术标签:
【中文标题】cdev_alloc() 与 cdev_init()【英文标题】:cdev_alloc() vs cdev_init() 【发布时间】:2021-12-28 10:33:07 【问题描述】:在 Linux 内核模块中,在创建 struct cdev
时可以采用两种不同的方法,如 this site 和 this answer 中所建议的那样:
第一种方法,cdev_alloc()
struct cdev *my_dev;
...
static int __init example_module_init(void)
...
my_dev = cdev_alloc();
if (my_dev != NULL)
my_dev->ops = &my_fops; /* The file_operations structure */
my_dev->owner = THIS_MODULE;
else
...
第二种方法,cdev_init()
static struct cdev my_cdev;
...
static int __init example_module_init(void)
...
cdev_init(&my_cdev, my_fops);
my_cdev.owner = THIS_MODULE;
...
(假设my_fops
是一个指向已初始化struct file_operations
的指针)。
-
第一种方法是否已弃用或仍在使用?
cdev_init()
是否也可以在第一种方法中与cdev_alloc()
一起使用?如果没有,为什么?
第二个问题也在linked answer的评论中。
【问题讨论】:
如果您在由cdev_alloc()
分配的struct cdev
上调用cdev_init()
,您将在内核日志上打印出令人讨厌的错误消息。 cdev_put()
也不会释放cdev_alloc()
分配的内存。
@IanAbbott 好的,但是为什么我会收到该错误消息?在使用cdev_alloc
创建的结构上无法完成的cdev_init
是什么?那么,也许你的意思不是cdev_put
,而是cdev_del
。
@BowPark:编辑后,在您的第一个 sn-p 中,cdev_alloc
似乎在任何函数的外部调用。这在 C 语言中是被禁止的(与 C++ 不同)。在example_module_init
内部调用cdev_alloc
看起来更合理。
您会收到错误消息,告诉您您做错了什么。 cdev_alloc()
已经部分初始化了struct cdev
(但没有像cdev_init()
那样设置文件操作)。在同一个struct cdev
上调用cdev_init()
会破坏cdev_alloc()
精心设置的一些东西。
是的,我的意思是 cdev_del()
不是 cdev_put()
(cdev_put()
和 cdev_get()
存在,但仅用于“fs/char_dev.c”,不会导出。)
【参考方案1】:
他们做不同的事情。偏好通常是 - 在不需要时不使用动态分配,并尽可能在堆栈上分配。
cdev_alloc()
动态分配my_dev
,所以cdev_del()
时会调用kfree(pointer)
。
cdev_init()
不会释放指针。
最重要的是,my_cdev
结构的生命周期不同。在 cdev_init()
情况下,struct cdev my_cdev
绑定到包含词法范围,而 cdev_alloc()
返回动态分配指针,直到 free-d 为止有效。
【讨论】:
"并尽可能在堆栈上分配。" - 我很难想象 cdev 结构会被分配到堆栈上。字符设备在一个函数中创建(例如 init)并在 另一个 中销毁(例如 exit);在调用这些函数之间,用户可以访问设备。所以,如果不是动态分配的,cdev 结构通常是一个全局变量或者一个静态变量。 @Tsyvarev 感谢您的考虑。我编辑了这个问题以给出一个更精确的例子。事实上,在第二种情况下,struct cdev
是static
。
@KamilCuk IIUC,最好使用cdev_init
。如果我只有一个设备,如果它的内存区域没有用cdev_del
释放真的有问题吗?您没有解决第二个问题:cdev_init()
也可以与cdev_alloc()
一起使用吗?如果不是,为什么?【参考方案2】:
cdev_init()
也可以用在第一种方法中吗,cdev_alloc()
?
不,cdev_init
不应用于分配有cdev_alloc
的字符设备。
在某种程度上,cdev_alloc
等价于kmalloc
加上cdev_init
。因此,为使用cdev_alloc
创建的字符设备调用cdev_init
毫无意义。
此外,分配有cdev_alloc
的字符设备包含一个提示,当不再使用该设备时应该解除分配。为该设备调用cdev_init
将清除该提示,因此您将得到内存泄漏。
cdev_init
和 cdev_alloc
之间的选择取决于您希望字符设备具有的使用寿命。
通常,人们希望字符设备的生命周期与 模块 的生命周期相同。在这种情况下:
-
定义
struct cdev
类型的静态或全局变量。
使用cdev_init
在模块的init函数中创建字符设备。
使用cdev_del
在模块的exit函数中销毁字符设备。
确保字符设备的文件操作将.owner
字段设置为THIS_MODULE
。
在复杂的情况下,希望在模块初始化后的特定点创建字符设备。例如。模块可以为某些硬件提供驱动程序,并且字符设备应该与该硬件绑定。在这种情况下,无法在模块的 init 函数中创建字符设备(因为尚未检测到硬件),更重要的是,无法在模块的 exit 函数中销毁字符设备。在这种情况下:
-
在结构中定义一个字段,描述硬件,指针类型为
struct cdev*
。
在创建(探测)硬件的函数中使用cdev_alloc
创建字符设备。
在销毁(断开)硬件的函数中使用cdev_del
销毁字符设备。
在第一种情况下,cdev_del
被调用,此时字符设备用户未使用。这个保证是由THIS_MODULE
在文件操作中提供的:如果一个字符设备对应的文件被用户打开,一个模块是不能被卸载的。
在第二种情况下,没有这样的保证(因为cdev_del
在模块的退出函数中被调用)。因此,当cdev_del
返回时,字符设备仍可被用户使用。这里cdev_alloc
真的很重要:字符设备的deallocation 将被deferred 直到用户关闭与字符设备关联的所有文件描述符。没有cdev_alloc
就无法获得这样的行为。
【讨论】:
这很清楚,谢谢,但现在我对什么是最好的方法感到困惑。我的情况很简单,只有一个设备,一个次要号码。cdev_alloc
(以及动态分配)更可取吗?
我已经更新了关于cdev_init
和cdev_alloc
用例的答案。以上是关于cdev_alloc() 与 cdev_init()的主要内容,如果未能解决你的问题,请参考以下文章