Разбираемся в одной статье | Драйвер символьного устройства Linux
Разбираемся в одной статье | Драйвер символьного устройства Linux

Разбираемся в одной статье | Драйвер символьного устройства Linux

image-20231123091238538

1. Предисловие

как мы все знаем,LinuxЯдро в основном включает в себя три модели драйверов.,Драйвер символьного устройства,Блокируйте драйверы устройств и драйверы сетевых устройств.

в,LinuxДрайвер символьного устройства,Можно сказать, чтоLinuxСамая распространенная модель драйверов при разработке драйверов.。

Наша серия статей,Главным образом, чтобы помочь вам быстро начать работуLinuxСтимулируйте развитие,В этой статье основное внимание уделяется пониманию некоторых Драйвер символьного устройстварамки и механизмы。

Серия статей по мотивамKernel 4.19

2. Ключевые структуры данных

2.1 cdev

Язык кода:javascript
копировать
struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
} __randomize_layout;

Имя структурыcdev

расположение файлаinclude/linux/cdev.h

Основная функцияcdevможно понимать какchar device,Используется для абстрагирования символьного устройства.

Основные члены и значение

  • kobj:Представляет объект ядра。
  • owner:указатель на модуль
  • ops:указатель на файловую операцию,включатьopenreadwriteЖдите операцийинтерфейс
  • list:Используется для добавления устройства в связанный список модулей ядра.
  • dev:Номер устройства,Состоит из основного номера устройства и младшего номера устройства.
  • count:Показывает, сколько существует устройств одного типа.,Он также косвенно представляет диапазон номеров устройств.
  • __randomize_layout:директива компилятора,Используется для рандомизации расположения структур.,для дополнительной безопасности.

2.2 file_operations

Язык кода:javascript
копировать
struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
    ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
    int (*iterate) (struct file *, struct dir_context *);
    int (*iterate_shared) (struct file *, struct dir_context *);
    __poll_t (*poll) (struct file *, struct poll_table_struct *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    unsigned long mmap_supported_flags;
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, loff_t, loff_t, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **, void **);
    long (*fallocate)(struct file *file, int mode, loff_t offset,
              loff_t len);
    void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
    unsigned (*mmap_capabilities)(struct file *);
#endif
    ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
            loff_t, size_t, unsigned int);
    int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
            u64);
    int (*dedupe_file_range)(struct file *, loff_t, struct file *, loff_t,
            u64);
    int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;

Имя структурыfile_operations

расположение файлаinclude/linux/fs.h

Основная функция:как следует из названия,В основном используется для описания различных файловых операций интерфейса.,LinuxВсе мысли по подключению файлов,С каким файлом ядро ​​хочет работать?,Всего этого нужно добиться через эти интерфейсы.

Основные члены и значение

  • open:открыть файлфункция
  • read:прочитать файлфункция。
  • write:записано в файлфункция。
  • release:закрыть файлфункция。
  • flush:обновить файлфункция,Обычно вызывается при закрытии файла.
  • llseek:Измените положение указателя чтения и записи файла.функция。
  • fsync:Синхронная запись данных файла на дискфункция。
  • poll:Спросите, можно ли прочитать и записать файл без блокировки.

2.3 dev_t

Язык кода:javascript
копировать
typedef u32 __kernel_dev_t;

typedef __kernel_dev_t  dev_t;

Введите имяdev_t

расположение файлаinclude/linux/types.h

Основная функция:Представляет персонажейоборудование Соответствующий Номер устройства,ввключатьхозяин Номер устройстваи Второсортный Номер устройства。

3. Связь между структурами данных

image-20231123085448145

Рисунок выше правильный Драйвер символьного устройстваструктуры данных программы иAPIдиаграмма, Если вам нужны оригинальные файлы, вы можете получить их по публичному адресу [Embedded Art].

4. Общая архитектура драйвера символьного устройства

4.1 Функции загрузки и разгрузки

Первое, что реализует драйвер, — это функции загрузки и выгрузки, которые также являются функциями входа в программу драйвера.

