Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

Для выгрузки модуля используется утилита rmmod. Выгрузка модуля более простая задача нежели его загрузка, при которой выполняется его динамическая линковка с ядром. При выгрузке модуля выполняется системный вызов delete_module(), который либо выполняет вызов функции cleanup_module() выгружаемого модуля в случае, если его счетчик использования равен нулю, либо прекращает работу с ошибкой.


12. Охарактеризуйте механизм проверки версии модулей ядра.

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

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

При компиляции модуля специальный макрос из <module. h> определяет номер версии ядра, для которого производится компиляция, и помещается полученный номер в специальгую секцию объектного файла модуля. Поэтому при включении его в код модуля при компиляции автоматически определяется номер ядра. Если номер ядра не определен, то он принимается раным номеру текущего релиза ядра (например, 2.2 или 2.4).

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

В случае отказа загрузки модуля по причине несоответствия версий, можно попытаться загрузить этот модуль передав в строку параметров утилиты insmod ключ -f (force). В этом случае линковка производится по сигнатурам функций. Если сигнатура функций в новой версии ядра была изменена, то модуль все ранво не будет загружен.

НЕ нашли? Не то? Что вы ищете?

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


13. Подсчет ссылок на модули ядра. Использование макросов MOD_INC_USE_COUNT, MOD_DEC_USE_COUNT и MOD_IN_USE.

Сейчас подсчет ссылок происходит автоматически, раньше использовали макросы.

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

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

MOD_INC_USE_COUNT

Увеличивает счетчик использования текущего модуля

int silly_open(struct inode *inode, struct file *filp)

{

MOD_INC_USE_COUNT;

return 0;

}

MOD_DEC_USE_COUNT

Уменьшает счетчик использования текущего модуля

MOD_IN_USE

Возвращает истину, если счетчик использования данного модуля равен нулю. Не требуется проверять MOD_IN_USE в коде функции cleanup_module(), потому, что эта проверка выполняется автоматически до вызова cleanup_module() в системном вызове sys_delete_module(), который определен в kernel/module. c.

Эти макросы определены в <linux/module. h>, и они манипулируют специальной внутренней структурой данных прямой доступ к которой нежелателен. Дело в том, что внутренняя структура и способ управления этими данными могут меняться от версии к версии, в то время как внешний интерфейс использования этих макросов остается неизменным.

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

Возможны ситуации, в которых, по понятным причинам, вы не сможете выгрузить модуль если потеряете управление счетчиком его использования. Такая ситуация часто встречается на этапе разработки модуля. Например, процесс может прерваться при попытке разыменования NULL указателя, и вы не сможете выгрузить такой модуль, пока не вернете счетчик его использования к нулю. Одно из возможных решений такой проблемы на этапе отладки модуля заключается в полном отказе от управления счетчиком использования модуля путем переопределения MOD_INC_USE_COUNT и MOD_DEC_USE_COUNT в пустой код.

14. Драйверы символьных устройств. Старший (major) и младший (minor) номера устройств.

Символьные устройства доступны через специальные символьные файлы устройств файловой системы Unix. Такие файлы часто называются интерфейсами символьных устройств. Обычно, они располагаются в каталоге /dev. С помощью команды ls - l можно вывести список файлов этого каталога, (интерфейсы симв. у-тв помечены символом “c”, интерфейсы бл. у-тв с символом “b”). В строках вывода есть два номера разделенные запятыми перед датой последнего изменения файла, на месте где обычно располагается размер файла. Эти номера представляют собой старший и младший номер каждого из устройств. Ниже приведен типичный пример вывода команды ls - l. В этом списке, старшие номера устройств представлены числами 1, 10, тогда как младшие – числами 3, 1.

crw-rw-rw - 1 root root 1, 3 Febnull

crw———- 1 root root 10, 1 Febpsaux

Старший номер (major) определяет драйвер связанный с устройством, т. е. это номер драйвера. Например, устройства /dev/null используют драйвер с номером 1. Ядро использует старший номер устройства для диспетчеризации запроса на требуемый драйвер. [Старший номер говорит о том, какой драйвер используется для обслуживания аппаратного обеспечения. Каждый драйвер имеет свой уникальный старший номер. Все файлы устройств с одинаковым старшим номером управляются одним и тем же драйвером.]

