Как создать узел устройства из кода init_module модуля ядра Linux?

Я пишу модуль для ядра Linux и хочу создать некоторые узлы устройств в функции инициализации.

int init_module(void)
{
    Major = register_chrdev(0, DEVICE_NAME, &fops);
 // Now I want to create device nodes with the returned major number
}

Я также хочу, чтобы ядро ​​присвоило моему первому узлу младший номер, а затем я сам назначу второстепенные номера другим узлам.

Как я могу сделать это в коде. Я не хочу создавать устройства из оболочки с помощью mknod


person Alptugay    schedule 11.05.2011    source источник


Ответы (3)


Чтобы иметь больший контроль над номерами устройств и созданием устройств, вы можете выполнить следующие шаги (вместо register_chrdev()):

  1. Позвоните alloc_chrdev_region(), чтобы получить основной номер и диапазон младших номеров для работы.
  2. Создайте класс устройств для своих устройств с помощью class_create().
  3. Для каждого устройства вызовите cdev_init() и cdev_add(), чтобы добавить символьное устройство в систему.
  4. Для каждого устройства вызовите device_create(). В результате, среди прочего, Udev создаст узлы устройств для ваших устройств. Нет необходимости в mknod или подобном. device_create() также позволяет управлять именами устройств.

Вероятно, в Сети есть много таких примеров, один из они здесь.

person Eugene    schedule 12.05.2011
comment
Извините, что выкопал это из прошлого, но есть ли эквивалентный способ сделать это, когда лицензия не GPL? class_create нельзя использовать с лицензиями, отличными от GPL. - person Piotr; 28.05.2014
comment
@Piotr: на самом деле я не знаю, существует ли он. - person Eugene; 29.05.2014
comment
Выглядит неплохо. Спасибо, что поделился. Вопрос: как очистить зарегистрированные устройства и файлы в /dev/my_dev_files? - person Nikita Vorontsov; 21.06.2014
comment
@Nikita Vorontsov, выполняются следующие операции очистки: device_destroy (он также удаляет узел устройства), cdev_del отменяет регистрацию устройства в ядре. После удаления каждого устройства вызывается class_destroy для удаления класса, а затем - unregister_chrdev_region. То, что делается при создании устройств, как обычно, отменяется в обратном порядке. - person Eugene; 22.06.2014
comment
@Eugene - отличный ответ !! Спасибо. У меня есть один вопрос: разве не следует вызывать device_create() перед вызовом cdev_add()? - person Guy Avraham; 24.10.2017
comment
Я думаю, что device_create() следует вызывать после cdev_add(). cdev_add() подготавливает структуры ядра для обслуживания устройства, а device_create() среди прочего делает устройство доступным для пользовательского пространства. И как только вы сделаете свое устройство доступным для пользовательского пространства, это устройство должно быть готово обрабатывать оттуда запросы. - person Eugene; 24.10.2017
comment
@Евгений Спасибо за ответ. Я следую за драйверами устройств Linux, 3-е издание O'Reilly - и до этого этапа они не упоминают метод device_create, но они вводят cdev_add, и они упоминают то же самое, что вы упоминаете, только для метода cdev_add . Я предполагаю, что позже, когда они будут вызывать device_create в своих примерах, они будут вызывать их обоих в указанном вами порядке. Спасибо за разъяснение этого замечания (см. конец раздела 3.4, чтобы понять мою точку зрения: makelinux.net/ldd3/chp-3-sect-4). Большое спасибо !! - person Guy Avraham; 24.10.2017
comment
@Eugene Кстати, в приведенном здесь примере stackoverflow.com/a/41327360/1971003 cdev_add() также вызывается после device_create (). - person Guy Avraham; 24.10.2017
comment
LDD3 великолепен, но да, он немного устарел. Раньше файлы устройств можно было создавать с помощью mknod. device_create() станет доступным позже. Что касается других примеров, где cdev_add() вызывается после device_create() - ну, я не знаю, правильно ли это делать, и предпочитаю перестраховываться. Можно попытаться создать такой примерный модуль и попытаться загрузить его, многократно пытаясь получить доступ к устройствам, которые он создает одновременно. Если ядро ​​не имеет защиты w.r.t. таких случаях, это может привести к сбою. Или, может быть, он как-то определяет неинициализированные устройства - я просто не знаю. - person Eugene; 24.10.2017
comment
Извините, что копаю, но у меня есть вопрос в этом. После создания файла устройства и т. д. я хотел бы прочитать, записать и т. д. к нему. Для этого мне приходится менять права доступа к файлу с помощью chmod каждый раз, когда я загружаю модуль. Есть ли автоматизированный способ сделать это. - person Parth K; 30.01.2019
comment
Вы можете использовать правило udev, например, для автоматической установки необходимых разрешений. См. раздел unix.stackexchange.com/questions/111593/ - person Eugene; 31.01.2019

Минимальный запускаемый пример

Свернуто из других ответов. GitHub upstream с тестовой настройкой.

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h> /* register_chrdev, unregister_chrdev */
#include <linux/module.h>
#include <linux/seq_file.h> /* seq_read, seq_lseek, single_release */

#define NAME "lkmc_character_device_create"

static int major = -1;
static struct cdev mycdev;
static struct class *myclass = NULL;

static int show(struct seq_file *m, void *v)
{
    seq_printf(m, "abcd");
    return 0;
}

static int open(struct inode *inode, struct file *file)
{
    return single_open(file, show, NULL);
}

static const struct file_operations fops = {
    .llseek = seq_lseek,
    .open = open,
    .owner = THIS_MODULE,
    .read = seq_read,
    .release = single_release,
};

static void cleanup(int device_created)
{
    if (device_created) {
        device_destroy(myclass, major);
        cdev_del(&mycdev);
    }
    if (myclass)
        class_destroy(myclass);
    if (major != -1)
        unregister_chrdev_region(major, 1);
}

static int myinit(void)
{
    int device_created = 0;

    /* cat /proc/devices */
    if (alloc_chrdev_region(&major, 0, 1, NAME "_proc") < 0)
        goto error;
    /* ls /sys/class */
    if ((myclass = class_create(THIS_MODULE, NAME "_sys")) == NULL)
        goto error;
    /* ls /dev/ */
    if (device_create(myclass, NULL, major, NULL, NAME "_dev") == NULL)
        goto error;
    device_created = 1;
    cdev_init(&mycdev, &fops);
    if (cdev_add(&mycdev, major, 1) == -1)
        goto error;
    return 0;
error:
    cleanup(device_created);
    return -1;
}

static void myexit(void)
{
    cleanup(1);
}

module_init(myinit)
module_exit(myexit)
MODULE_LICENSE("GPL");
person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 06.08.2017
comment
Сантилли - +1 за отличный пример. - person Guy Avraham; 17.10.2017

person    schedule
comment
Возможно, cdev_add() следует вызывать перед device_create(). Кажется, это более распространенный порядок. - person Craig McQueen; 27.03.2015
comment
Код может быть понятен, если вы будете использовать goto, как это использует ядро. - person GeekyJ; 05.01.2016
comment
Ваша очистка отсутствует cdev_del. См.: stackoverflow.com/a/45531867/895245 - person Ciro Santilli 新疆再教育营六四事件ۍ 06.08.2017