image-20231123091238538
как мы все знаем,Linux
Ядро в основном включает в себя три модели драйверов.,Драйвер символьного устройства,Блокируйте драйверы устройств и драйверы сетевых устройств.
в,Linux
Драйвер символьного устройства,Можно сказать, чтоLinux
Самая распространенная модель драйверов при разработке драйверов.。
Наша серия статей,Главным образом, чтобы помочь вам быстро начать работуLinux
Стимулируйте развитие,В этой статье основное внимание уделяется пониманию некоторых Драйвер символьного устройстварамки и механизмы。
Серия статей по мотивам
Kernel 4.19
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
:указатель на файловую операцию,включатьopen
、read
、write
Ждите операцийинтерфейсlist
:Используется для добавления устройства в связанный список модулей ядра.dev
:Номер устройства,Состоит из основного номера устройства и младшего номера устройства.count
:Показывает, сколько существует устройств одного типа.,Он также косвенно представляет диапазон номеров устройств.__randomize_layout
:директива компилятора,Используется для рандомизации расположения структур.,для дополнительной безопасности.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
:Спросите, можно ли прочитать и записать файл без блокировки.typedef u32 __kernel_dev_t;
typedef __kernel_dev_t dev_t;
Введите имя:dev_t
расположение файла:include/linux/types.h
Основная функция:Представляет персонажейоборудование Соответствующий Номер устройства,ввключатьхозяин Номер устройстваи Второсортный Номер устройства。
image-20231123085448145
Рисунок выше правильный Драйвер символьного устройстваструктуры данных программы и
API
диаграмма, Если вам нужны оригинальные файлы, вы можете получить их по публичному адресу [Embedded Art].
Первое, что реализует драйвер, — это функции загрузки и выгрузки, которые также являются функциями входа в программу драйвера.
Обычно мы определяем функции загрузки и выгрузки драйвера следующим образом:
static int __init xxx_init(void)
{
}
static void __exit xxx_exit(void)
{
}
module_init(xxx_init);
module_exit(xxx_exit);
Этот код предназначен для реализации загрузки и выгрузки универсального драйвера.,оmodule_init
иmodule_exit
механизм реализации,Вы можете просмотреть предыдущие сводные статьи.
Каждый тип символьного устройства имеет уникальный номер устройства, и номер устройства делится на основной номер устройства и младший номер устройства. Итак, каковы функции этих двух устройств?
Проще говоря,хозяин Номер устройстваиспользуется для различенияда
IIC
Оборудование все ещеSPI
оборудование,而Второсортный Номер устройстваиспользуется для различенияIIC
оборудование Вниз,Какое конкретно устройство,даMPU6050
все ещеEEPROM
。
Узнать о предложении номера устройства,
Linux
середина Номер устройстваих так много,Так как же нам использовать правильный номер устройства?
Существует два способа выделения номеров устройств: один — динамическое выделение, другой — статическое. Его также можно понимать как автоматическое выделение ядром, а другой — ручное выделение.
Функция статического распределения:
int register_chrdev_region(dev_t from, unsigned count, const char *name);
from
:представляет собой известный Номер устройстваcount
:означает непрерывныйоборудование Количество номеров,(Сколько существует устройств одного типа)name
:выражатьоборудование Или имя водителяфункциональный эффект:кfrom
Номер устройстваначинать,непрерывное распределениеcount
тот же тип Номер устройства
**Функция динамического распределения**:
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
Заказ,Просмотрите список всех зарегистрированных номеров устройств.
Когда у нас будет время позже, мы сможем подробно поговорить о механизме автоматического распределения и механизме управления номерами устройств.
В качестве системного ресурса номер устройства должен быть возвращен в систему при удалении соответствующего устройства. Независимо от того, выделено ли оно статически или динамически, в конечном итоге будет вызвана следующая функция для выхода из системы.
void unregister_chrdev_region(dev_t from, unsigned count);
from
:представляет собой известный Номер устройстваcount
:означает непрерывныйоборудование Количество номеров,(Сколько существует устройств одного типа)функциональный эффект:Чтобы выйти из системыfrom
хозяин Номер устройства Внизиз连续count
индивидуальныйоборудование
Управление номерами устройств очень простое.,в ключевых структурах данных,Давайте посмотримприезжать Номер устройстватипдаdev_t
,То естьдаu32
числовое значение, представленное типом。
вхозяин Номер устройстваи Второсортный Номер устройстваразделительная линия,Зависит отMINORBITS
Спецификация определения макроса:
#define MINORBITS 20
То естьдахозяин Номер устройства Высокая заполняемость12bit
,Второсортный Номер устройства Низкая заполняемость20bit
и,Ядро также предоставляет связанныеAPI
интерфейс,Чтобы получить основной номер устройства и дополнительный номер устройства,и интерфейс, который генерирует номера устройств,следующее:
#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))
В приведенном выше примере первичные и вторичные номера устройств получаются посредством операции сдвига.
#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
,Затем используйте метод динамического распределения,В противном случае используется статическое распределение.
Еще больше полезной информации можно найти: место сбора старших инженеров, которое поможет каждому выйти на новый уровень!
Разобравшись с управлением номерами устройств, давайте посмотрим, как управляются символьные устройства.
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
cdev
:一индивидуальный字符оборудованиеобъект,То есть устройство персонажа, которое мы создалиfops
:персонажоборудованиеобработка файловинтерфейсфункциональный эффект:初始化一индивидуальный字符оборудование,и将所Соответствующий文件处理указатель与字符оборудованиеперевязать。
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
p
:一индивидуальный字符оборудованиеуказатель,Добавляется только объект символьного устройства.dev
:персонажоборудование所负责из第一индивидуальныйоборудованиесерийный номерcount
:Этот типоборудованиеизиндивидуальный数функциональный эффект:добавить Драйвер символьного устройстваприезжатьLinux
системасередина。
void cdev_del(struct cdev *p);
p
:Укажите на персонажаоборудованиеобъектуказательфункциональный эффект:отсистемасерединаудалите это Драйвер символьного устройства
因длясуществоватьLinux
середина,Идея о том, что все представляет собой файл,Таким образом, каждое символьное устройство,Существует также файловый узел, которому он соответствует.
Когда мы инициализируем символьное устройство,воляstruct file_operations
изобъект与字符оборудование Связывать,Его рольда来处理персонажоборудованиеизopen
、read
、write
Ждите операций。
Что нам нужно сделать, так это реализовать необходимый нам функциональный интерфейс, например:
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,
};
На данный момент у нас есть базовое понимание структуры базового драйвера символьного устройства.
Целью этой статьи является объяснение в доступной для понимания форме:
API
интерфейсНадеюсь, это поможет всем.