Модуль ядра Linux
От: maks1180  
Дата: 04.06.24 20:55
Оценка:
Я новичёк в режиме ядра и я взял пример chrdev модуля Linux, работает нормально.
При чтении устройста данные пользователю отправляет через put_user, в функции
static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset)

если я в этой функции попытаюсь записать или прочитать из buffer, то получаю ошибку в ядре,
хотя я проверил: указатель buffer валидный и он одинаковый и в режиме ядра и в режиме пользователя.
перед вызовом read() в режиме ядра буфер проинициализировал.

Сначала я подумал, что модуль работает не в 0-кольце, но проверил два младших бита регистра CS, всё в порядке, модуль в 0 кольце, программа в 3-м кольце.

В чём может быть дело ? Почему в режиме ядра я не могу напрямую обращаться к памяти пользовательского режима ?
===============================================
(реклама, удалена модератором)
Отредактировано 04.06.2024 20:57 maks1180 . Предыдущая версия .
Re: Модуль ядра Linux
От: aik Австралия  
Дата: 05.06.24 00:07
Оценка:
Здравствуйте, maks1180, Вы писали:

M>Я новичёк в режиме ядра и я взял пример chrdev модуля Linux, работает нормально.

M>При чтении устройста данные пользователю отправляет через put_user, в функции
M>static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset)

Непонятно. device_read есть только в fs/dlm/user.c, но там нет put_user.

M>если я в этой функции попытаюсь записать или прочитать из buffer, то получаю ошибку в ядре,

M>хотя я проверил: указатель buffer валидный и он одинаковый и в режиме ядра и в режиме пользователя.
M>перед вызовом read() в режиме ядра буфер проинициализировал.

Проинициализировал как? Нулями не считается. Запиши туда ненули, а потом что там тебе нужно. Я всю последнюю фразу не понял Это вызов в режиме ядра, или инициализировал в режиме ядра?

M>Сначала я подумал, что модуль работает не в 0-кольце, но проверил два младших бита регистра CS, всё в порядке, модуль в 0 кольце, программа в 3-м кольце.

M>В чём может быть дело ? Почему в режиме ядра я не могу напрямую обращаться к памяти пользовательского режима ?

Можешь, только памяти может не быть на месте (не выделена ещё или в свопе), и тогда будет page fault, который окей если это пользовательская память (тогда ядро вытащит страницу из свопа или наконец выделит память, и повторит чтение), или не окей, если это память самого ядра — тогда будет краш. Эти все put_user() — они чтоб ядро различало чья это память. Можно попробовать перед чтением вызвать get_user_pages_fast() (а потом не забыть put_page()).
Отредактировано 05.06.2024 0:13 aik . Предыдущая версия .
Re[2]: Модуль ядра Linux
От: Pzz Россия https://github.com/alexpevzner
Дата: 05.06.24 01:02
Оценка:
Здравствуйте, aik, Вы писали:

aik>Можешь, только памяти может не быть на месте (не выделена ещё или в свопе), и тогда будет page fault, который окей если это пользовательская память (тогда ядро вытащит страницу из свопа или наконец выделит память, и повторит чтение), или не окей, если это память самого ядра — тогда будет краш. Эти все put_user() — они чтоб ядро различало чья это память. Можно попробовать перед чтением вызвать get_user_pages_fast() (а потом не забыть put_page()).


put_user() — это такой специальный memcpy, у которого адрес начала и конца кода лежит в специальной отдельной секции. И если происходит исключение, связанное с обращением к памяти, ядро проверяет, часом не из put_user() ли оно прилетело. И если оттуда, относится к нему особым образом.
Re[3]: Модуль ядра Linux
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.06.24 10:51
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>если происходит исключение, связанное с обращением к памяти, ядро проверяет


А самостоятельно обработать исключение, как в винде, модуль ядра может?
Re[4]: Модуль ядра Linux
От: Pzz Россия https://github.com/alexpevzner
Дата: 05.06.24 10:55
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

Pzz>>если происходит исключение, связанное с обращением к памяти, ядро проверяет


ЕМ>А самостоятельно обработать исключение, как в винде, модуль ядра может?


Не знаю. Вроде нет. Никогда не было причин разбираться.
Re[5]: Модуль ядра Linux
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.06.24 11:12
Оценка:
Здравствуйте, Pzz, Вы писали:

ЕМ>>А самостоятельно обработать исключение, как в винде, модуль ядра может?


Pzz>Не знаю. Вроде нет. Никогда не было причин разбираться.


Когда процесс обменивается с модулем ядра относительно небольшими порциями данных, нет проблем использовать промежуточную буферизацию. А когда идет какой-нибудь аудио/видеопоток, уже разбитый на достаточно большие порции, и нужно его не просто копировать, а разбирать/формировать на ходу, то промежуточное порционное копирование может заметно усложнить обработку. Возможность перехватить исключение процессора тут довольно удобна.
Re[6]: Модуль ядра Linux
От: Pzz Россия https://github.com/alexpevzner
Дата: 05.06.24 11:22
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

Pzz>>Не знаю. Вроде нет. Никогда не было причин разбираться.


