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

Общий семафор между пространствами пользователя и ядра

Краткая версия

Можно ли использовать семафор (или любую другую блокировку синхронизации) между пользовательским пространством и пространством ядра? Именованные семафоры POSIX имеют стойкость ядра, поэтому мне было интересно, можно ли создавать и/или получать доступ к ним из контекста ядра.

Поиск в Интернете не сильно помог из-за моря информации о нормальном использовании семафоров POSIX.

Длинная версия

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

В RTAI поток, ожидающий и отправляющий семафор, должен быть в режиме реального времени. Это означает, что использование RTAI семафора означает переключение между контекстом реального времени и не в реальном времени при каждом ожидании/сообщении в пользовательском пространстве и, что еще хуже, создание короткого потока в реальном времени для каждого сеанса sem/wait в пространстве ядра.

Я ищу способ поделиться обычным семафором Linux или POSIX между ядром и пользовательскими пространствами, чтобы я мог спокойно ждать/публиковать его в контексте, отличном от реального времени.

Любая информация по этому вопросу будет принята с благодарностью. Если это невозможно, есть ли у вас какие-либо другие идеи о том, как эта задача может быть выполнена? 1

1 Одним из способов было бы добавить системный вызов, иметь семафор в пространстве ядра и заставить процессы пространства пользователя вызывать этот системный вызов, и семафор будет управляться в пространстве ядра, Я был бы счастливее, если бы мне не пришлось исправлять ядро ​​только из-за этого.

4b9b3361

Ответ 1

Одним из решений, о котором я могу думать, является наличие файла /proc (или /sys или любого другого) в основном модуле ядра, где он записывает 0/1 (или читает/записывает на него) приведет к выпуску up/down на semaphore. Экспорт этого семафора позволяет другим модулям ядра напрямую обращаться к нему, в то время как пользовательские приложения будут проходить через файловую систему /proc.

Я все еще жду, чтобы узнать, есть ли у исходного вопроса ответ.

Ответ 2

Ну, ты был в правильном направлении, но не совсем -

Linux с именем POSIX семафор основаны на FUTex, что означает Fast Mutex для пользовательского пространства. Как следует из названия, в то время как их реализации помогает ядро, большая часть его выполняется с помощью кода пользователя. Совместное использование такого семафора между ядром и пользовательским пространством потребует повторной реализации этой инфраструктуры в ядре. Возможно, но, конечно, нелегко.

Семафоры SysV, с другой стороны, полностью реализованы в ядре и доступны только для пользовательского пространства с помощью стандартных системных вызовов (например, sem_timedwait() и друзей).

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

Таким образом, ваш код пользователя просто вызовет sem_timedwait(). Это легкая часть.

Часть ядра немного сложнее: вам нужно найти код, реализующий sem_timedwait() и связанные с ним вызовы в ядре (все они находятся в файле ipc/sem.c) и создать реплику каждая из функций выполняет то, что делает исходная функция без вызовов copy_from_user(...) и copy_to_user(..) и друзей.

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