Младший номер (minor) определяет устройство, и используется драйвером, определенным по старшему номеру. Никакие другие части ядра не используют младший номер устройства. Наиболее часто, один драйвер управляет разными устройствами различаемыми кодом драйвера по младшему номеру. [Младший номер используется драйвером, для различения аппаратных средств, которыми он управляет. Даже если несколько устройств обслуживаются одним и тем же драйвером, тем не менее каждое из них имеет уникальный младший номер, поэтому драйвер "видит" их как различные аппаратные устройства.] На практике, различные младшие номера используются для доступа к различным устройствам управляемым одним драйвером, или для открытия одного и того же устройства с разными целями.

Добавление нового драйвера в систему означает назначение ему старшего номера.

Старший номер устройства представляет собой небольшое целое число, являющееся индексом в массиве драйверов символьных устройств. Ядро версии 2.0 поддерживало 128 устройств, ядра версий 2.2 и 2.4 увеличили их число до 256 (резервируя номера 0 и 255 для особого использования в будущем).

Младшие номера (0 до 255), также, представляют собой восьмибитовую величину. Эти номера не передаются в функцию register_chardev(), потому что они используются только для диспетчеризации устройств в коде драйвера, и системе безынтересны.

struct file_operation fop={

open: drv_open;

… };

int drv_open (struct inode * inodp, struct file &filp)

{ int k=MINOR (inodp ->i_rdev); } //макрос, вызывающий minor number

15. Динамическое выделение старших номеров устройств.

Некоторые старшие номера статически назначаются наиболее общим устройствам. Список таких устройств - в файле Documentation/devices. txt, расположенном в дереве каталогов источников ядра. Следует учитывать 1) что многие номера уже назначены, 2) что в системе могут работать пользовательские драйверы с назначенными старшими номерами.

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

Старшие номера устройств в диапазонах от 60 до 63, от 120 до 127, от 240 до 254 зарезервированы для местного и экспериментального использования. Эти номера не должны быть назначены реальным устройствам.

Можно запросить динамическое назначение для старших номеров устройств.

Если параметр major установлен в 0, при вызове register_chrdev(), то функция регистрации сама выбирает свободный номер и возвращает его. Возвращенный, в этом случае номер, всегда положителен. Отрицательное значение всегда говорит об ошибке исполнения функции. Заметьте, что интерпретация возвращаемого значения функции register_chardev() несколько отличается при статическом и динамическом назначении номеров.

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

где unsigned int major -- запрашиваемый старший номер устройства, const char *name -- название устройства, которое будет отображаться в /proc/devices и struct file_operations *fops -- указатель на таблицу file_operations драйвера. В случае ошибки, функция register_chrdev() возвращает отрицательное число.

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

Лучше использовать динамическое назначение старшего номера устройства.

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

Неудобство динамического назначения: нельзя использовать постоянный файл интерфейса устройства, потому что нет никакой гарантии, что при следующей регистрации, ваш модуль получит тот же самый старший номер устройства. Таким образом, нельзя обеспечить загрузку драйвера по требованию. Это представляет проблему для нормального использования драйвера –придется определять назначенный номер устройства по содержимому файла /proc/devices.

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

16. Регистрация драйвера символьного устройства и удаление драйвера из системы.

Добавление драйвера в систему подразумевает его регистрацию в ядре, т. е. получение старшего номера в момент инициализации модуля. Получить его можно вызовом функции register_chrdev(), определенной в <linux/fs. h>:

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

- функция регистрации драйвера устройства. Если старший номер задан нулем, то функция назначает устройству динамический старший номер. В случае ошибки, функция register_chrdev() возвращает отрицательное число.

int unregister_chrdev(unsigned int major, const char *name);

- функция отмены регистрации вызываемая при выгрузке драйвера. (Когда модуль выгружается из системы, старший номер устройства должен быть освобожден). Значение major и name должны быть такими же, какие были у драйвера во время регистрации.

unsigned int major -- запрашиваемый старший номер устройства,

const char *name -- название устройства, которое будет отображаться в /proc/devices

struct file_operations *fops -- указатель на таблицу file_operations драйвера.

Ядро сравнивает переданное в функцию имя с зарегистрированным для данного номера. Если значения имени не совпадают, то функция возвращает значение - EINVAL. Ядро, также возвращает - EINVAL, если значение старшего номера устройства выходит за допустимый диапазон.

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

