gpt4 book ai didi

在 module_exit 上正确关闭结构文件 *

转载 作者:IT王子 更新时间:2023-10-29 01:25:26 31 4
gpt4 key购买 nike

我正在学习如何编写 linux 驱动程序,但我对如何正确优雅地使用内核 API 感到有些困惑。我尝试编写一个简单的 misc 驱动程序,它在/dev/hello 上创建一个设备节点。在用户空间中,读者可以在设备上读取并阻塞,直到写者在设备上写入一些数据,所有读者都获得了写入的数据。如果在读取器读取旧数据之前有新数据到来,则读取器将丢失旧数据。

为了实现它,我为每个打开的文件创建一个struct hello_file_data,并将它们放在一个全局链接列表中,数据字段指示有一些数据可以读取。

static LIST_HEAD(hello_opened_file_list);

struct hello_file_data {
struct list_head entry;
struct file * owner_file;
int data;
};

在读取函数中,我使用wait_event_interruptible来阻塞这个线程,等待文件的struct hello_file_datadata字段变为1。

static ssize_t hello_read(struct file * file, char __user * data, size_t n, loff_t * offset_p)
{
int res;
struct hello_file_data * fdat = file->private_data;

res = wait_event_interruptible(hello_wait_data, hello_dead || fdat->data);
if (res) {
return res;
}
if (hello_dead) {
return -ENODEV;
}
n = min(hello_bufsize, n);
if (copy_to_user(data, hello_buffer, n)) {
return -EFAULT;
}
fdat->data = 0;
return n;
}

在写入函数中,我迭代全局链表以将每个文件的struct hello_file_datadata 字段设置为1,然后通知所有读取线程唤醒。

static ssize_t hello_write(struct file * file, const char __user * data, size_t n, loff_t * offset_p)
{
struct hello_file_data * fdat = file->private_data;

if (!n) {
return 0;
}
n = min(sizeof(hello_buffer), n);
if (copy_from_user(hello_buffer, data, n)) {
return -EFAULT;
}
hello_bufsize = n;
spin_lock(&hello_list_lock);
list_for_each_entry(fdat, &hello_opened_file_list, entry) {
fdat->data = 1;
}
spin_unlock(&hello_list_lock);
wake_up_interruptible(&hello_wait_data);
return n;
}

我对代码有3个困惑

  1. 当调用module_exit时,我必须等到所有struct file *都正确关闭,使用wait_event合适吗?
  2. 当这个内核模块将被 rmmod 删除时,我无法关闭(分离?)struct file *,所以 rmmod 命令将阻塞直到这些文件被程序关闭;有更好的处理方法吗?
  3. 在迭代所有 struct file * 时,有没有办法使用内核 API 而不是管理我自己的链表?

你好.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>

static LIST_HEAD(hello_opened_file_list);
static DEFINE_SPINLOCK(hello_list_lock);
static DECLARE_WAIT_QUEUE_HEAD(hello_wait_data);
static DECLARE_WAIT_QUEUE_HEAD(hello_wait_all_file_close);

static char hello_buffer[1024];
static size_t hello_bufsize = 0;
static int hello_dead = 0;

struct hello_file_data {
struct list_head entry;
struct file * owner_file;
int data;
};

static int hello_open(struct inode * inode, struct file * file)
{
struct hello_file_data * fdat;

fdat = kzalloc(sizeof(struct hello_file_data), GFP_KERNEL);
if (!fdat) {
return -ENOMEM;
}
fdat->owner_file = file;
fdat->data = 0;
file->private_data = fdat;
spin_lock(&hello_list_lock);
list_add(&fdat->entry, &hello_opened_file_list);
spin_unlock(&hello_list_lock);
return 0;
}

static int hello_release(struct inode * inode, struct file * file)
{
struct hello_file_data * fdat = file->private_data;
int notify_module_exit = 0;

spin_lock(&hello_list_lock);
list_del(&fdat->entry);
if (hello_dead && list_empty(&hello_opened_file_list)) {
notify_module_exit = 1;
}
spin_unlock(&hello_list_lock);
file->private_data = NULL;
kfree(fdat);

if (notify_module_exit) {
wake_up(&hello_wait_all_file_close);
}
return 0;
}

static ssize_t hello_read(struct file * file, char __user * data, size_t n, loff_t * offset_p)
{
int res;
struct hello_file_data * fdat = file->private_data;

res = wait_event_interruptible(hello_wait_data, hello_dead || fdat->data);
if (res) {
return res;
}
if (hello_dead) {
return -ENODEV;
}
n = min(hello_bufsize, n);
if (copy_to_user(data, hello_buffer, n)) {
return -EFAULT;
}
fdat->data = 0;
return n;
}

static ssize_t hello_write(struct file * file, const char __user * data, size_t n, loff_t * offset_p)
{
struct hello_file_data * fdat = file->private_data;

if (!n) {
return 0;
}
n = min(sizeof(hello_buffer), n);
if (copy_from_user(hello_buffer, data, n)) {
return -EFAULT;
}
hello_bufsize = n;
spin_lock(&hello_list_lock);
list_for_each_entry(fdat, &hello_opened_file_list, entry) {
fdat->data = 1;
}
spin_unlock(&hello_list_lock);
wake_up_interruptible(&hello_wait_data);
return n;
}

static struct file_operations hello_fops = {
.open = hello_open,
.read = hello_read,
.write = hello_write,
.release = hello_release,
};

static struct miscdevice hellodev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "hello",
.fops = &hello_fops,
};

static int hello_module_init(void)
{
return misc_register(&hellodev);
}

static void hello_module_exit(void)
{
misc_deregister(&hellodev);
hello_dead = 1;
wake_up_interruptible(&hello_wait_data);
wait_event(hello_wait_all_file_close, ({
int empty;
spin_lock(&hello_list_lock);
empty = list_empty(&hello_opened_file_list);
spin_unlock(&hello_list_lock);
empty;
}));
}

module_init(hello_module_init);
module_exit(hello_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xfan");
MODULE_DESCRIPTION("This is test driver");

最佳答案

这不是正确的做法。事实上,正确的方法对你来说要简单得多。内核构建框架与内核运行时加载器一起将为您的模块构建一个struct module(称为"THIS_MODULE")。您需要在 file_operations 结构的 .owner 槽中放置一个指向它的指针。这很容易做到:

static struct file_operations hello_fops = {
.owner = THIS_MODULE, // <<<<<<========
.open = hello_open,
.read = hello_read,
.write = hello_write,
.release = hello_release,
};

它的工作方式是内核跟踪所有属于您的模块的打开文件(通过 owner 成员)。也就是说,每当打开设备实例时,它都会增加与您的模块关联的引用计数。当设备实例关闭时,您的 hello_release 被调用,然后模块引用计数递减。内核不允许您的模块在它仍然拥有打开的文件时被卸载,因此您需要在卸载您的模块之前追踪并终止任何持有文件引用的用户进程。这是可靠地执行此操作的唯一方法(听起来这就是您想要的)。

不幸的是,许多内核模块/驱动程序示例是在过去创建的,当时模块必须使用 try_module_getmodule_put 进行自己的引用计数,所以很多样本都没有解释这是如何工作的。尽管 get/put 机制存在竞争:模块本身确实不能可靠地引用计数。

如果你这样做,那么你就不必担心:当你的 module_exit 被调用时,你可以确信没有属于你的模块的打开的设备实例。

关于在 module_exit 上正确关闭结构文件 *,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53012935/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com