ЕМ>Когда процесс обменивается с модулем ядра относительно небольшими порциями данных, нет проблем использовать промежуточную буферизацию. А когда идет какой-нибудь аудио/видеопоток, уже разбитый на достаточно большие порции, и нужно его не просто копировать, а разбирать/формировать на ходу, то промежуточное порционное копирование может заметно усложнить обработку. Возможность перехватить исключение процессора тут довольно удобна.


В UNIX когда процесс делает write(), он переходит в режим ядра, оставаясь на контексте процесса, и адрес буфера, переданный write(), остается валидным в режиме ядра (с той только оговоркой, что переданный адрес может и в user space быть невалидным, и в отличии от процесса, ядру при этом надо не падать, а возвращать ошибку).

Я не очень понимаю, как возможность обработки исключений в драйвере избавляет от копирований. Во-первых, пользовательская память (ее адреса) привязаны к процессу и перестают быть валидными при переключении текущего процесса. Т.е., надо или лочить процесс, или переходить в физические адреса (т.е., постоянно надрючивать memory mapping, а это очень дорого на современных CPU) или все равно копировать.

Во-вторых, самые "агрессивные" потребители больших потоков данных, а именно, сеть и дисковая подсистема, в любом случае вынуждены делать это лишнее копирование. На их фоне видео не создает столько потока, и вообще, обработка видеопотока — штука тяжелая, и мне не очень верится, что копирование вносит заметную лепту в эту нагрузку, а аудиопоток, по сравнению с другими, оперирует совсем уж какими-то копеешными объемами данных.
Re[7]: Модуль ядра Linux
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.06.24 11:48
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>В UNIX когда процесс делает write(), он переходит в режим ядра, оставаясь на контексте процесса, и адрес буфера, переданный write(), остается валидным в режиме ядра (с той только оговоркой, что переданный адрес может и в user space быть невалидным, и в отличии от процесса, ядру при этом надо не падать, а возвращать ошибку).


Именно.

Pzz>Я не очень понимаю, как возможность обработки исключений в драйвере избавляет от копирований.


Полностью от копирований оно не избавляет, конечно. Если драйверу нужно тупо передать порцию данных туда-сюда, то проще воспользоваться копированием. Если же, например, от процесса идет поток данных сложной структуры (тот же видео/аудиопоток), его может быть нужно раскладывать по разным областям памяти устройства, или особым образом обрабатывать некоторые кадры. Возможность делать это, имея непосредственный доступ к памяти процесса, может заметно облегчить/ускорить обработку.

Pzz>пользовательская память (ее адреса) привязаны к процессу и перестают быть валидными при переключении текущего процесса. Т.е., надо или лочить процесс, или переходить в физические адреса


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

Pzz>обработка видеопотока — штука тяжелая


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

Pzz>мне не очень верится, что копирование вносит заметную лепту в эту нагрузку, а аудиопоток, по сравнению с другими, оперирует совсем уж какими-то копеешными объемами данных.


Такая ситуация была не всегда, На заре и линуксов, и unix'ов вообще, почти любая поточная передача в реальном времени считалась серьезной нагрузкой, и ее оптимизировали, как могли. Все эти схемы взаимодействия процесса с драйвером появились еще тогда.
Re[6]: Модуль ядра Linux
От: aik Австралия  
Дата: 05.06.24 11:51
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Когда процесс обменивается с модулем ядра относительно небольшими порциями данных, нет проблем использовать промежуточную буферизацию. А когда идет какой-нибудь аудио/видеопоток, уже разбитый на достаточно большие порции, и нужно его не просто копировать, а разбирать/формировать на ходу, то промежуточное порционное копирование может заметно усложнить обработку. Возможность перехватить исключение процессора тут довольно удобна.


Процесс же может замапить память из драйвера и писать/читать оттуда/туда. Драйвер вызовут для mmap(), он там выделит и замапит что надо для DMA и привет — ни исключений, ни копирований. Или драйвер подпишется на page fault, если сразу не захочет всё выделять.
Re[8]: Модуль ядра Linux
От: Pzz Россия https://github.com/alexpevzner
Дата: 05.06.24 13:38
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

Pzz>>Я не очень понимаю, как возможность обработки исключений в драйвере избавляет от копирований.


ЕМ>Полностью от копирований оно не избавляет, конечно. Если драйверу нужно тупо передать порцию данных туда-сюда, то проще воспользоваться копированием. Если же, например, от процесса идет поток данных сложной структуры (тот же видео/аудиопоток), его может быть нужно раскладывать по разным областям памяти устройства, или особым образом обрабатывать некоторые кадры. Возможность делать это, имея непосредственный доступ к памяти процесса, может заметно облегчить/ускорить обработку.


Ну и пожалуйста. В UNIX на контексте условного write() все то же самое.

Или хочется прям по структурам в пользовательской памяти указателем полазить? IMHO, это — стрёмная практика.

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


Ты не можешь просто так взять и зафиксировать нужную область памяти, при переключении контекстов она просто выйдет из области видимости.

