Нижеприведённый код ожидает готовность жёсткого диска выполнять команды. Проблема в том, что этот участок кода вызывает 100% загрузку процессора. Вызов функции Sleep в фазе ожидания, проблему загрузки CPU решает, но появляется другая - замедление работы программы из-за того, что эта функция даёт задержку не менее 10 мс, что в данном случае критично , т.к. жёсткий диск отрабатыват команды менее чем а 5 мс. Требуется HELP!
Tropez ничего себе это ты открыл доступ к портам и из юзермода управляешь диском? - так делать полюбому нельзы, особенно в сочетании с командой sleep, которая принудительно передаёт управление другим потокам/процессам и они рано или поздно законфликтят твою команду в порту и твоя прога натворит дел на диске
Так я пробовал. В таком виде, загрузка проца врё равно 100%, с той разницей, что 50% занимает время ядра операционки (судя по диспетчеру задач)
С этого места подробнее. Не совсем понимаю, что подразумевается под "законфликтят" и как моя прога начнёт делать не то, что я в неё заложил.
Делаю сейчас нечто подобно, только из кернела. Очень много проблем, которые не возникли бы в реальном режиме. Самые критичные: 1) Если читать в PIO то бывает, что отправил команду, ожидаешь данные, а они уже прочитаны виндой... В DMA такого не замечал. 2) Если чтение работает - посмотри EventLog - увидишь кучу ошибок от atapi.sys/disk.sys, хотя ты свою инфу получаешь. В результате ошибок винда сама переходит из DMA в PIO, хотя в настройках канала стоит "По возможности использовать DMA". Мне не удается вернуть обратно DMA. Из-за этого резко падает производительность - приходится восстанавливаться из образа Много чего полезного можно прочитать здесь http://www.wasm.ru/forum/viewtopic.php?id=29217
Многозадачные ОС рассчитаны на то, что прикладные программы не будут напрямую общаться с железом, а делать это будет ОС. Ты фактически это нарушаешь, проблем не оберешься. Конфликт твоей программы и драйверов диска обеспечен из-за совместного использования напрямую одного и того же оборудования. Проблемы будут начиная с того, что хард запутается что ему делать - то, что ты говоришь или то, что винда говорит, и заканчивая потерей данных. Если очень надо, код стоит выполнять в драйвере режима ядра на IRQL не ниже 2 (Dispatch), исключая переключение потоков (разумеется, винда в это время будет мертво висеть, пока ты не закончишь свои дела) и перехватив обработчик IRQ от контроллера. Да и то я не уверен, что всё учел
В данном случае с этим будут определенные проблемы. Если к IDT подключаться через IoConnectInterrupt, то уже подключенный atapi.sys будет перехватывать и обрабатывать твои запросы. Если напрямую патчить IDT, то это подпадает под PatchGuard. Если я все правильно понимаю, то единтвенное решение запретить прерывания от диска на время своей работы. Кстати у меня вопрос по многопроцессорности. В каждом процессоре есть своя IDT. Я предполагаю, что для всех процессоров IDT одинакова. Соответственно если приходит IRQ от устройства, то обработчик должен будет вызваться столько раз, сколько процессоров. Где это разруливается - на уровне винды или еще где-то (например на APIC).
В моём случае, кофликты с другими драйверами/программами исключены т.к. контроллер через который я общаюсь с диском не зарегистрирован в операционной системе (не стоят никакие драйвера) и диск ни как логический ни как физический в системе естесственно не присутствует. Возвращаюсь к вопросу: как передать управление другим потокам/процессам подобно функции Sleep, но на время менее 10 миллисекунд ?
Не годится. Это то же самое, что и Sleep(0). Так я пробовал. В таком виде, загрузка проца врё равно 100%, с той разницей, что 50% занимает время ядра операционки (судя по диспетчеру задач)
А другие готовые потоки то есть? Если нету тогда загрузка 100% очевидна и вполне нормальна, если больше некому отдавать управление
Абсолютное заблуждение. Каждый процессор выполняет свою задачу. У каждого процессора есть своя IDT. Куда придёт прерывание от устройства разрешается путём конфигурирования Apic. Прерывание должен обработать именно тот процессор, на вход которого оно поступило.
Я могу ошибаться, но из информации,которую я нашёл в сети, в системах класса NT, есть процесс "Бездействие системы", который выполняет команды HLT, с целью охладить процессор. Очень хочется передавать управление ему. А вообще мне всё равно, какому процессу я отдам управление. Мне важно, чтобы мой процесс, который фактически ничего не выполняет а ждёт событие, не занимал ресурс процессора.
Tropez "Передать управление ему" можно либо до возникновения какого-либо события (например, WaitForSingleObject), либо на определённое время (например, Sleep). Т.к. Вы не можете указать ни время, ни хэндл объекта, сигнализацию которого ожидать, (а откуда тогда системе знать, когда выделять Вашему потоку процессорное время?) Вам остаётся только отдавать управление другим ожидающим потокам (SwitchToThread). Т.о. Ваш процесс будет выполнять функцию процесса "Бездействие системы".
Tropez Можно сделать задержку (WaitForSingleObject) на ждущем таймере (CreateWaitableTimer+SetWaitableTimer) или мультимедийном (timeSetEvent) предварительно увеличив разрешение timeBeginPeriod(1)
Как уже правильно сказал l_inc - ты не можешь ему отдать управление. В статье (которую на днях должен выложить Aquila, надо его пнуть посильнее ) описано подробно как потоки получают и теряют управление. Действительно есть безымянный процесс, по-разному именуемый в разных менеджерах задач, в виндовом называется System Idle Process (Бездействие системы в русской версии), который содержит по одному потоку на каждый логический процессор в системе. Соответствующий поток получает управление когда нет НИ одного готового к выполнению потока для данного процессора (под готовым имеется в виду Thread->State == Ready). Твой же поток всегда будет готовым либо выполняющимся (State == Ready или Running) если у тебя цикл. Преждевременно отдать свой квант можно либо функциями ожидания объекта (WaitFor****), либо безусловным ожиданием (Sleep, SleepEx), либо просто завершить квант функцией SwitchToThread (ZwYieldExecution). Ожидать объект ты не можешь, поскольку тебе нечего ждать. Ожидать по таймауту можно в принципе, если укажешь разумный таймаут. Соответственно подели время выполнение кода на полное время цикла и узнаешь сколько будет отнимать % кванта процессорного времени твой поток. Делать ZwYieldExecution (SwitchToThread) смысла мало - твой поток всегда будет Ready/Running, разница лишь в том, что иногда он будет делиться квантом с другими потоками. Но это и так будет происходить, поскольку Windows по таймеру сама заберет у тебя твой квант, если будешь слишком "жадным". Получаем следующее - WaitFor*** ничего не даст, SwitchToThread чтото даст, но почти незаметно. Sleep - даст то, что ты хочешь, если укажешь разумный таймаут, который не нарушит твоего алгоритма
PS. Поточнее задать время можно прямым вызовом ZwDelayExecution (в сотнях наносекунд) вместо Sleep(Ex), которая является оберткой для нее
что впрочем отнюдь не гарантирует, что этот уточнённый запрос будет уточнённо исполняться ведь timeBeginPeriod на которую опирается Sleep (а значит и её "внутренность" тоже) задаётся в миллисекундах, и те не гарантированы
Само собой, я про теоритическое значение. Ну а какая уж точность там получится фиг его знает) не проверял никогда.