C 在模块退出时正确关闭结构文件*

C 在模块退出时正确关闭结构文件*,c,linux,linux-kernel,linux-device-driver,C,Linux,Linux Kernel,Linux Device Driver,我正在学习如何编写linux驱动程序,但对于如何正确优雅地使用内核API,我有些困惑。 我尝试编写一个简单的misc驱动程序,它在/dev/hello上创建一个设备节点。在用户空间中,读卡器可以在设备上读取数据并阻塞,直到写卡器在设备上写入一些数据,所有读卡器都可以写入数据。如果在读卡器读取旧数据之前出现新数据,则读卡器将丢失旧数据 为了实现它,我为每个打开的文件创建了一个struct hello\u file\u data,并将它们放在一个全局链接列表中,数据字段表示有一些数据可以读取 sta

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

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

static LIST_HEAD(hello_opened_file_list);

struct hello_file_data {
    struct list_head    entry;
    struct file *       owner_file;
    int                 data;
};
在read函数中,我使用
wait\u event\u interruptable
阻塞该线程,并等待文件的
struct hello\u file\u data
字段变为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;
}
在write函数中,我迭代全局链表,将每个文件的
结构hello_file_data
数据字段设置为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;
}
我在密码上有三个谜题

  • 调用模块退出时,我必须等待所有
    struct file*
    正确关闭,是否使用wait\u事件
  • 当rmmod将删除这个内核模块时,我无法关闭(分离?
    struct file*
    ),因此rmmod命令将阻塞,直到程序关闭这些文件;有没有更好的办法处理
  • 当迭代所有
    结构文件*
    时,有没有一种方法可以使用内核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");
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    静态列表\u头(hello\u打开的\u文件\u列表);
    静态定义\u自旋锁(hello\u列表\u锁);
    静态声明队列头(hello\u WAIT\u数据);
    静态声明队列头(hello\u WAIT\u all\u file\u close);
    静态字符hello_缓冲区[1024];
    静态大小\u t hello\u bufsize=0;
    静态int hello_dead=0;
    结构hello\u文件\u数据{
    结构列表\头条目;
    结构文件*所有者文件;
    int数据;
    };
    静态int hello_open(结构inode*inode,结构文件*file)
    {
    结构hello_file_data*fdat;
    fdat=kzalloc(sizeof(结构hello_文件_数据),GFP_内核);
    如果(!fdat){
    return-ENOMEM;
    }
    fdat->owner\u file=文件;
    fdat->data=0;
    文件->私有数据=fdat;
    旋转锁定(&hello\u列表锁定);
    列表\u添加(&fdat->entry,&hello\u打开的文件\u列表);
    旋转解锁(&hello\u列表锁定);
    返回0;
    }
    静态int hello_发布(struct inode*inode,struct file*file)
    {
    struct hello\u file\u data*fdat=file->private\u data;
    int notify_module_exit=0;
    旋转锁定(&hello\u列表锁定);
    列表删除(&fdat->条目);
    if(hello_dead&&list_empty(&hello_opened_file_list)){
    通知_模块_退出=1;
    }
    旋转解锁(&hello\u列表锁定);
    file->private_data=NULL;
    kfree(fdat);
    如果(通知模块退出){
    唤醒(&hello\u wait\u all\u file\u close);
    }
    返回0;
    }
    静态ssize\t hello\u read(结构文件*文件,字符*用户*数据,大小\u t n,loff\u t*偏移量)
    {
    国际关系;
    struct hello\u file\u data*fdat=file->private\u data;
    res=等待事件可中断(hello_wait_数据,hello_dead | fdat->data);
    如果(res){
    返回res;
    }
    如果(你好,你死了){
    return-ENODEV;
    }
    n=min(hello_bufsize,n);
    if(将_复制到_用户(数据,hello_缓冲区,n)){
    返回-默认值;
    }
    fdat->data=0;
    返回n;
    }
    静态ssize\u t hello\u write(结构文件*文件,常量字符*用户*数据,大小\u t n,loff\u t*偏移量)
    {
    struct hello\u file\u data*fdat=file->private\u data;
    如果(!n){
    返回0;
    }
    n=min(sizeof(hello\u缓冲区),n);
    if(从用户复制用户(hello\u缓冲区,数据,n)){
    返回-默认值;
    }
    hello_bufsize=n;
    旋转锁定(&hello\u列表锁定);
    每个条目的列表(fdat和hello已打开的文件列表,条目){
    fdat->data=1;
    }
    旋转解锁(&hello\u列表锁定);
    唤醒可中断(&hello\u wait\u数据);
    返回n;
    }
    静态结构文件\u操作hello\u fops={
    .open=hello\u open,
    .read=hello\u read,
    .write=hello\u write,
    .release=hello\u release,
    };
    静态结构miscdevice hellodev={
    .minor=杂项动力minor,
    .name=“你好”,
    .fops=&hello\u fops,
    };
    静态int hello_模块_init(void)
    {
    返回杂项寄存器(&hellodev);
    }
    静态void hello\u模块\u退出(void)
    {
    杂项注销登记(&hellodev);
    hello_dead=1;
    唤醒可中断(&hello\u wait\u数据);
    等待事件(hello\u wait\u all\u file\u close({
    int空;
    旋转锁定(&hello\u列表锁定);
    empty=列表\u empty(&hello\u打开的文件\u列表);
    旋转解锁(&hello\u列表锁定);
    空的;
    }));
    }
    模块_init(hello_模块_init);
    模块退出(你好模块退出);
    模块许可证(“GPL”);
    模块作者(“xfan”);
    模块描述(“这是测试驱动程序”);
    
    这不是正确的方法。事实上,正确的方法对你来说要简单得多。内核构建框架和内核运行时加载程序将为您的模块构建一个结构模块(称为
    “THIS_module”
    )。您需要在
    文件\u操作
    结构的
    .owner
    槽中放置指向它的指针。这很容易做到:

    static struct file_operations hello_fops = {
        .owner      = THIS_MODULE,        // <<<<<<========
        .open       = hello_open,
        .read       = hello_read,
        .write      = hello_write,
        .release    = hello_release,
    };
    
    静态结构文件\u操作hello\u fops={
    
    .owner=此_模块,//非常感谢,是否有方法从misc设备实例获取所有打开的文件?