Мое почтение всем. Встретил в одном исходнике такой вызов: Код (Text): status = KeWaitForSingleObject(&Mutex, Executive, KemelMode, FALSE, NULL); Этот код может выполняться на DISPATCH_LEVEL. А в MSDN сказано: Вопрос в том, насколько легитимен такой вызов. И если нет, то как решать проблему разгрузки буфера, заполняющегося на DISPATCH_LEVEL? Заранее благодарен.
Так как у вас в режиме ожидания указан режим верблюда, такой вызов по идее легитимен. Объяснение и оговорки тут: http://msdn.microsoft.com/en-us/lib...N-US&k=k(KESETEVENT);k(DevLang-"C++")&rd=true
Ага, спасибо, документ полезный. Но это не совсем ответ на вопрос. Поясню: мне не понятно, почему ОС запрещает ждать вечно на объекте диспетчеризации на высоком IRQL. Обойти это ограничение можно, задав огромный интервал ожидания, например, 0x80000000`00000000. Это почти как передать NULL вместо Timeout. Но каков кармический смысл запрета не совсем понятно.
Во-первых, на irql = 2 и Timeout = NULL в режиме ядерного ожидания никогда не будут доставлены APC, таким образом, поток имеет шансы стать зомби. Никто же не знает логику твоего драйвера, выйдет он из режима ожидания вообще или нет? Если нет, то будет плохо, особенно в случае arbitrary-потока (об этом тоже оговорка в документации, кстати), т.е. текущего потока, который заранее неизвестен. По этой же причине нельзя вызывать ожидание с ненулевым таймаутом из DPC-функции, упадёт сразу (attempt to switch from dpc, или как его там). Кстати, драйвер тот, о котором речь, скорее всего тупо бажный, т.к. Verifier такую ситуацию поймает по-любому. В общем, не надо так делать, если не понимаешь точно, зачем это тебе. Я вижу одну только ситуацию, когда это теоретически можно делать: когда вызываешь ожидание с ненулевым таймаутом на irql = 2 в заранее известном потоке твоего приложения, но опять же Verifier такое поймает, потому что про твою логику ничего не знает.
Ну, суть происходящего довольно проста: буфер заполняется в completion routine. Содержимое буфера надо сохранить в файл. Для этого создается рабочий поток, который чистит буфер, копируя его содержимое в другое место, откуда оно потом записывается в файл. Буфер защищен мьютексом, собственно на него и вешается этот KeWaitForSingleObject. Я решил, сделать немного по-другому, оформив синхронихазию с помощью KEVENT'а. Когда буфер заполняется, вызывается рабочий поток, completion routine спит на KEVENT. Когда рабочий поток копирует содержимое буфера, он сигнализирует об этом, и completion routine продолжает выполнение. Мда, вообще изначально хотел сделать два события, но пока писал предыдущий абзац, понял, что достаточно одного и работать будет. Все-таки форум полезная штука .
Это фильтр у тебя? Функция завершения вызывается на irql=2? И где вызывается у тебя KeWaitForSingleObject()?
Фильтр. Схематично выглядит так: Код (Text): CompletionRoutine: ... if (BufferFull) { IoQueueWorkItem(Critical); KeWaitForSingleObject(Event, ...); } ... WorkItem: { CopyDataToAnotherBuffer(); KeSetEvent(Event); WriteDataFromAnotherBufferToFile(); }
Фильтр чего? Нижележащий драйвер может завершать запросы на irql=2? В общем случае, так нельзя делать (да и не за чем), для этого уже давно существуют встроенные механизмы. Тебе следует запустить рабочий поток и вернуть STATUS_MORE_PROCESSING_REQUIRED из функции CompletionRoutine(), затем в функции WorkItem(), после копирования данных, возобновить завершение запроса вызовом IoCompleteRequest().
Клавиатуры. Насколько я понимаю, да. Можно чуть более детально, почему так нельзя делать в общем случае? Что может случиться? Беглым поиском нашел кое-что об этом: 1, 2. Что еще стоит почитать по этому вопросу?
izl3sa Ага, это в планах. Просто сейчас другую книгу читаю и хочу закончить, не слишком отвлекаясь на остальные книги. А Они -- это большая тема. Поэтому хочется построить некую модель в голове, которую я смогу использовать, пусть пока упрощенную и не очень детальную. Хотя, по-моему с драйверами этои номер не пройдет . Поэтому по драйверам больше пока пробавляюсь статьями.
Ну вот давай рассмотрим твой пример с клавиатурой. Ты представляешь, как система работает с клавиатурой? Ядерная оконная подсистема открывает девайсы клавиатур и шлёт каждому запрос IRP_MJ_READ, который откладывается в драйвере клавиатуры и не завершается до тех пор, пока не придёт прерывание от железки (т.н. модель inverted calls). Далее, прерывание обрабатывается, затем, чтобы передать данные клиенту, шедулиться DPC, исполняемая на irql=2, в которой и происходит завершение запроса. На самом деле, эта DPC могла бы зашедулить Work Item, предоставив таким образом фильтрам возможность обработки входных данных с клавиатуры на irql=0, но зачем? Это привело бы к падению производительности, чего нельзя допускать для таких девайсов. Следовательно, функция завершения твоего клавиатурного фильтра почти всегда будет завершаться на irql=2, если только какой-нибудь нижележащий фильтр не окажется вдруг настолько тупым, что будет откладывать обработку на irql=0. Вот и получается, что ты вызываешь функцию ожидания на irql=2, а чем это плохо, я уже писал выше. Это - основное, ещё у Уолтера Они это описано хорошо.
x64 Да, это я как раз знал. Насколько я понял, отсылкой IRP_MJ_READ клавиатурам занимается специально выделенный для этой цели поток, ну а про схему "вызов IRP_MJ_READ верхнего устройства/сон в ожидании события от клавиатуры в самом нижнем устройстве/возврат значения с помощью completion routine" читал в книге. Да, это понятно. Но мне кажется, что если буфер содержит, допустим, место для 1024х нажатий, то сон будет только каждые 1024 нажатия. Учитывая, что с т.з. компьютерного времени клавиатура -- очень медленное устройство, это не должно чувствоваться. Понятно, что, например, для быстрого сетевого устройства такой подход неприменим. Кроме того, я планировал сделать буфер только на одно нажатие, чтобы сон и запись на диск случались каждый раз. Мне просто интересно посмотреть, будет тормозить или нет. Как набью код -- проверю.