Страница 5 из 5 Посмотрим теперь, как изменится программа драйвера. Программа драйвера для обслуживания аппаратных прерываний ... WM_USER=SPM_UM_AlwaysSchedule+400h;Код сообщения WM_USER include shell.inc ;Дополнительный включаемый файл ... ;====================== VxD_DATA_SEG Data dw 0,0 ;32-битовая ячейка с данным для передачи в приложение hwnd dd 0 ;32-битовая ячейка для получения дескриптора окна IRQ_Handle dd 0 ;Дескриптор виртуального прерывания VMyD_Int13_Desc label dword;32-битовый адрес следующей далее структуры VPICD_IRQ_Descriptor <5,,OFFSET32 VMyD_Int_13>;Структура с данными о прерывании VxD_DATA_ENDS ;====================== VxD_CODE_SEG BeginProc VMyD_Control ... EndProc VMyD_Control ;---------------------- BeginProc VMyD_Device_Init ... EndProc VMyD_Device_Init ;------------------------- ;API-процедура, вызываемая из приложения ;При вызове: BX=C0, CX=C1, DX=C2, DI=дескриптор главного окна BeginProc API_Handler ;Получим параметры из приложения movzx EAX,[EBP.Client_DI] mov hwnd,EAX ;Общий сброс ... ;Размаскируем уровень 5 в физическом контроллере прерываний ... ;Засылаем управляющие слова по каналам ... ;Засылаем константы в каналы ... ;Установим флаг S2 разрешения счета ... ret EndProc API_Handler ;------------------------- ;Процедура обработки аппаратного прерывания IRQ5 (вектор 13) BeginProc VMyD_Int_13, High_Freq ;Получим результат измерений из выходного регистра счетчика ... mov Data,AX ;Результат в младшей половине Data ;Выполним завершающие действия в PIC и вызовем функцию приложения mov EAX,IRQ_Handle VxDCall VPICD_Phys_EOI;EOI в физический контроллер прерываний VxDCall VPICD_Physically_Mask;Маскируем наш уровень ;Перейдем на синхронный уровень. Это все иначе push 0 ;Таймаут push CAAFL_RING0 ;Событие кольца 0 push 0 ;Данные для передачи в процедуру отложенных прерываний push OFFSET32 Reflect_Int;Вызываемая процедура отложенных прерываний VxDCall _SHELL_CallAtAppyTime;Вызвать во "время приложения" add ESP,4*4 ;Компенсация стека clc ret EndProc VMyD_Int_13 ;------------------------- ;Процедура уровня отложенных прерываний. Это тоже иначе BeginProc Reflect_Int push 0 ;Данные для функции обратного вызова push 0 ;Адрес функции обратного вызова push 0 ;lParam push Data ;wParam push WM_USER ;Код сообщения push hwnd ;Окно-адресат VxDCall _SHELL_PostMessage;Поставить сообщение в очередь add ESP,4*6 ;Компенсация стека clc ret EndProc Reflect_Int VxD_CODE_ENDS end VMyD_Real_Init В начале текста драйвера необходимо подключить еще один заголовочный файл SHELL.INC и определить значение константы WM_USER. В Windows эта константа имеет длину 16 бит и равна 400h, однако функции _SHELL_PostMessage необходимо передать 32-битовое слово, причем сам код сообщения WM_USER должен находиться в младшей половине этого слова, а в старшую половину следует поместить информацию о диспетчеризации. В нашем случае эта информация выглядит как константа: SPM_UM_AlwaysSchedule. В сегменте данных удалены ячейки для адреса функции обратного вызова isr и селектора DS. Ячейка для результата измерений объявлена как два слова, поскольку все параметры функции Shell_PostMessage имеют размер 32 бит. Добавлена ячейка hwnd для получения в нее из приложения дескриптора главного окна. Сам дескриптор имеет размер 16 бит, однако передавать его той же функции Shell_PostMessage надо в виде длинного слова. В начале API-процедуры из структуры клиента (конкретно - из регистра DI) извлекается дескриптор окна и после расширения до длинного слова помещается в ячейку hwnd. Остальные изменения касаются лишь способа перехода на уровень отложенных прерываний и состава процедуры ReflectInt, работающей на этом уровне. Для перехода на синхронный уровень в данном случае используется системный вызов _SHELL_CallAtAppyTime, осуществляющий передачу управления указанной в вызове процедуре ReflectInt во <время приложения>, то есть когда управление будет возвращено из VMM в приложение. В этой процедуре уже можно будет поставить сообщение WM_USER в очередь сообщений главного окна нашего приложения. В процедуре уровня отложенных прерываний ReflectInt после помещения в стек необходимых параметров вызывается системная функция _Shell_PostMessage, которая и посылает в приложение сообщение WM_USER. Легко увидеть, что программист должен в этом случае полностью сформировать весь состав структуры сообщения msg - дескриптор окна-адресата, код сообщения, а также оба параметра, входящие во все сообщения Windows, - wParam и lParam. Параметром wParam мы в данном примере пользуемся для передачи в приложение результата измерения из ячейки Data. При необходимости можно было использовать и lParam. Функция обратного вызова, адрес которой можно было указать в числе параметров, вызывается Windows после успешной постановки в очередь посылаемого сообщения. Мы эту функцию не используем. Для задач управления аппаратурой, работающей в режиме прерываний, важной характеристикой является время отклика на прерывание, то есть временная задержка от момента поступления прерывания до выполнения первой команды обработчика. Как мы увидели, при использовании виртуального драйвера системные издержки перехода на прикладной обработчик, включенный в состав драйвера, составляют около 40 команд, на выполнение которых на машине средней производительности может понадобиться 10...15 мкс. При использовании системы MS-DOS этих издержек не было бы вовсе, так как в реальном режиме переход на обработчик прерываний процессор осуществляет практически мгновенно. Если же реализовать обработку прерываний без помощи виртуального драйвера, как это было сделано в предыдущей части статьи, то переход на прикладной обработчик прерываний потребовал бы 200...300 команд, а время задержки увеличилось бы (на таком же компьютере) до 120...180 мкс, то есть более чем на порядок. К.Г.Финогенов КомпьютерПресс 8'2001 |