17. Перечислите основные виды файлов в ОС LINUX и охарактеризуйте их.

С точки зрения операционной системы файл представляет собой просто поток байтов. Такой подход позволяет распространить концепцию файла на физические устройства и некоторые другие объекты. Это позволяет упростить организацию данных и обмен ими, потому что аналогичным образом осуществляется запись данных в файл, передача их на физические устройства и обмен данными между процессами. С точки зрения ОС Linux, все подключаемые к компьютеру устройства (жесткие и съемные диски, терминал, принтер, модем и т. д.), представляются файлами. Если, например, надо вывести на экран какую-то информацию, то система как бы производит запись в файл /dev/tty01. Во всех случаях используется один и тот же подход, основанный на идее байтового потока. Поэтому наряду с обычными файлами и каталогами, файлами с точки зрения Linux являются также:

·  файлы физических устройств;

Физические устройства бывают двух типов: символьными (или байт-ориентированными) и блочными (или блок-ориентированными). Различие между ними состоит в том, как производится считывание и запись информации в эти устройства. Взаимодействие с символьными устройствами производится посимвольно, в режиме потока байтов. К таким устройствам относятся, например, терминалы. На блок-ориентированных устройствах информация записывается (и, соответственно, считывается) блоками. Примером устройств этого типа являются жесткие диски. На диск невозможно записать или считать с него один байт: обмен с диском производится только блоками.

Взаимодействием с физическими устройствами в Linux управляют драйверы устройств, которые либо встроены в ядро, либо подключаются к нему как отдельные модули. Для взаимодействия с остальными частями операционной системы каждый драйвер образует коммуникационный интерфейс, который выглядит как файл. Большинство таких файлов для различных устройств как бы "заготовлены заранее" и располагаются в каталоге /dev.

·  именованные каналы или буферы FIFO (named pipes);

Файлы этого типа служат в основном для того, чтобы организовать обмен данными между разными. Канал — это очень удобное и широко применяемое средство обмена информацией между процессами. Все, что один процесс помещает в канал, другой может оттуда прочитать. Если два процесса, обменивающиеся информацией, порождены одним и тем же родительским процессом (а так чаще всего и происходит), канал может быть неименованным. При этом собственно файл именованного канала участвует только в инициации обмена данными.

·  гнезда (sockets);

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

·  символические ссылки (symlinks).

Жесткая ссылка является еще одним именем для исходного файла. Она прописывается в индексном дескрипторе исходного файла. После создания жесткой ссылки невозможно различить, где исходное имя файла, а где ссылка. Если вы удаляете один из этих файлов (точнее одно из этих имен), то файл еще сохраняется на диске (пока у него есть хоть одно имя-ссылка). Одно из применений жестких ссылок состоит в том, чтобы предотвратить возможность случайного удаления файла. Особенностью жестких ссылок является то, что они прямо указывают на номер индексного дескриптора, т. е. такие имена могут указывать только на файлы внутри той же самой файловой системы (на том же самом носителе, на котором находится каталог, содержащий это имя).

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

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

18. Структура file_operations: основные члены и назначение. Использование расширенного синтаксиса для ее инициализации.

Открытое устройство характеризуется структурой file, а ядро использует структуру file_operations для доступа к функциям драйвера. Структура, определенная в заголовочном файле <linux/fs. h> представляет собой массив указателей на функции драйвера, обрабатывающие стандартные запросы. Каждый файл интерфейса связан с собственным набором функций, т. к. для каждого из них определено поле f_op указывающее на структуру file_operations. Эти механизмы поддерживаются системными вызовами open(), read() и пр.

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

Именно через структуру file_operations добавляется новая функциональность в ядро.

Ниже приводится определение структуры, взятое из исходных текстов ядра 2.6.5:

struct file_operations {

loff_t(*llseek) (struct file *, loff_t, int);// loff_t drv_llseek (struct file *filp, loff_t off, int whence)

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

ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);

// для получения данных из устройства.// ssize_t drv_read (struct file * filp, char* buff, size_t count, loff_t * pos)

ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);

// пишет данные в устройство.// ssize_t drv_write (struct file * filp, char* buff, size_t count, loff_t * pos)

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

// для выполнения различных операций по упр. оборудованием через драйвер //

int (*open) (struct inode *, struct file *);