Обычно мы определяем функции загрузки и выгрузки драйвера следующим образом:

Язык кода:javascript
копировать
static int __init xxx_init(void)
{

}

static void __exit xxx_exit(void)
{
    
}

module_init(xxx_init);
module_exit(xxx_exit);

Этот код предназначен для реализации загрузки и выгрузки универсального драйвера.,оmodule_initиmodule_exitмеханизм реализации,Вы можете просмотреть предыдущие сводные статьи.

4.2 Управление номерами устройств

4.2.1 Понятие номера устройства

Каждый тип символьного устройства имеет уникальный номер устройства, и номер устройства делится на основной номер устройства и младший номер устройства. Итак, каковы функции этих двух устройств?

  • Основной номер устройства: используется для идентификации типа устройства,
  • Младший номер устройства: используется для различения разных устройств одного типа.

Проще говоря,хозяин Номер устройстваиспользуется для различениядаIICОборудование все ещеSPIоборудование,而Второсортный Номер устройстваиспользуется для различенияIICоборудование Вниз,Какое конкретно устройство,даMPU6050все ещеEEPROM

4.2.2 Присвоение номеров устройствам

Узнать о предложении номера устройства,Linuxсередина Номер устройстваих так много,Так как же нам использовать правильный номер устройства?

Существует два способа выделения номеров устройств: один — динамическое выделение, другой — статическое. Его также можно понимать как автоматическое выделение ядром, а другой — ручное выделение.

Функция статического распределения

Язык кода:javascript
копировать
int register_chrdev_region(dev_t from, unsigned count, const char *name);
  • from:представляет собой известный Номер устройства
  • count:означает непрерывныйоборудование Количество номеров,(Сколько существует устройств одного типа)
  • name:выражатьоборудование Или имя водителя

функциональный эффект:кfromНомер устройстваначинать,непрерывное распределениеcountтот же тип Номер устройства

**Функция динамического распределения**:

Язык кода:javascript
копировать
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
  • dev:Номер устройствауказатель,Значение, используемое для хранения назначенного номера устройства.
  • baseminor:Второсортный Номер устройстваначинать分配из起始值
  • count:означает непрерывныйоборудование Количество номеров,(Сколько существует устройств одного типа)
  • name:выражатьоборудование Или имя водителя

функциональный эффект:отbaseminorВторосортный Номер устройстваначинать,непрерывное распределениеcountтот же тип Номер устройства,и автоматически назначить основной номер устройства,Общий、Второсортный组成из Номер устройства Информация закреплена за*dev

**Самая большая разница между этими двумя функциями**:

  • register_chrdev_region:перед звонком,Старший и младший номера устройств были заданы заранее.,После вызова интерфейса,Настроенный номер устройства будет зарегистрирован и добавлен в подсистему.,Системе удобно отслеживать использование номера устройства системы.
  • alloc_chrdev_region:перед звонком,Старший и младший номера устройств после звонка не определяются;,хозяин Номер устройствак0выражать,для автоматического назначения,и номер устройства будет присвоен автоматически,Также добавлено в подсистему,Системе удобно отслеживать использование номера устройства системы.

Общим для этих двух функций является то, что

Система поддерживает список массивов для регистрации всей информации о номерах используемых устройств. В конечном итоге эти два интерфейса также регистрируют информацию о номерах своих устройств в списке номеров устройств, поддерживаемом системой, чтобы избежать последующего конфликтного использования.

существоватьLinuxсередина,мы можемкпроходитьcat /proc/devicesЗаказ,Просмотрите список всех зарегистрированных номеров устройств.

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

4.2.3 Аннулирование номера устройства

В качестве системного ресурса номер устройства должен быть возвращен в систему при удалении соответствующего устройства. Независимо от того, выделено ли оно статически или динамически, в конечном итоге будет вызвана следующая функция для выхода из системы.

