Подтвердить что ты не робот

Имитировать эффект select() и poll() в программировании сокета ядра

Один из драйверов ядра 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(), чтобы получить адрес структуры списка, который содержит структура работников.

Какая техника самая звуковая?

4b9b3361

Ответ 1

Ваша вторая идея звучит скорее как она будет работать.

Код CEPH выглядит так, как будто он делает что-то подобное, см. net/ceph/messenger.c.