#linux #kernel #multicast #buffering #datagram
#linux #ядро #многоадресная передача #буферизация #дейтаграмма
Вопрос:
Я планирую написать драйвер Linux для некоторого оборудования с отображением памяти (оно находится в FPGA, поэтому я могу настроить этот интерфейс с отображением памяти на обоих концах, если это необходимо).
Эта логика FPGA генерирует последовательность дейтаграмм, которые я должен обработать, а затем передать по каналу Ethernet. Нет причин для того, чтобы код обработки или сетевой код находился в ядре, поэтому я спрашиваю о «лучшем» механизме перемещения блоков данных с оборудования в пространство пользователя. Самая большая сложность заключается в том, что обработка пользовательского пространства должна быть распределена между несколькими процессами.
Скорость передачи данных не очень высока (до 1 Мбит / с), а интерфейс mmio поддерживается довольно глубоким FIFO (в настоящее время 2 КБ, может быть увеличен до 8 КБ), поэтому я думаю, что процесс в пользовательском режиме с высоким приоритетом может продолжаться.
Что мне действительно хотелось бы, так это указатель на существующий драйвер с существующим интерфейсом многоадресной рассылки в пользовательском пространстве (и не сложный со многим другим). Но схема того, что должно быть сделано, была бы разумной заменой.
На данный момент я собрал следующие идеи:
-
AF_NETLINK: поддерживает многоадресную рассылку, заботится о буферизации для меня. Но API нестабилен, я должен определить новый идентификатор сокета, который может конфликтовать с другими драйверами в дереве, а интерфейс пользовательского режима довольно специализирован, я не могу использовать стандартные инструменты, такие как
socat
, для тестирования потока данных. -
Передайте сокет в режиме дейтаграммы или файловый дескриптор FIFO из пользовательского пространства и запишите в него (как?). Есть исправление для многоадресной рассылки сокетов дейтаграмм домена unix, которое я мог бы применить.
-
Предоставьте устройство символьного режима единственному высокоприоритетному приложению пользовательского режима, которое действует как сервер сокетов дейтаграмм домена unix и копирует дейтаграммы на каждый подключенный узел. Сохраняются ли границы дейтаграмм для устройств с символьным режимом (т. Е., если моя
read
функция драйвера возвращает меньше байтов, чем размерfread
буфера,fread
вернет ли этот блок данных как единое целое, или он может фрагментировать и собирать блоки заново? Поможет ли это, если я используюread (2)
вместоfread (3)
? Есть ли что-нибудь подобное,EMSGSIZE
что функция чтения драйвера может использовать для указания того, что дейтаграмма была усечена, или это доступно только для сокетов?) -
Предоставьте устройство символьного режима, которое может быть открыто несколькими приложениями пользовательского режима одновременно, и буферизуйте данные для каждого в ядре.
Я склоняюсь к устройству в символьном режиме с сервером домена unix, который перенаправляет входящие пакеты. Это избавляет меня от необходимости реализовывать логику буферизации внутри драйвера ядра. Тогда возникает вопрос, как разбудить пользовательский процесс от select
вызова или блокирующего чтения при возникновении прерывания. Кажется, что моя poll
функция может считывать управляющий регистр и возвращать, POLLIN|POLLRDNORM
если данные уже доступны, и демаскировать прерывание, если нет. И затем обработчик прерываний будет использовать, wake_up
чтобы пометить wait_queue
как готовый. read
всегда маскировало бы прерывание.
Комментарии:
1. Я заинтригован этим интересным проектом.
Ответ №1:
Я думаю, netlink — ваш лучший вариант. Кстати, вы можете обрабатывать сокет netlink как обычный сокет и использовать для него POLL, EPOLL, select.
Кроме того, что вы подразумеваете под «нестабильным API»? Netlink используется часто, и у него довольно приличный API.
Вам просто нужно использовать универсальный netlink для отправки (и, возможно, получения) сообщений. Ваша задача становится еще проще, если существует односторонняя связь, то есть из ядра -> пользовательского пространства.
РЕДАКТИРОВАТЬ: Если у вас есть доступ, смотрите стр. 810 и далее (глава. 12) из книги «Профессиональная архитектура ядра Linux» от Wrox. Насколько я знаю, в нем есть (относительно) хорошее описание того, как вы могли бы использовать netlink для связи с пользовательской средой.
Единственным недостатком netlink является скудная документация. В остальном, на мой взгляд, все в порядке.
Комментарии:
1. Под «API нестабилен» я подразумеваю, что каждый раз, когда я нахожу пример, в комментариях ниже жалуются на то, что он больше не работает в следующей версии ядра. например linuxjournal.com/article/7356 Это заставляет меня думать, что любой код, который существует достаточно долго, чтобы попасть в книгу, больше не будет работать так, как написано.
Ответ №2:
Я думаю, что драйвер char — лучший вариант, потому что вы найдете намного больше документации для него, а часть ядра проще. API хорошо известен, и вы найдете больше людей с опытом работы с драйвером char.
Начните с малого, т.Е. с рабочего чтения блокировки на основе прерывания :
- если данные недоступны, переходите в очередь ожидания
- else возвращает доступные данные в пользовательское пространство.
с помощью процедуры прерывания, которая в конечном итоге запускает очередь, если данные доступны.
Как только это заработает, реализуйте опрос.
Комментарии:
1. В итоге я сделал это таким образом. Возможность тестирования с помощью
hexdump
,dd
иsocat
была большим преимуществом для char driver по сравнению с netlink.
Ответ №3:
При выборе опции символьного устройства read(2)
из пользовательского пространства в конечном итоге будет вызвана read()
функция вашего драйвера (указанная в struct file_operations
том, на котором вы зарегистрировали свое устройство). Таким образом, от вашей реализации зависит, сохраняете ли вы границы дейтаграммы и какие ошибки вы хотите возвращать в различных случаях сбоя.