Язык кода:javascript
копировать
void unregister_chrdev_region(dev_t from, unsigned count);
  • from:представляет собой известный Номер устройства
  • count:означает непрерывныйоборудование Количество номеров,(Сколько существует устройств одного типа)

функциональный эффект:Чтобы выйти из системыfromхозяин Номер устройства Внизиз连续countиндивидуальныйоборудование

4.2.4 Получение номера устройства

Управление номерами устройств очень простое.,в ключевых структурах данных,Давайте посмотримприезжать Номер устройстватипдаdev_t,То естьдаu32числовое значение, представленное типом。

вхозяин Номер устройстваи Второсортный Номер устройстваразделительная линия,Зависит отMINORBITSСпецификация определения макроса:

Язык кода:javascript
копировать
#define MINORBITS 20

То естьдахозяин Номер устройства Высокая заполняемость12bit,Второсортный Номер устройства Низкая заполняемость20bit

и,Ядро также предоставляет связанныеAPIинтерфейс,Чтобы получить основной номер устройства и дополнительный номер устройства,и интерфейс, который генерирует номера устройств,следующее:

Язык кода:javascript
копировать
#define MINORMASK ((1U << MINORBITS) - 1)

#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

В приведенном выше примере первичные и вторичные номера устройств получаются посредством операции сдвига.

4.2.4 Реализация общего кода

Язык кода:javascript
копировать
#define CUSTOM_DEVICE_NUM 0
#define DEVICE_NUM 1
#device DEVICE_NAME "XXXXXX"
static dev_t global_custom_major = CUSTOM_DEVICE_NUM;

static int __init xxx_init(void)
{
    dev_t custom_device_number= MKDEV(global_custom_major, 0); // custom device number
    /* device number register*/
    if (global_custom_major) {
        ret = register_chrdev_region(custom_device_number, DEVICE_NUM, DEVICE_NAME);
    } else {
        ret = alloc_chrdev_region(&custom_device_number, 0, DEVICE_NUM, DEVICE_NAME);
        global_custom_major = MAJOR(custom_device_number);
    }
}

static void __exit xxx_exit(void)
{
    unregister_chrdev_region(MKDEV(global_mem_major, 0), DEVICE_NUM);
}

module_init(xxx_init);
module_exit(xxx_exit);

Реализована функция Присвоение номеров устройств,如果хозяин Номер устройствадля0,Затем используйте метод динамического распределения,В противном случае используется статическое распределение.

Еще больше полезной информации можно найти: место сбора старших инженеров, которое поможет каждому выйти на новый уровень!

4.3 Управление символьными устройствами

Разобравшись с управлением номерами устройств, давайте посмотрим, как управляются символьные устройства.

4.3.1. Инициализация символьного устройства.

Язык кода:javascript
копировать
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
  • cdev:一индивидуальный字符оборудованиеобъект,То есть устройство персонажа, которое мы создали
  • fops:персонажоборудованиеобработка файловинтерфейс

функциональный эффект:初始化一индивидуальный字符оборудование,и将所Соответствующий文件处理указатель与字符оборудованиеперевязать。

4.3.2. Регистрация символьного устройства.

Язык кода:javascript
копировать
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
  • p:一индивидуальный字符оборудованиеуказатель,Добавляется только объект символьного устройства.
  • dev:персонажоборудование所负责из第一индивидуальныйоборудованиесерийный номер
  • count:Этот типоборудованиеизиндивидуальный数

функциональный эффект:добавить Драйвер символьного устройстваприезжатьLinuxсистемасередина。

4.3.3. Выход из символьного устройства.

Язык кода:javascript
копировать
void cdev_del(struct cdev *p);
  • p:Укажите на персонажаоборудованиеобъектуказатель

функциональный эффект:отсистемасерединаудалите это Драйвер символьного устройства

4.4 Реализация интерфейса файловых операций

因длясуществоватьLinuxсередина,Идея о том, что все представляет собой файл,Таким образом, каждое символьное устройство,Существует также файловый узел, которому он соответствует.

