Один из разрабатываемых мной драйверов ядра Linux использует сетевое взаимодействие в ядре (sock_create()
., sock->ops->bind()
и так далее ).
Проблема в том, что будет несколько сокетов для получения данных. Поэтому мне нужно что-то, что будет имитировать select()
или poll()
в пространстве ядра. Поскольку эти функции используют файловые дескрипторы, я не могу использовать системные вызовы, если я не использую системные вызовы для создания сокетов, но это кажется ненужным, поскольку я работаю в ядре.
Поэтому я подумал о том, чтобы обернуть обработчик по умолчанию sock->sk_data_ready
в свой собственный обработчик (custom_sk_data_ready()
)., который разблокирует семафор. Затем я могу написать свою собственную функцию kernel_select()
, которая пытается заблокировать семафор и блокирует ожидание, пока он не будет открыт. Таким образом, функция ядра переходит в спящий режим до тех пор, пока семафор не будет разблокирован с помощью custom_sk_data_ready()
. Как только kernel_select()
получает блокировку, она разблокируется и вызывает custom_sk_data_ready()
, чтобы повторно заблокировать ее. Таким образом, единственная дополнительная инициализация — запустить custom_sk_data_ready()
перед привязкой сокета, чтобы первый вызов custom_select()
не срабатывал ложно.
Я вижу одну возможную проблему. Если происходит несколько приемов, то несколько вызовов custom_sk_data_ready()
попытаются разблокировать семафор. Таким образом, чтобы не потерять несколько вызовов и отслеживать использование sock
, должна быть таблица или список указателей на используемые сокеты. И custom_sk_data_ready()
должен будет указать в таблице/списке, какой сокет был передан.
Является ли этот метод надежным? Или я должен просто бороться с проблемой пространства пользователя/ядра при использовании стандартных системных вызовов?
Первоначальный вывод:
Все функции обратного вызова в структуре sock
вызываются в контексте прерывания. Это означает, что они не могут спать.Чтобы основной поток ядра мог спать в списке готовых сокетов, используются мьютексы, но custom_sk_data_ready()
должен действовать как спин-блокировка мьютексов (, многократно вызывающих mutex_trylock()
). Это также означает, что любое динамическое выделение должно использовать флаг GFP_ATOMIC
.
Дополнительная возможность:
Для каждого открытого сокета замените sk_data_ready()
каждого сокета на пользовательский (custom_sk_data_ready()
). и создайте рабочего(struct work_struct
)и очередь работ(struct workqueue_struct
). Для каждого работника будет использоваться общая функция process_msg()
. Создайте глобальный список уровня модуля ядра -, где каждый элемент списка имеет указатель на сокет и содержит рабочую структуру. Когда данные в сокете будут готовы, custom_sk_data_ready()
выполнит и найдет соответствующий элемент списка с тем же сокетом, а затем вызовет queue_work()
с рабочей очередью элемента списка и рабочим процессом. Затем будет вызвана функция process_msg()
, которая может либо найти соответствующий элемент списка по содержимому параметра struct work_struct *
(по адресу ), либо использовать макрос container_of()
для получения адреса списка структура, которая содержит рабочую структуру.
Какая техника самая правильная?