//для проведения подготовки к дальнейшим операциям с драйвером// int drv_open(struct inode * nod, struct file * filp)

int (*release) (struct inode *, struct file *); …};

// освобождает память, занятую под указатели filp->private_data в методе open() и выполняет все завершающие действия при последнем закрытии устройства// int mydrv_release(struct inode * nod, struct file * filp)

struct inode * – Указатель на структуру inode специального файла устройства, доступного для использования непосредственно пользователем.

char * buff - адрес, куда надо прочесть пользовательскую часть.

size_t size - размер, кот. пользователь хочет прочесть.

loff_t * pos - ссылается на текущую позицию внутри файла.

Драйвер зачастую реализует не все функции, предусмотренные структурой file_operations. Поля структуры с нереализованными функциями, заполняются "пустыми" указателями — NULL.

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

struct file_operations fops = {read: device_read, write: device_write,};

Однако, существует еще один способ заполнения структур, который описывается стандартом C99. Причем этот способ более предпочтителен. gcc 2.95, который я использую, поддерживает синтаксис C99. Вам так же следует придерживаться этого синтаксиса, если вы желаете обеспечить переносимость своему драйверу:

struct file_operations fops = {.read = device_read, .write = device_write,};


19. Структура file: члены и назначение.

Структура file, определенная в <linux/fs. h>, является второй, по важности структурой данных, используемой в драйверах устройств. (Структура FILE, доступная программам пользовательского пространства, определена в стандартной библиотеке языка Си, и не видна из кода ядра). В противоположность этому структура file принадлежит ядру, и не используется в программах пользователя.

Структура file представляет информацию об открытом файле. !это не является спецификой именно драйверов устройств – каждый открытый в системе файл связан со структурой file в пространстве ядра. Она создается ядром в ответ на системный вызов open() и передается во все функции работающие с файлом. При закрытии файла системным вызовом close() код ядра освобождает эту структуру данных. Лежащий на диске файл представляется в системе структурой inode. При открытии файла, система дополняет информацию о файле соответствующими логическими структурами.

В источниках ядра, указатель на структуру file обычно называется либо file либо filp (“file pointer”). Мы будем использовать название filp для этого указателя, для предотвращения двусмысленности. Таким образом, в нашем коде, название file применяется к структуре, а название filp к указателю на эту структуру.

Наиболее важные поля структуры file описаны ниже.

loff_t f_pos;

Текущее значение позиции чтения или записи. loff_t представляет собой 64-битное значение. Драйвер может обратится к этому значению, если потребуется значение текущей позиции в файле, но он, ни в коем случае не должен изменять это значение. Изменение этого значения должно производиться только в функциях read() и write(), которые, последним аргументом, получают указатель на этот элемент.

unsigned int f_flags;

Имеется набор флагов определенных для файла, таких как O_RDONLY, O_NONBLOCK и O_SYNC. Часто, драйверу может понадобиться проверить флаг запрещения блокировки, в то время остальные флаги используются значительно реже. В особенности, права на чтение/запись должны проверяться через f_mode, вместо f_flags. Полный набор флагов определен в заголовочном файле <linux/fcntl. h>.

struct file_operations *f_op;

Операции, связанные с файлом. Ядро назначает этот указатель в коде системного вызова open(). Чтение этого указателя производится ядром при каждом переключении (диспетчеризации) операций. Значение filp->f_op не сохраняется нигде отдельно для последующих обращений. Это означает, что в любое время можно изменить операции, связанные с файлом, и они вступят в силу уже при следующем вызове. Это позволяет реализовывать различное поведение для одного и того же старшего номера устройства без перегрузки каждого системного вызова.

void *private_data;

Код системного вызова open() устанавливает этот указатель в NULL перед вызовом метода open() вашего драйвера. Драйвер может использовать это поле по своему собственному усмотрению или не использовать его вовсе. Можно использовать это поле для указателя на динамически распределенную область памяти. В этом случае, необходимо освободить эту память в методе release() драйвера, перед тем, как ядро уничтожит структуру file. Такой указатель очень удобен для передачи информации между системными вызовами и используется в большинстве наших примеров модулей.

В действительности, структура file имеет еще несколько других полей. Драйвер не заполняет, а использует структуру file.

20. Методы open и release структуры file_operations.