Когда мы инициализируем символьное устройство,воляstruct file_operationsизобъект与字符оборудование Связывать,Его рольда来处理персонажоборудованиеизopenreadwriteЖдите операций。

Что нам нужно сделать, так это реализовать необходимый нам функциональный интерфейс, например:

Язык кода:javascript
копировать
static const struct file_operations global_mem_fops = {
    .owner = THIS_MODULE,
    .llseek = global_mem_llseek,
    .read = global_mem_read,
    .write = global_mem_write,
    .unlocked_ioctl = global_mem_ioctl,
    .open = global_mem_open,
    .release = global_mem_release,
};

На данный момент у нас есть базовое понимание структуры базового драйвера символьного устройства.

5. Резюме

Целью этой статьи является объяснение в доступной для понимания форме:

  • Структуры данных, связанные с драйвером символьного устройства
  • диаграмма структуры данных
  • основнойAPIинтерфейс
  • Общая структура драйвера символьного устройства

Надеюсь, это поможет всем.

boy illustration
【Java】Решено: org.springframework.web.HttpRequestMethodNotSupportedException
boy illustration
Native js отправляет запрос на публикацию_javascript отправляет запрос на публикацию
boy illustration
.net PDF в Word_pdf в Word
boy illustration
[Пул потоков] Как Springboot использует пул потоков
boy illustration
Подробное объяснение в одной статье: Как работают пулы потоков
boy illustration
Серия SpringCloud (6) | Поговорим о балансировке нагрузки
boy illustration
IDEA Maven может упаковать все импортное полностью красное решение — универсальное решение.
boy illustration
Последний выпуск 2023 года, самое полное руководство по обучению Spring Boot во всей сети (с интеллект-картой).
boy illustration
[Решено — Практическая работа] SaTokenException: запрос не может быть получен в контексте, отличном от Интернета. Решение проблем — Практическая работа.
boy illustration
HikariPool-1 - Connection is not available, request timed out after 30000ms
boy illustration
Power Query: автоматическое суммирование ежемесячных данных с обновлением одним щелчком мыши.
boy illustration
установка Ubuntu в среде npm
boy illustration
3 Бесплатные системы управления складом (WMS) .NET с открытым исходным кодом
boy illustration
Глубокое погружение в библиотеку Python Lassie: мощный инструмент для автоматизации извлечения метаданных
boy illustration
Объяснение прослушивателя серии Activiti7 последней версии 2023 года
boy illustration
API-интерфейс Jitu Express для электронных счетов-Express Bird [просто для понимания]
boy illustration
Каковы архитектуры микросервисов Java. Серверная часть плавающей области обслуживания
boy illustration
Описание трех режимов жизненного цикла службы внедрения зависимостей Asp.net Core.
boy illustration
Java реализует пользовательские аннотации для доступа к интерфейсу без проверки токена.
boy illustration
Серверная часть Unity добавляет поддержку .net 8. Я еще думал об этом два дня назад, и это сбылось.
boy illustration
Проект с открытым исходным кодом | Самый элегантный метод подписки на публичные аккаунты WeChat на данный момент
boy illustration
Разрешения роли пользователя Gitlab Гость, Репортер, Разработчик, Мастер, Владелец
boy illustration
Spring Security 6.x подробно объясняет механизм управления аутентификацией сеанса в этой статье.
boy illustration
[Основные знания ASP.NET] — Аутентификация и авторизация — Использование удостоверений для аутентификации.
boy illustration
Соединение JDBC с базой данных MySQL в jsp [легко понять]
boy illustration
[Уровень няни] Полный процесс развертывания проекта Python (веб-страницы Flask) в Docker.
boy illustration
6 способов чтения файлов свойств, рекомендуем собрать!
boy illustration
Графическое объяснение этапа строительства проекта IDEA 2021 Spring Cloud (базовая версия)
boy illustration
Подробное объяснение технологии междоменного запроса данных JSONP.
boy illustration
Учебное пособие по SpringBoot (14) | SpringBoot интегрирует Redis (наиболее полный во всей сети)