Но пока ты на контексте системного вызова, ты остаешься на контексте вызвавшего процесса.

Pzz>>обработка видеопотока — штука тяжелая


ЕМ>Если математическая, то да. Но нередко нужно не преобразовывать картинку, а только перегруппировать данные в потоке.


Все равно заголовки всякие там разбирать. Дело небыстрое.

Pzz>>мне не очень верится, что копирование вносит заметную лепту в эту нагрузку, а аудиопоток, по сравнению с другими, оперирует совсем уж какими-то копеешными объемами данных.


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


Знаешь, на заре UNIX-ов вообще (а так же Windows NT вообще), UNIX-овская простая и уютная, как домашние тапочки, модель ввода-вывода была прям ощутимо эффективнее вендовой.

Все эти IRP-ы и MDL-и, ну, они ребята недешевые.

Сейчас, конечно, появились всякие там более навороченные механизмы, про которые я не очень в курсе, потому, что давно драйверами не занимался. Но они факультативные, простому советскому драйверу про них знать не обязательно. Они становятся важными, когда у тебя железо ввода-вывода умеет прямой доступ прямо в пользовательскую память, чтобы совсем уже избежать копирования. Но это совсем не всякое железо так умеет.
Re[7]: Модуль ядра Linux
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.06.24 20:54
Оценка:
Здравствуйте, aik, Вы писали:

aik>Процесс же может замапить память из драйвера и писать/читать оттуда/туда.


Это используется прежде всего для асинхронного обмена. При синхронном обычно удобнее использовать обычную память.

aik>Или драйвер подпишется на page fault


Вопрос о том и был, может ли ядерный модуль в линуксе подписаться на обработку исключений.
Re[9]: Модуль ядра Linux
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.06.24 21:14
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>В UNIX на контексте условного write() все то же самое.


Каким образом контекст, как таковой, может обеспечить такое? В нем непременно есть точка входа по исключению?

Pzz>Или хочется прям по структурам в пользовательской памяти указателем полазить?


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

Pzz>IMHO, это — стрёмная практика.


Почему вдруг?

Pzz>Ты не можешь просто так взять и зафиксировать нужную область памяти, при переключении контекстов она просто выйдет из области видимости.

Pzz>Но пока ты на контексте системного вызова, ты остаешься на контексте вызвавшего процесса.

Ну вот в это время, пока драйвер обрабатывает вызов, и не выполнил явно возврата/переключения, система может принудительно переключить контекст?

Pzz>Все равно заголовки всякие там разбирать. Дело небыстрое.


Какие там сложные заголовки в самом-то видеопотоке? Это в файле может лежать много дополнительной информации, а в самом потоке — только то, что нужно для расшифровки кадров. Если расшифровка делается программно, то дополнительное копирование действительно не критично. Если же аппаратно, то вполне может ощущаться, особенно вкупе с динамическим выделением/освобождением памяти.

Pzz>на заре UNIX-ов вообще (а так же Windows NT вообще), UNIX-овская простая и уютная, как домашние тапочки, модель ввода-вывода была прям ощутимо эффективнее вендовой.


Только на простых и линейных моделях, работавших в доверенном окружении, где все свои. А так, чтоб много параллельных процессов работало с нетривиальными моделями ввода/вывода, и при этом никто не хотел доверять другим? А если при этом нужно было встроить дополнительную обработку куда-нибудь в середину стека, а не банально поверх него?

Pzz>Все эти IRP-ы и MDL-и, ну, они ребята недешевые.


Так они все изначально про параллелизм, управляемые очереди, разделение памяти, разделение функций, безопасность, ортогональность и прочее, а родная модель ввода/вывода Unix — только про примитивный последовательный/блочный обмен.
Re[6]: Модуль ядра Linux
От: ononim  
Дата: 06.06.24 19:44
Оценка:
ЕМ>Когда процесс обменивается с модулем ядра относительно небольшими порциями данных, нет проблем использовать промежуточную буферизацию. А когда идет какой-нибудь аудио/видеопоток, уже разбитый на достаточно большие порции, и нужно его не просто копировать, а разбирать/формировать на ходу, то промежуточное порционное копирование может заметно усложнить обработку. Возможность перехватить исключение процессора тут довольно удобна.

Можно промапить юзерские страницы в kernelspace, чтоб с ними точно ничего не произошло
Linux: https://stackoverflow.com/questions/63440519/understanding-kmap-on-64-bit-linux
Windows: https://github.com/microsoft/Windows-driver-samples/blob/main/general/ioctl/wdm/sys/sioctl.c#L441
Как много веселых ребят, и все делают велосипед...
Re: Модуль ядра Linux
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 09.06.24 04:54
Оценка: +1
Здравствуйте, maks1180, Вы писали:

M>если я в этой функции попытаюсь записать или прочитать из buffer, то получаю ошибку в ядре,


— Доктор, со мной что-то происходит.
— Ну выпейте что-то, больной.

Что за ошибка? Тип (код ошибки, крэш, что-то другое)?

Может, прочтёте smart-questions перед отправкой вопроса? Оно тут на сайте продублировано.
The God is real, unless declared integer.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.