Возьмем, например, sem_timedwait() - соответствующая функция ядра sys_timedwait() в ipc/sem.c(см. здесь: http://lxr.free-electrons.com/source/ipc/sem.c#L1537). Если вы скопируете эту функцию в свой код ядра и просто удалите части, которые делают copy_from_user() и copy_to_user(), и просто используйте переданные указатели (поскольку вы вызовете их из пространства ядра), вы получите эквивалентные функции ядра, которые могут возьмите семафор SysV из пространства ядра, по боковому пространству пользователя - , пока вы вызываете их из контекста пользователя в ядре (если вы не знаете, что означает это последнее предложение, я настоятельно рекомендую прочитать Драйверы устройств Linux, 3-е издание).

Желаем удачи.

Ответ 3

На самом деле я не очень разбираюсь в этом, но здесь я беру. Если вы посмотрите на реализацию glibc sem_open и sem_wait, это действительно просто создание файла в /dev/shm, mmap'ing из него структуры и использование атомных операций на нем. Если вы хотите получить доступ к названному семафору из пользовательского пространства, вам, вероятно, придется исправить подсистему tmpfs. Тем не менее, я думаю, что это будет сложно, так как было бы непросто определить, является ли файл названным семафором.

Более простой способ, вероятно, состоял бы в том, чтобы просто повторно использовать реализацию семафора ядра и заставить ядро ​​управлять семафором для процессов пользовательского пространства. Для этого вы должны написать модуль ядра, который вы связываете с файлом устройства. Затем определите два файла ioctl для файла устройства, один для ожидания и один для сообщения. Вот хороший учебник по написанию модулей ядра, включая настройку файла устройства и добавление операций ввода-вывода для него. http://www.freesoftwaremagazine.com/articles/drivers_linux. Я не знаю точно, как реализовать операцию ioctl, но я думаю, что вы можете просто назначить функцию члену ioctl структуры file_operations. Не уверен, что должна быть сигнатурой функции, но вы, вероятно, могли бы это выяснить, копаясь в исходном коде ядра.

Ответ 4

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

Ответ 5

Я прочитал ваш проект README, и у меня есть следующие замечания. Извините заранее:

Во-первых, уже существует универсальный интерфейс для систем реального времени. Он называется POSIX; конечно, VxWorks, Integrity и QNX совместимы с POSIX, и по моему опыту очень мало проблем с переносимостью, если вы разрабатываете в POSIX API. Является ли POSIX разумным или нет, это другое дело, но это тот, который мы все используем.

[Причина, по которой большинство RTOS являются совместимыми с POSIX, объясняется тем, что один из крупных рынков для них - оборонное оборудование. И DoD в США не позволит вам использовать ОС для своего ИТ-оборудования (например, радаров), если он не совместим с POSIX... Это в значительной степени сделало невозможным сделать RTOS без коммерческого использования без предоставления POSIX]

Во-вторых, сам Linux можно превратить в довольно хорошую ОС реального времени, применив патч PREMPT_RT. Из всех RTOSs это, вероятно, самый лучший на данный момент с точки зрения эффективного использования всех этих многоядерных процессоров. Однако это не совсем такая ОС реального времени, как и другие, поэтому ее quid pro quo.

RTAI использует другой подход, по сути, размещая свои собственные ОСРВ под Linux и делая Linux не более чем одной задачей, выполняемой в ОС. Этот подход в порядке до точки, но большой штраф за RTAI заключается в том, что бит реального времени теперь (насколько я могу судить) не POSIX-совместимый (хотя API выглядит так, как будто они просто застрял rt_ в начале некоторых имен функций POSIX), и взаимодействие с другими вещами теперь, как вы обнаруживаете, довольно сложно.

PREEMPT_RT - гораздо более навязчивый набор патчей, чем RTAI, но окупаемость заключается в том, что все остальное (например, POSIX и valgrind) остается полностью нормальным. Кроме того, доступны приятные вещи, такие как FTrace. Ведение бухгалтерского учета - это случай простого использования существующих инструментов, не имеющих необходимости писать новые. Также похоже, что PREEMPT_RT постепенно проникает в основное ядро ​​Linux. Это сделает другие патч-наборы, такие как RTAI, почти бессмысленным.

Итак, Linux + PREEMPT_RT дает нам POSIX в реальном времени плюс набор инструментов, как и все другие RTOS; общность по всем направлениям. Какой вид звучит как цель вашего проекта.

Я прошу прощения за то, что не помогал с "как" вашего проекта, и я очень нелюбовь от меня спрашивать "почему?". из этого тоже. Но я чувствую, что важно знать, что есть определенные вещи, которые, похоже, сильно перекрываются с тем, что вы пытаетесь сделать. Unseating King POSIX будет сложно.

Ответ 6

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

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

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

Одна вещь, которую вам нужно понять о файловых системах, - это блокировка. Когда вы ищете имя файла, например "foo/bar", ядро ​​каким-то образом получает node для каталога "foo" , затем блокирует его и спрашивает, имеет ли он файл "bar". Код файловой системы как-то находит "бар" , блокирует его, а затем разблокирует "foo" . Протокол блокировки довольно прямолинейный (если вы не выполняете переименование), родитель всегда блокируется до того, как ребенок и ребенок заблокированы до того, как будет выпущена родительская блокировка. Сообщение о поиске для файла - это то, что будет отправлено нашему демону userland, пока каталог все еще заблокирован, когда демон ответил, что ядро ​​начнет сначала блокировать "бар" , а затем разблокировать "foo" .

Я даже не помню симптомы, которые мы отлаживали, но я помню, что проблема не была тривиально воспроизводимой, она требовала часов и часов программ для файловой системы, пока она не проявилась. Но через несколько недель мы выяснили, что происходит. Скажем, что полный путь к нашему файлу был "/a/b/c/foo/bar". Мы находимся в процессе поиска в "баре", что означает, что мы держим замок на "foo" . Демон является обычным процессом пользовательских прав, поэтому некоторые операции, которые он выполняет, могут блокировать и могут быть также выгружены. Он фактически разговаривает по сети, поэтому он может блокироваться в течение длительного времени. Пока мы ждем демона userland, какой-то другой процесс по какой-то причине хочет найти "foo" . Для этого у него есть node для "c" , конечно, заблокирован и просит его искать "foo" . Ему удается найти его и попытаться заблокировать его (его нужно заблокировать, прежде чем мы сможем освободить блокировку на "c" ) и ожидаем, что блокировка на "foo" будет выпущена. Другой процесс приходит к желанию найти "c" , он, конечно же, ждет этого замка, удерживая блокировку на "b". Другой процесс ждет "b" и содержит "a". Еще один процесс требует "a" и содержит блокировку "/".

Это еще не проблема, еще нет. Это иногда случается и с обычными файловыми системами, блокировки могут полностью каскадироваться до корня, вы ждёте некоторое время для медленного диска, диск реагирует, перегрузки уменьшаются, и каждый получает свои блокировки, и все работает нормально. Однако в нашем случае причиной долгого блокировки было то, что удаленный сервер для нашей распределенной файловой системы не ответил. Через X секунд пользовательский демон истечет время и перед тем, как ответить на ядро, что операция поиска на "бар" не удалась, он регистрирует сообщение в syslog с меткой времени. Одна из вещей, которые нужны временной отметке, это информация о часовом поясе, поэтому для ее открытия необходимо открыть "/etc/localtime", для этого нужно начать поиск "/etc", и для этого ему необходимо заблокировать "/". "/" уже заблокирован кем-то другим, поэтому демон пользователя userland ожидает, что кто-то другой откроет "/", пока кто-то еще ждет через цепочку из 5 процессов и блокировок для ответа демона. Система попадает в полный тупик.

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

Ответ 7

Я думал о том, как ядро ​​и пользовательская земля делят вещи напрямую, то есть без стоимости syscall/copyin-out. Одна вещь, которую я помнил, была модель RDMA, где ядро ​​записывает/читает непосредственно из пользовательского пространства, с синхронизацией, конечно. Вы можете изучить эту модель и посмотреть, работает ли она для вашей цели.