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 cdevstatic @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_initcdev_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_initcdev_alloc 用例的答案。

以上是关于cdev_alloc() 与 cdev_init()的主要内容,如果未能解决你的问题,请参考以下文章

驱动开发 —— 高级驱动学习方法

T8 高级驱动学习方法

linux设备注册

第七章 I/O

Android深度探索--HAL与驱动开发----第七章读书笔记

05 字符设备的API