Метод open() может быть использован драйвером для проведения подготовки к дальнейшим операциям с драйвером. Кроме того, обычно в этом методе увеличивают на 1 счетчик использования модуля, предотвращая его выгрузку до момента закрытия файла-интерфейса к модулю.

В большинстве драйверов, метод open() выполняет следующие задачи:

·  Инкрементирование счетчика использования модуля.

·  Специфичные для устройства проверки

(проверка готовности устройства или выявление какие-нибудь других специфичных проблем).

·  Инициализация устройства, если оно открывается первый раз.

·  Определение младшего номера устройства и соотв. корректировка поля f_op, при необходимости.

·  Распределение и заполнение других структур данных для передачи их через указатель filp->private_data

int mydrv_open( struct inode* node,

struct file* filp )

{

int i = MINOR(node->i_rdev);//0 - если все хорошо

//отрицательное число если ошибка

printk("MINOR ID = %d",i);

return 0;

};

 

Назначение метода release() противоположно методу open(). Иногда метод носит название device_close() а не device_release(). Метод должен выполнять следующие задачи:

·  Освобождение всей памяти занятой под указателем filp->private_data в методе open()

    По необходимости, выполняются все завершающие действия при последнем закрытии устройства Декрементирование (уменьшение на 1) счетчика использования модуля

int mydrv_release(struct inode *inode, struct file *filp)

{

MOD_DEC_USE_COUNT;

return 0;

}

Обратите внимание, что декрементрирование счетчика использования модуля необходимо, если вы инкрементируете его в методе open(). Иначе, ядро не сможет выгрузить модуль, счетчик использования которого не равен нулю.

Не каждый системный вызов close() приводит к вызову метода release().

Метод release() вызывается только в том случае, если ядро действительно освобождает структуру данных устройства – отсюда и название – release значит “освобождать”. Ядро содержит счетчик использования структуры file.

Системный вызов close() вызывает метод release() только тогда, когда счетчик использования структуры file уменьшается до нуля. При этом, структура уничтожается. Взаимодействия между системным вызовом call() и методом release() гарантируют корректное состояния счетчика использования модуля.

Завершение приложения автоматически приводит к закрытию связанных с ним файлов. Т. е. для каждого открытого файла, ядро автоматически вызывает системный close().

21. Методы read и write структуры file_operations.

Методы read() и write() выполняют схожие задачи, заключающиеся в копировании данных из, или в приложение пользователя. Поэтому, их прототипы очень похожи.

Возвращаемое значение {<0 (error) } {=count (ok)} {=0 (nothing)} {>0 и <count (not all)}

ssize_t write(struct file *filp, const char *buff, size_t count, loff_t *offp);

ssize_t read (struct file *filp, char *buff, size_t count, loff_t *offp);

где

filp - указатель на структуру file,

count - определяет размер передаваемых данных.

buff - указывает на буфер данных чтения/записи.

offp - (указатель на “long offset type”) указывает на смещение от начала данных файла для операций чтения/записи.

Методы возвращают “signed size type.

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

Код методов read() / write()реализовывает копирование целых сегментов данных в / из адресного пространства пользователя. Для этого используется функции ядра, которые копируют произвольное количество байт.

Метод read() занимается копированием данных из устройства в адресное пространство пользователя, используя функцию copy_to_user().

Метод write() копирует данные из пространства пользователя в устройство, используя copy_from_user().

Для каждого системного вызова read() или write() определяется количество передаваемых байт, однако драйвер может передать меньшее количество данных. Независимо от количества данных передаваемых этими методами, они должны изменять значение *offp текущей позиции файла, после успешного исполнения системного вызова. В большинстве случаев аргумент offp представляет собой указатель на filp->f_pos.

Типичная реализация использования аргументов в методе read():

Оба метода, и read(), и write() возвращают отрицательное значение в случае ошибки. Если возвращаемое значение равно или больше нуля, то это говорит вызывающей программе о количестве успешно переданных байт. Если ошибка возникла во время передачи, то возвращается количество переданных байт, либо ошибка, при повторном вызове функции.

22. Работа с пользовательским адресным пространством. Функции copy_to_user, copy_from_user, access_ok, get_user, put_user и др.

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

unsigned long copy_to_user(void *to, const void *from, unsigned long count);

unsigned long copy_from_user(void *to, const void *from, unsigned long count);

