Многопоточный DeviceIOControl для KMDF

Прежде чем я перейду к своему вопросу, я расскажу, над чем я сейчас работаю, чтобы у вас было хорошее представление о том, что я уже сделал/пробовал.

У меня есть многопоточное пользовательское приложение Windows Desktop, которое отправляет вызовы DeviceIOControl драйверу KMDF (чисто программное обеспечение, без оборудования). Существует 5 отдельных потоков, которые постоянно выполняют один и тот же пользовательский вызов IOCTL к драйверу. Этот запрос состоит из:

  1. PsLookupProcessByProcessId, чтобы процесс считывал память.
  2. MmCopyVirtualMemory для копирования запрошенной памяти в предоставленный буфер.
  3. ObDereferenceObject для уменьшения счетчика ссылок.

Драйвер в настоящее время делает это последовательно, и основным узким местом в моем приложении пользовательского режима является ожидание завершения чтения памяти, и все должно быть завершено, прежде чем сцена может быть «отрисована».

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


person ryanbowen    schedule 07.02.2017    source источник
comment
@HarryJohnston Внутри каждого потока операции чтения зависят от предыдущих, чтобы возвращать правильные данные. Однако сами потоки не зависят друг от друга. Если я открою файл как OVERLAPPED и внутри моей функции управления устройством ожидаю завершения IOCTL или истечения времени ожидания, я все равно увижу прирост производительности из-за того, что драйвер обрабатывает 5 потоков не последовательно? По сути, я спрашиваю, с перекрывающимся вводом-выводом, это эквивалент одного экземпляра моего драйвера, обрабатывающего каждый поток, или это все еще один драйвер, получающий массу запросов от 5 потоков и изо всех сил пытающийся не отставать?   -  person ryanbowen    schedule 08.02.2017
comment
@HarryJohnston Извините, мне трудно найти правильные слова, чтобы выразить свои вопросы. Я искал повсюду, пытаясь выяснить, что именно открытие файла как перекрывающегося на самом деле меняет в отношении того, как WDF обрабатывает запросы IOCTL, но все, что я нашел, это реализации. Порождает ли каждый запрос поток (или эквивалент), поэтому запросы обрабатываются параллельно в моем драйвере, или диспетчеризация моего драйвера просто последовательно работает с очередью, и я должен реализовать распараллеливание чтений?   -  person ryanbowen    schedule 09.02.2017
comment
Хорошо, забудьте большинство моих предыдущих комментариев - я не совсем понял, что вы используете WDF, поэтому запросы ввода-вывода помещаются в очередь, а не отправляются непосредственно в ваш код. Виноват; Извините.   -  person Harry Johnston    schedule 09.02.2017
comment
The driver is currently doing this serially - так ты используешь WdfIoQueueDispatchSequential ? почему не WdfIoQueueDispatchParallel ?   -  person RbMm    schedule 09.02.2017
comment
как я понимаю, пользовательский режим должен создавать файл на вашем устройстве с флагом FILE_FLAG_OVERLAPPED (или без FILE_SYNCHRONOUS_IO_[NO]NALERT). водитель со своей стороны должен использовать тип очереди WdfIoQueueDispatchParallel   -  person RbMm    schedule 09.02.2017


Ответы (2)


Хорошо, похоже, самая важная часть вашего вопроса здесь:

Я искал повсюду, пытаясь выяснить, что именно открытие файла как перекрывающегося на самом деле меняет в отношении того, как WDF обрабатывает запросы IOCTL [...]

Это ничего не меняет; все запросы к драйверам устройств являются асинхронными.

Когда вы выполняете ввод-вывод на синхронном дескрипторе, Windows отправляет драйверу асинхронный запрос ввода-вывода от вашего имени и ожидает его завершения. Насколько мне известно, у драйвера даже нет способа определить, был ли первоначальный запрос синхронным или перекрывающимся. [Редактировать: это не совсем так. Как указывает RbMm в комментариях, ядро ​​на самом деле проводит различие между синхронным и асинхронным вводом-выводом, но с практической точки зрения это не должно иметь для вас значения.]

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

Я не знаком с WDF, но запись MSDN Методы диспетчеризации запросов ввода-вывода выглядят уместно.

person Harry Johnston    schedule 08.02.2017
comment
действительно драйвер может определить, является ли операция синхронной - IoIsOperationSynchronous - person RbMm; 09.02.2017
comment
@RbMm: отмечено; Спасибо. У вас есть идеи, почему водитель может различать эти два случая? Какая-то оптимизация, наверное? - person Harry Johnston; 09.02.2017
comment
это используется главным образом драйверами файловой системы. посмотри fastfat пример. например FS Обновить текущую позицию в файле, только если операция синхронная - person RbMm; 09.02.2017
comment
но основная причина, по которой драйверу требуется определить тип операции синхронизации/асинхронности, - если поток может заблокироваться для ввода-вывода или ожидания ресурса - но обычно это используется только достаточно сложными драйверами - person RbMm; 09.02.2017
comment
Call the common set routine, with blocking allowed if synchronous - если операция synchronous - клиент все равно будет где-то заблокирован до тех пор, пока запрос не завершится. так что водитель может ждать себя. если операция асинхронная, рекомендуется не блокировать клиент. если мы не можем выполнить просто - нужно сохранить запрос и вернуть STATUS_PENDIG. и завершить его позже. но все это для достаточно сложных случаев - person RbMm; 09.02.2017

во-первых очень важно в каком пользовательском режиме открывать файл - в синхронном или асинхронном режиме? (FILE_FLAG_OVERLAPPED для CreateFile или FILE_SYNCHRONOUS_IO_[NO]NALERT для ZwOpenFile или ZwCreateFile)

если файл открыт в синхронном режиме (FO_SYNCHRONOUS_IO будет в FILE_OBJECT.Flags), подсистема ввода-вывода сериализует все запросы в файл, поэтому новый запрос не отправляется на ваше устройство, пока предыдущий не будет завершен. с асинхронным файловым объектом - такого ограничения нет - запрос (IRP) будет просто отправлен на ваше устройство

если ты скажешь это

Однако сами потоки независимы друг от друга.

вам нужно открыть файл как асинхронный (с FILE_FLAG_OVERLAPPED), если потоки совместно используют один дескриптор файла (FILE_OBJECT), или каждый поток должен открывать собственный частный файл на вашем устройстве. думаю лучше общий асинхронный файл.

со стороны водителя вы должны использовать тип отправки очереди WdfIoQueueDispatchParallel. поэтому просто возьмите запрос (IRP), обработайте его и завершите (как я понимаю, вы не отправляете этот запрос другому драйверу или не ставите в какую-то другую очередь)

По сути, я спрашиваю, с перекрывающимся вводом-выводом, является ли это эквивалентом одного экземпляра моего драйвера, обрабатывающего каждый поток, или это все еще один драйвер, получающий массу запросов от 5 потоков и изо всех сил пытающийся не отставать?

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

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

person RbMm    schedule 09.02.2017