правильно закрыть файл структуры * в модуле - PullRequest
0 голосов
/ 26 октября 2018

Я учусь писать драйвер для 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_data файла 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;
}

В функции записи я перебираю глобальный связанный список, чтобы задать для поля struct 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;
}

У меня есть три загадки с кодом

  1. Когда вызывается module_exit, я должен ждать, пока все struct file * будут закрыты правильно, уместно ли использовать wait_event?
  2. Когда этот модуль ядра будет удален с помощью rmmod, у меня нет возможности закрыть (отсоединиться?) struct file *, поэтому будет блокироваться команда rmmod, пока эти файлы не будут закрыты программой; есть ли лучший способ справиться с этим?
  3. При повторении всех struct file *, есть ли способ использовать API ядра вместо управления моим собственным связанным списком?

hello.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");

1 Ответ

0 голосов
/ 27 октября 2018

Это не правильный способ сделать это.На самом деле, правильный путь намного проще для вас.Инфраструктура сборки ядра в сочетании с загрузчиком времени выполнения ядра создаст struct module для вашего модуля (называемого "THIS_MODULE").Вам нужно поместить указатель на него в слот .owner структуры file_operations.Это легко сделать:

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_get и module_put, поэтому многие примеры не выполняются.Не могу объяснить, как это работает.Механизм get / put имел расы: модуль действительно не может надежно подсчитывать ссылки сам.

Если вы сделаете это, вам не нужно об этом беспокоиться: когда вызывается ваш module_exit, вы можете быть уверены, что нет открытых экземпляров устройства, которые принадлежат вашему модулю.

...