Проверка корректности адреса в ядрах до версии 2.2.x реализуется через вызов функции access_ok(), которая описана в заголовочном файле <asm/uaccess. h>:

int access_ok(int type, const void *addr, unsigned long size);

(возвращает булево значение: 1 – в случае успешной проверки, и 0 – в случае неудачи; если проверка адреса не удалась, то драйвер обычно возвращает код ошибки –EFAULT). Функция не выполняет полной проверки для заданного диапазона адресов – определяется только принадлежность заданного диапазона тому множеству адресов, которые доступны для данного процесса. Таким образом, функция access_ok() может гарантировать, что заданный диапазон адресов не принадлежит пространству ядра. Кроме того, необходимо помнить, что в большинстве случаев вы не столкнетесь с необходимостью прямого вызова access_ok(), т. к. процедуры доступа к памяти, которые будут описаны позднее, выполняют эту проверку за вас.

После вызова access_ok() драйвер может безопасно выполнять передачу данных из одного адресного пространства в другое (ядро и пользовательский процесс). В добавлении к функциям copy_from_user() и copy_to_user(), выполняющим такую передачу данных, программист может использовать множество функций, которые оптимизированы для наиболее часто используемых размеров данных – один, два, четыре, или восемь байт (для 64-х разрядной платформы).

Список этих функций, описанных в заголовочном файле <asm/uaccess. h>.

put_user(datum, ptr)

проверяет возможность записи по данному адресу памяти. В случае успешного завершения возвращается 0, и - EFAULT в случае ошибки. __put_user() выполняет меньшее количество проверок (не выполняется вызов access_ok()), но определенные ошибки неправильной адресации могут быть определены данным вызовом. Таким образом, __put_user() должен быть использован только если регион памяти был уже проверен вызовом access_ok().

get_user(local, ptr)

для получения одного элемента данных из адресного пространства пользователя. Поведение схоже с макросом put_user(), но передача данных производится в обратном направлении. Полученное из адресного пространства пользователя значение сохраняется в локальной переменной local. Возвращаемое макросом значение определяет успешность выполнения операции.

Если попытка выполнения передачи данных с помощью описанных выше макросов приводит, на этапе компиляции, к сообщению типа "conversion to non-scalar type requested", то размер передаваемого аргумента не соответствует размерам обрабатываемым макросом. В этом случае, необходимо воспользоваться функциями copy_to_user() и copy_from_user().


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

Для выполнения функций в режиме ядра (таких как функций ввода/вывода) программа из пользовательского режима просто посылает сообщение WinDriver Kernel PlugIn. Это сообщение вызывает соответствующую функцию в режиме ядра.

пользователь: int fd = open (char const * file_path, int mode[int flags])

ядро: int open(struct inode * inode, struct file * filp)

int close (int fd)

int release (struct inode * inode, struct file * filp)

int read (int fd, char * buff, size)

ssize_t read (struct file * filp, char * buff, int size, loff_t *pos)

int write (int fd, char * buff, size)

ssize_t read (struct file * filp, char * buff, int size, loff_t *pos)

int llseek (int fd, loff_t offset, int mode)

loff_t llseek (struct file * filp, loff_t offset, int mode)

//режимы - от начала файла, от текущего положения указателя, от конца.

int ioctl (int fd, int mode, …)

int ioctl (struct inode * inode, struct file* filp, int cmd, long arg)

Указатели inode и filp представляют собой значения, соответствующие файловому дескриптору fd.

fd был передан пользовательским процессом, и полностью совпадают с параметрами, передаваемыми в системный вызов.

24. Состояние гонки в режиме ядра. Использование семафоров в режиме ядра.

Предположим, что имеется два процесса A и B, открывшие драйвер mydrv для записи данных. И оба они пытаются одновременно добавлять данные в устройство. Для обеспечение этой операции требуется распределение новых квантов. Поэтому, каждый процесс распределяет требуемое количество памяти и сохраняет эти указатели в квантовом массиве. Результат такой операции должен вызывать беспокойство. Оба процесса работают с одним и тем же устройством mydrv. Каждый сохраняет распределенные кванты в одном и том же квантовом массиве. Если сначала устройство A сохраняет свой указатель, то процесс B перезаписывает этот указатель впоследствии. Таким образом, память распределенная процессом A будет потеряна.

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3