Ответ 1
Ваша вторая идея звучит скорее как она будет работать.
Код CEPH выглядит так, как будто он делает что-то подобное, см. net/ceph/messenger.c
.
Один из драйверов ядра 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()
, чтобы получить адрес структуры списка, который содержит структура работников.
Какая техника самая звуковая?
Ваша вторая идея звучит скорее как она будет работать.
Код CEPH выглядит так, как будто он делает что-то подобное, см. net/ceph/messenger.c
.