Работа с портами под Win95, обзор и теория
Вступление
Предполагалось, что данный материал будет небольшой лекцией, посвящающей вас
в технологию работы с последовательными портами под различными платформами. Но
это не так, поскольку материала для этой лекции хватит на десятка два советов.
Поэтому коротенько так, кратенько, небольшое вступление... Проблема
Под MS-DOS приложение управляет всем компьютером. Это развязывало
программисту руки. Достижение максимальной скорости работы осуществлялось
непосредственным доступом к аппаратным средствам.
Под Windows 3.x эта свобода отчасти была ограничена. К примеру вы уже не
имели полный доступ к экрану. Проблема объясняется легко: с тех пор, как
пользователь мог запускать любое количество приложений, не было никакой
гарантии, что приложения не получали одновременно те же самые аппаратные
средства.
Другая проблема - вы уже должны были считаться с параллельно запущенными
задачами, а не требовать у компьютера в свое распоряжение все ресурсы. Win 3.x
осуществляет кооперацию параллельных задач, означая, что каждое приложение
должно исходить из концепции совместного существования и не монополизировать
ресурсы, а пользоваться услугами специализированного диспетчера. Захват CPU на
длительное время здесь не приветствуется.
Но тем не менее монополизированный доступ к аппаратным средствам также
возможен, но вся ответственность за работу других приложений ложится на
программиста. Получается борьба вашего приложения с системой: если вы
захватываете все рабочее время CPU, контроль над портами или работу с памятью,
то система милостиво ждет, пока вы не отдадите бразды правления в ее руки, при
этом другие приложения (если они не успели это сделать до вас) могут ругаться,
выплевывать на экран грязные ругательства и пугать не в чем не повинного
пользователя.
Факт, но тенденция отбивания рук от прямого доступа к железу победила на
платформе Win32 (Windows NT и Windows 95). Это операционные системы с истинной
многозадачностью. Каждый поток (выполняемый модуль) получает определенный квант
процессорного времени. Когда лимит процессорного времени исчерпан, или
появляется поток с более высоким приоритетом, система прекращает обслуживать
первый поток, даже в случае, если он не завершен. Это переключение между
потоками может произойти между двумя ассемблерными инструкциями, нет никакой
гарантии, что поток сможет завершить определенное количество инструкций, прежде
чем у него отнимут процессорное время, к тому же неизвестно как долго ждать
следующей порции процессорного времени.
Это приводит к проблеме с прямым доступом к аппаратным средствам. Например,
типичное чтение из порта формируется из нескольких ассемблерных инструкций:
mov dx, AddressPort
mov al, Address
out dx, al
jmp Wait
Wait:
mov dx, DataPort
in al, dx
|
Состояние всех регистров при переключении потоков сохраняется, состояние I/O
портов (последовательные порты, порты ввода/вывода) - нет. Так, велика
вероятность что другие приложения производят другие операции с I/O портом, в то
время как вы "застряли" между инструкциями 'out' и 'in'. Документированный путь
Для решения этой проблемы мы должны как-то сообщить всем другим приложениям,
что "К настоящему времени MyProg использует порт 546, и всем оставаться на своих
местах до моего особого распоряжения." В этом случае подешел бы мьютекс. К
сожалению, для использования созданного мьютекса все приложения должны знать его
имя. Но даже если бы это было возможно, вы легко можете наткнуться на другие
заковыристые проблемы. Рассмотрим два приложения - App1 и App2. Оба пытаются
выполнить вышеприведенный код. К несчастью, они созданы разными программистами с
разным взглядом на технологию доступа, поэтому App1 сначала требует
AddressPortMutex, в то время как App2 требует DataPortMutex. И, по печальному
совпадению, когда App1 получает AddressPortMutex, система переключается на App2,
которое захватывает DataPortMutex и получается праздник смертельного объятия.
App2 не может получить адрес порта, т.к. его захватило App1. App1 не может
получить данные порта, т.к. это захватило App2. И все чего-то ждут...
Правильное решение - создание драйвера устройства, которой единолично владеет
портами/памятью. Доступ к аппаратным средствам осуществляется посредством API.
Вот типичный вызов:
GetIOPortData(AddressPort, DataPort : word) : Byte;
|
GetIOPortData сначала создает мьютекс, который защищает от вторжения
(возможно все) порты, затем дает доступ к портам и, наконец, уничтожает его
перед возвратом в вызвавшему функцию оператору. В случае, когда функцию пытаются
вызвать несколько потоков, управление получает только один, остальные в это
время ждут.
Создание драйвера устройства дело нелегкое. Он должен быть создать с помощью
ассемблера или C и невероятно труден в отладке. Более того, из-за соображений
безопасности драйверы устройств для Windows 95 (VxD) не совместимы с драйверами
для Windows NT (VDD, virtual device driver - виртуальный драйвер устройства).
Говорят, что в будущих версиях они будут совместимы, и Windows NT 6.0 и Windows
2000 будут использовать одни и те же драйвера, но пока разработчики вынуждены
заниматься созданием двух различных версий.
Для получения более подробной информации рекомендую обратиться к следующим
ресурсам:
Microsoft Windows 95 Device Driver Kit
Microsoft Windows NT Device Driver Kit
Microsoft Press "Systems Programming for Windows 95" автора Walter Oney
Также вы можете ознакомиться с библиотекой Vireo VtoolsD на предмет написания
VxD в C, расположенной по адресу http://www.vireo.com. Недокументированный путь
Вышеуказанная проблема не слишком реальна. Приложение, которое имеет
непосредственный доступ к аппаратным средствам, обычно использует некоторые
специализированные аппаратные средства. Конфигурация типа той, которая
стремиться запустить только одно приложение имеет единственную цель - получить
монопольный доступ к этим аппаратным средствам. В этом случае создание драйверов
устройств очень нерентабельно. В конце концов, причина хотя бы в том, что это
работает под Windows, что можно получить свободно (почти) классный GUI, а не в
том, чтобы 10 приложений работало одновременно.
К счастью, в Windows 95 заложена совместимость с Windows 3.x. Это означает,
что директивное использование I/O портов также возможно, поскольку до сих пор
находятся в эксплуатации множество 16-битных программ, которые просто не могут
работать по другому. Просто в этом случае при кодировании вам придется
спуститься до уровня ассемблера. Автор следующего кода Arthur Hoornweg:
function getport(p:word):byte; stdcall;
begin
asm
push edx
push eax
mov dx,p
in al,dx
mov @result,al
pop eax
pop edx
end;
end;
Procedure Setport(p:word;b:byte);Stdcall;
begin
asm
push edx
push eax
mov dx,p
mov al,b
out dx,al
pop eax
pop edx
end;
end;
|
François Piette также предлагает свое решение прямого доступа к портам I/O на
страничке http://rtfm.netline.be/fpiette/portiofr.htm Как насчет NT?
Но все вышесказанное под Windows NT работать не будет. NT более "прочная"
операционная система, поэтому если она позволит в любое время кому попало
обращаться к любым аппаратным средствам, она не была бы такой устойчивой. Кроме
того, NT является кроссплатформенной системой, поэтому доступ к I/O портам может
кардинально различаться при работе на различных процессорах.
Но тем не менее даже под NT можно добраться непосредственно до I/O портов,
правда только на x86 процессорах. Это не является документированной
особенностью, и, вероятно, исчезнет в будущих версиях этой операционной
системы.
Я не обладаю достаточно полной информацией по этому вопросу, но интересующая
нас статья D. Roberts в майском номере журнала Dr. Dobb's Journal за 1996 год
так и называется "Direct Port I/O and Windows NT." К сожалению, я так и не нашел
времени проверить приведенный там код. Статью и посвященный ей флейм вы можете
почитать по адресу http://www.ddj.com.
Также рекомендую ознакомиться с опубликованной в Windows Developer Journal
статьей "Port I/O under Windows." Опубликована Karen Hazzah в июне 1996 года.
Статью и посвященный ей флейм вы можете найти по адресу http://www.wdj.com. Ресурсы
(Примечание, я не очень много знаю об этих ресурсах, проверьте их пожалуйста
сами.)
Существуют новостные группы, посвященные написанию VxD и VDD:
comp.os.ms-windows.programmer.nt.kernel-mode (VDD)
comp.os.ms-windows.programmer.vxd (VxD)
Dejanews (http://www.dejanews.com)
выдает достаточно много результатов, если для поиска задать фразу 'device driver
direct I/O access 95'.
Компания BlueWater Systems разработала OCX, осуществляющее прямой доступ к
I/O портам, памяти и прерываниям, работающее под всеми Win32 платформами. Они
также, кажется, предлагают изготовление драйверов устройств под заказ.
Посмотрите их сервер по адресу http://www.bluewatersystems.com.
Я сляшал, что какая-то другая компания также рекламировала свои услуги в
области разработчики VxD, но я не нашел их адреса.
|