Ocenite etot tekst:


---------------------------------------------------------------
 Date: 14 Apr 1997
 From: Kazennov Vladimir 
 Perevod Vladimira Kazennova
---------------------------------------------------------------

     Drajvery  ustrojstv,  kak  pravilo,  - naibolee kriticheskaya chast'
programmnogo obespecheniya  komp'yuterov. Po  ironii sud'by  eto takzhe  i
naibolee skrytaya chast'  razrabotki programmnogo obespecheniya.  Drajvery
ustrojstv  sistemy  Windows  firmy  Microsoft ne yavlyayutsya isklyucheniem.
Esli vy  kogda-libo pisali  obychnoe prilozhenie  v sisteme  Windows, to
vam izvestno, chto trebuetsya opredelennoe kolichestvo skrytyh  sposobov,
chtoby  prilozhenie  rabotalo   nadezhno.  Kak  podmnozhestvo   prilozhenij
Windows, drajvery ustrojstv sistemy Windows sleduyut etomu zhe  pravilu.
V  dannoj  stat'e  avtor  rassmatrivaet rabotayushchij drajver ustrojstva,
kotoryj  obespechivaet  dostup  k  portam  vvoda-vyvoda  i obrabatyvaet
preryvaniya, i virtual'nyj drajver ustrojstva (VxD), kotoryj  imitiruet
tehnicheskie  sredstva.   Predpolagaetsya,  chto  chitatel'  znaet  osnovy
programmirovaniya v  sisteme Windows,  vklyuchaya biblioteki  dinamicheskoj
svyazi (dynamic link libraries - DLLs).


    Ustrojstvo

Rassmatrivaemoe ustrojstvo - eto ne chast' tehnicheskih sredstv, kotoraya byla razrabotana, chtoby prodemonstrirovat', kak pisat' drajver ustrojstva v sisteme Windows. Skoree, eto virtual'noe ustrojstvo, polnost'yu realizovannoe v programmnom obespechenii. Programma-primer vypolnyaetsya tol'ko s virtual'nym ustrojstvom, kotoroe avtor opredelil rabotaya s sistemoj Windows v rasshirennom rezhime (Enhanced mode) processora 386, i pri uslovii, chto ustanovlen virtual'nyj drajver ustrojstva (VxD). Dalee v stat'e bolee detal'no budet opisan ishodnyj kod dlya etogo ustrojstva. Na dannyj moment sleduet znat', chto ustrojstvo imeet dva porta: port sostoyaniya i port upravleniya, oba na odnom i tom zhe adrese. Na ris. 1 pokazany bity, ispol'zuemye v porte sostoyaniya. Bit 2 ukazyvaet, chto imela mesto oshibka ustrojstva, bit 1 pokazyvaet, chto zapros na preryvanie yavlyaetsya otlozhennym, a bit 0 ukazyvaet, chto ustrojstvo zanyato. Bit 7 govorit o tom, chto ustrojstvo est' v nalichii. V etom sluchae dannyj bit raven nulyu. Esli zhe ustrojstvo ne ustanovleno ili k nemu net dostupa, to bit prinimaet znachenie, ravnoe 1. +--------------------------------------------------------+ | 7 6...3 2 1 0 | +---------+-------------------+---------+-------+--------+ | PRESENT | | ERROR | IRQ | BUSY | +---------+-------------------+---------+-------+--------+ PRESENT - ustrojstvo est' v nalichii; ERROR - proizoshla oshibka ustrojstva; IRQ - preryvanie otlozheno; BUSY - ustrojstvo zanyato. (Ostal'nye bity ignoriruyutsya dlya dal'nejshej sovmestimosti.) Ris. 1. Bity porta sostoyaniya ustrojstva Na ris. 2 pokazany bity, ispol'zuemye v porte upravleniya. Bit 1 ukazyvaet ustrojstvu, chto CPU zakonchilo obrabotku preryvaniya. Bit 0 pokazyvaet, chto ustrojstvo mozhet nachat' obrabotku vvoda-vyvoda. (V dannyj moment ne sleduet zaostryat' vnimanie na tom, chto fakticheski ustrojstvo delaet. Vmesto etogo, neobhodimo udelit' vnimanie tomu, kak napisat' drajver dlya takogo ustrojstva, kotoroe obespechivaet apparatnye preryvaniya.) +--------------------------------------------------------+ | 7...2 1 0 | +---------------------------------------+-------+--------+ | 1 1 1 1 1 1 | EOI | START | +---------------------------------------+-------+--------+ EOI - signal dlya ustrojstva, podtverzhdayushchij priem preryvaniya; START - signal dlya ustrojstva nachinat' peresylku vvoda-vyvoda. (Ostal'nye bity dolzhny byt' ustanovleny v 1 dlya dal'nejshej sovmestimosti.) Ris. 2. Bity porta upravleniya ustrojstva

    Drajver ustrojstva v sisteme MS-DOS

Na listinge 1 pokazana programma dostest.asm, predstavlyayushchaya soboj obychnyj drajver ustrojstva dlya sistemy MS-DOS, kotoryj obshchaetsya s ustrojstvom. Nesmotrya na prostotu i malyj razmer dannaya programma soderzhit osnovnye komponenty drajvera ustrojstva, kotoryj obrabatyvaet preryvaniya. _____________________________________________________________________ page ,132 ; masm tisr ; >err .286p .xlist include ..\..\include\bogus.inc .list Words struc LoWord dw ? HiWord dw ? Words ends EOI equ 020h ; komanda EOI dlya kontrollera PIC INTA00 equ 020h ; upravlenie glavnym kontrollerom PIC INTA01 equ 021h ; registr maski glavnogo kontrollera PIC INT_MASTER_0 equ 08h ;nomer INT glavn. kontrollera PIC INTB00 equ 0A0h ; upravlenie podchinennym kontrollerom PIC INTB01 equ 0A1h ;registr maski podchinennogo kontrollera PIC INT_SLAVE_0 EQU 70h ; nomer INT podchinennogo kontrollera PIC ; ; Ustanovit' peremennye dlya nashego nomera preryvaniya ; ife (FAKE_IRQ GE 8) INT_DEV equ (INT_MASTER_0+(FAKE_IRQ AND 7)) PIC00 equ INTA00 PIC01 equ INTA01 else INT_DEV equ (INT_SLAVE_0+(FAKE_IRQ AND 7)) INT_MASK equ 1 SHL (FAKE_IRQ AND 7) PIC00 equ INTB00 PIC01 equ INTB01 endif page CONST SEGMENT DWORD PUBLIC 'DATA' sdNoBogus db 'I do not see the bogus device.',Odh,Oah,'$' sdPrompt db Odh,Oah,'S)tart, or Q)uit: ','$' sdCRLF db Odh,Oah,'$' sdDot db '.','$' CONST ENDS DATA SEGMENT DWORD PUBLIC 'DATA' dwCount1 dw 0 dwCount2 dw 0 lpPrevISR dd 0 ; adres predydushchej programmy ISR fStopping db 0 ; znachenie TRUE pri zavershenii DATA ENDS STACK SEGMENT DWORD STACK 'STACK' db 512 dup (?) STACK ENDS DGroup GROUP CONST,DATA,STACK page ;IP IntSvcRtn - The Interrupt Service Routine (Programma obsluzhivaniya ; preryvaniya) ; WARNINGS (preduprezhdeniya) ; ; NOTES (primechaniya) ; Dannaya programma ISR uvelichivaet schetchik preryvanij (dwCount1) ; i zanovo maskiruet ustrojstvo. ; ; Esli ustanovlen flag "fStopping", ustrojstvo ne maskiruetsya zanovo. ; FIXED_TEXT SEGMENT PARA PUBLIC 'CODE' segData1 dw DGroup assume CS:FIXED_TEXT,DS:NOTHING IntSvcRtn proc far push ax push dx push ds mov ds,segDatal assume ds:DGroup inc dwCount1 mov al,NOT FAKE_CTL_EOI mov dx,FAKE_PORT out dx,al ; poslat' EOI ustrojstvu mov al,EOI out PIC00,al ; poslat' EOI kontrolleru PIC ife (PIC00 EQ INTA00) out INTA00,al ; poslat' EOI glavn. kontrolleru PIC takzhe endif cmp fStopping,0 ; sushchestvuet? jnz isr9 ; esli da, to ne nuzhen perezapusk mov al,NOT FAKE_CTL_START mov dx,FAKE_PORT out dx,al ; perezapusk vvoda-vyvoda isr9: pop ds assume ds:NOTHING pop dx pop ax iret IntSvcRtn endp FIXED_TEXT ENDS page ;IP_main - tochka vhoda v programmu ; NOTES (primechaniya) ; Drajver ustrojstva vydaet dlya pol'zovatelya priglashenie: ;S)tart(nachat') ili Q)uit(vyjti). Esli pol'zovatel' nazhimaet S, ;programma razreshaet preryvaniya i vooruzhaet ustrojstvo, pechataya tochku ;kazhdyj raz, kak ustrojstvo preryvaetsya. ; _TEXT SEGMENT PARA PUBLIC 'CODE' segData2 dw DGroup segfixed dw FIXED_TEXT assume cs:_TEXT,ds:NOTHING _main label far mov ds,segData2 ;inicializiruetsya segment dannyh po umolchaniyu assume ds:DGroup mov dx,FAKE_PORT in al,dx ; prisutstvuet li fiktivnoe ustrojstvo? or al,al jns m10 ;propustit', esli da mov dx,OFFSET DGroup:sdNoBogus mov ah,9 int 21h ; v protivnom sluchae pechatat' soobshchenie ob oshibke mov ax,4C01h int 21h ; i vyjti iz sistemy m10: mov ax,3500h+INT_DEV cli int 21h ; zaprosit' tekushchuyu programmu ISR mov lpPrevISR.LoWord,bx mov lpPrevISR.HiWord,es ; sohranit' ee mov dx,OFFSET FIXED_TEXT:IntSvcRtn push ds mov ds,segFixed assume ds:NOTHING mov ax,2500h+INT_DEV int 21h ; ustanovit' nashu programmu ISR pop ds assume ds:DGroup sti mov dx,OFFSET DGroup:sdPrompt mov ax,9 int 21h ; S)tart ili Q)uit ml1: mov dl,0PFh mov ah,6 int 21h ; chitat' s konsoli, ne ozhidaya jz ml3 or al,40h cmp al,'q' je ml8 ; propustit', esli nazhato "Q" cmp al,'s' jne ml3 ; propustit', esli ne nazhato "S" cli in al,PIC01 ; razmaskirovat' preryvanie and al,NOT INT_MASK out PIC01,al sti mov al,NOT FAKE_CTL_START mov dx,FAKE_PORT out dx,al ; nachat' vvod-vyvod s ustrojstva ml3: mov ax,dwCount1 cmp ax,dwCount2 je ml4 ; propustit', esli schetchik preryvanij ne izmenilsya mov dwCount2,ax mov dx,OFFSET DGroup:sdDot mov ah,9 int 21,h ;v protivnom sluchae vydat' tochku ml4: jmp ml1 ; cikl ml8: mov fStopping,1 ; ukazanie dlya programmy ISR zavershit' rabotu mov dx,FAKE_PORT ml9: in al,dx rcr al,1 jnc ml9 ; cikl, esli zanyato cli in al,PIC01 or al,INT_MASK out PIC01,al ; maskirovat' uroven' preryvaniya sti push ds lds dx,lpPrevISR assume ds:NOTHING mov ax,2500h+INT_DEV int 21h ; vosstanovit' predydushchuyu programmu ISR pop ds assume ds:DGroup mov ax,4C00h int 21h ; vyhod _TEXT ENDS end_main ; Konec fajla _____________________________________________________________________

    Listing 1. Programma dostest.asm

Drajver ustrojstva nachinaet rabotu s proverki starshego bita porta sostoyaniya, chtoby ubedit'sya v nalichii ustrojstva. Zatem on ustanavlivaet svyaz' s vektorom preryvaniya MS-DOS dlya preryvaniya 11. Drajver sohranyaet predydushchee znachenie, hranimoe v etom vektore, tak chtoby mozhno bylo zamenit' znachenie, esli programma sushchestvuet. Dalee drajver ustrojstva vydaet priglashenie dlya pol'zovatelya : Start(nachat') ili Quit(vyjti). Esli pol'zovatel' nazhimaet S, programma nachinaet peresylku vvoda-vyvoda. Esli pol'zovatel' nazhimaet Q, to programma otklyuchaet ustrojstvo, vosstanavlivaet vektor preryvaniya i zavershaetsya. CHtoby nachat' operaciyu vvoda-vyvoda, drajver MS-DOS snachada razmaskiruet programmiruemyj kontroller preryvanij (programmable interrupt controller - PIC) dlya urovnya preryvaniya ustrojstva (v primere preryvanie 11). Zatem drajver nachinaet operaciyu vvoda-vyvoda dlya ustrojstva putem zapisi 1 v bit 0 porta upravleniya. Tak kak preryvaniya vklyucheny, to pri vozniknovenii preryvanij na ustrojstve poluchit upravlenie programma obsluzhivaniya preryvanij (interrupt service routine - ISR). Esli proishodit preryvanie na ustrojstve, to programma ISR podtverzhdaet priem preryvaniya, posylaya znachenie EOI ustrojstvu (t.e. zapisyvaya 1 v bit 1 porta upravleniya ustrojstva) i kontrolleru PIC. Esli programma, vypolnyayushchaya vvod-vyvod, sushchestvuet, to programma ISR vypolnyaetsya. V protivnom sluchae programma ISR osushchestvlyaet inicializaciyu peresylki vvoda-vyvoda vnov', zapisyvaya 1 v bit 0 porta upravleniya ustrojstva. Itak, programma ISR vozobnovlyaet vvod-vyvod vsyakij raz, kogda proishodit preryvanie, takim obrazom ustrojstvo nepreryvno vypolnyaet operaciyu vvoda-vyvoda. Krome obespecheniya nepreryvnogo vvoda-vyvoda programma ISR uvelichivaet schetchik (dwCount1) vsyakij raz, kogda obrabatyvaet preryvanie. V processe vypolneniya vvoda-vyvoda programma sledit za schetchikom preryvanij, otobrazhaet tochku (".") dlya kazhdoj zakonchennoj peresylki vvoda-vyvoda i prodolzhaet skanirovat' klaviaturu, chtoby opredelit', hochet li pol'zovatel' ostanovit' peresylku. CHtoby zavershit' programmu, pol'zovatel' nazhimaet klavishu Q. Programma ustanavlivaet flag, kotoryj informiruet programmu ISR o tom, chto sleduet ostanovit' obrabotku. Posle togo, kak operaciya vvoda-vyvoda ostanovlena, programma maskiruet uroven' preryvaniya v kontrollere PIC i vosstanavlivaet vektor preryvaniya.

    Drajver ustrojstva v sisteme Windows

CHrezvychajno trivial'nyj drajver ustrojstva MS-DOS, opisannyj v predydushchem razdele, po sushchestvu dovol'no slozhno realizovat' v sisteme Windows. Pri napisanii drajvera ustrojstva v sisteme Windows, obrabatyvayushchego preryvaniya, neobhodimo ispol'zovat' arhitekturu, otlichnuyu ot toj, kotoraya byla ispol'zovana dlya drajvera MS-DOS. V chastnosti, neobhodimo otdelit' komponentu obrabotki preryvaniya ot komponenty prilozheniya. Vmesto edinstvennoj programmy, upravlyayushchej kak programmoj ISR, tak i interfejsom pol'zovatelya, kak sdelano v sisteme MS-DOS, v sisteme Windows neobhodimo vydelit' eti funkcii v otdel'nye programmnye moduli, nazyvaemye bibliotekoj dinamicheskoj svyazi (DLL) i interfejsom prikladnyh programm (Application Program Interface - API).

    Biblioteka DLL dlya drajvera

Pri napisanii prilozhenij v sisteme Windows obychno v programmnom module imeyut delo tol'ko s dvumya tipami segmentov: peremeshchaemym (moveable) i vygruzhaemym (discardable). Segmenty dannyh programmy yavlyayutsya peremeshchaemymi, t.e. ih linejnye adresa v pamyati mogut izmenyat'sya, kogda programme upravleniya pamyat'yu sistemy Windows trebuetsya organizovat' pamyat'. Selektor (selector) i smeshchenie, ispol'zuemye dlya dostupa k opredelennoj yachejke pamyati, ostayutsya fiksirovannymi, no pod shemoj selektor-smeshchenie sistema Windows mozhet peremeshchat' fakticheskie dannye v linejnoj pamyati. Segmenty programm-kodov takzhe peremeshchaemye, no imeyut dopolnitel'nyj atribut - vygruzhaemye. Ih soderzhimoe mozhet byt' vygruzheno polnost'yu, a pri neobhodimosti zagruzheno s diska, tak kak nel'zya pisat' i (ili) modificirovat' informaciyu v segmente programmy-koda. Esli pri obrashchenii k segmentu iz programmy Windows, on okazalsya vygruzhennym, programma upravleniya pamyat'yu sistemy Windows avtomaticheski obratitsya k disku i prochitaet ranee vygruzhennyj segment. Itak, kakim obrazom eto obstoyatel'stvo vliyaet na kod dlya programmy ISR? Tak kak preryvanie mozhet proizojti v lyuboe vremya, a kod ISR mozhet okazat'sya vygruzhennym, to vozniknet problema zagruzit' kod v pamyat', esli fiksiruetsya preryvanie. Vmesto etogo, mozhno opisat' segment kak FIXED (fiksirovannyj), a ne kak MOVEABLE (peremeshchaemyj) ili DISCARDABLE (vygruzhaemyj). Segment s atributom FIXED budet ostavat'sya v edinstvennom meste linejnoj pamyati i ne budet vygruzhat'sya, dazhe esli on soderzhit kod. V etom sluchae, esli proizojdet preryvanie, kod budet dostupen i gotov k vypolneniyu. Odnako sleduet otmetit' odin maloizvestnyj fakt, a imenno: v sisteme Windows tol'ko te segmenty budut schitat'sya FIXED, kotorye byli opisany v biblioteke DLL. Segment FIXED v obychnom programmnom module budet rassmatrivat'sya kak MOVEABLE. Takim obrazom v sisteme Windows nel'zya budet pomestit' programmu ISR v obychnyj programmnyj modul'. Vmesto etogo ee neobhodimo pomestit' v biblioteku DLL. Listing 2 predstavlyaet ishodnyj kod bogusa.asm na assemblere dlya biblioteki DLL, kotoryj soderzhit programmu ISR i mozhet vypolnyat'sya v okruzhenii Windows. Programma IntSvcRtn ochen' pohozha na svoj dublikat, rabotayushchij v sisteme MS-DOS. Odnako krome uvelicheniya peremennoj- schetchika dannaya programma ISR takzhe zapisyvaet v ochered' soobshchenie Windows. CHtoby izbezhat' perepolneniya ocheredi, zapis' soobshcheniya proizvoditsya tol'ko v sluchae, kogda peremennaya-schetchik wCount izmenyaet znachenie ot 0 k 1. Funkciya obnuleniya schetchika wCount posle togo, kak zakonchena obrabotka soobshcheniya, peredana vysokourovnevoj programme sistemy Windows. S pervogo vzglyada vse eti rassuzhdeniya kazhutsya prostymi, odnako obrabotka preryvanij v sisteme Windows sovsem ne tak prosta, kak v sisteme MS-DOS. _____________________________________________________________________ page ,132 ; masm tisr ; >err .286p .xlist include bogus.inc include pic.h .list WM_COMMAND =0111h EXTRN POSTMESSAGE:FAR Words struc LoWord dw ? HiWord dw ? Words ends ; ; Ustanovit' peremennye dlya nashego nomera preryvaniya ; ife (FAKE_IRQ GE 8) INT_DEV equ (INT_MASTER_0+(FAKE_IRQ AND 7)) PIC00 equ INTA00 PIC01 equ INTA01 else INT_DEV equ (INT_SLAVE_0+(FAKE_IRQ AND 7)) INT_MASK equ 1 SHL (FAKE_IRQ AND 7)) PIC00 equ INTB00 PIC01 equ INTB01 endif page FIXED_DATA SEGMENT DWORD PUBLIC 'DATA' PUBLIC _hWndEvent,_wParamEvent,_wCount _hWndEvent label word hWndEvent dw 0 ; Okno dlya postirovaniya sobytij _wParamEvent label word wParamEvent dw 0 ; Znachenie wParam dlya postirovaniya _wCount label word wCount dw 0 ; Schetchik neobrabotannyh preryvanij FIXED_DATA ENDS page ; IP IntSvcRtn - programma obsluzhivaniya preryvanij ; ; WARNINGS (Preduprezhdeniya) ; ; NOTES (Primechaniya) ; Dannaya programma ISR uvelichivaet schetchik preryvanij i zanovo ; maskiruet ustrojstvo. ; Esli predydushchee znachenie schetchika bylo ravno 0, to zapisyvaetsya ; soobshchenie ; Esli ustanovlen flag "fStopping", ustrojstvo ne maskiruetsya zanovo. ; FIXED_TEXT SEGMENT PARA PUBLIC 'CODE' selData1 dw FIXED_DATA assume CS:FIXED_TEXT,DS:NOTHING PUBLIC _IntSvcRtn _IntSvcRtn label far IntSvcRtn proc far push ax push dx push ds mov ds,selDatal assume ds:FIXED_DATA inc wCount mov al,NOT FAKE_CTL_EOI mov dx,FAKE_PORT out dx,al ; posylaem ustrojstvu EOI mov al,EOI out PIC00,al ; posylaem EOI kontrolleru PIC ife (PIC00 EQ INTA00) out INTA00,al ; posylaem EOI takzhe glavnomu kontrolleru PIC endif cmp hWndEvent,0 ; zavershat'? jz isr9 ; esli da, to ne delaem perezapuska i ; postirovaniya cmp wCount,1 ; trebuetsya postirovanie? jne isr8 ; propuskaem, esli net push bx ; sohranyaem ostavshiesya registry push cx push es push hWndEvent push WM_COMMAND push wParamEvent push 0 ; lParam ravno 0 push 0 call POSTMESSAGE ; registriruem sobytie pop es pop cx pop bx isr8: mov al,NOT FAKE_CTL_START mov dx,FAKE_PORT out dx,al ; vozobnovlyaem vvod-vyvod isr9: pop ds assume ds:NOTHING pop dx pop ax iret IntSvcRtn endp ; trebuetsya programme AllocIntReflector PUBLIC _BogusCallback _BogusCallback label far BogusCallback proc far pushf call IntSvcRtn ret BogusCallback endp FIXED_TEXT ENDS end ; konec fajla _____________________________________________________________________

    Listing 2. Programma bogusa.asm

    Drajvernyj interfejs API

Krome otdel'nogo programmnogo modulya dlya programmy ISR (v forme biblioteki DLL sistemy Windows), dlya raboty drajvera neobhodim takzhe programmnyj modul' pol'zovatel'skogo interfejsa, nazyvaemyj interfejs API. Na listinge 3 privedena programma bogus.h, predstavlyayushchaya soboj primer interfejsa API. |ta programma soderzhit 4 tochki vhoda v biblioteku DLL. _____________________________________________________________________ #ifndef EXPORT #define EXPORT #endif extern int EXPORT FAR PASCAL BogusCheck(void) ; extern void EXPORT FAR PASCAL BogusStart(HWND hWnd,WPARAM wParam) ; extern int EXPORT FAR PASCAL BogusGetEvent(void) ; extern void EXPORT FAR PASCAL BogusStop(void) ; /* Konec fajla */ ______________________________________________________________________ Listing 3. Programma bogus.h. V tochke vhoda BogusCheck prosto proveryaetsya nalichie ustrojstva. Programma vozvrashchaet znachenie TRUE, esli ustrojstvo obnaruzheno (bit 7 porta sostoyaniya), i znachenie FALSE v protivnom sluchae. Tochki vhoda BogusStart i BogusStop nachinayut i zavershayut rabotu ustrojstva. Krome togo, tochka vhoda BogusStart razreshaet preryvaniya i obespechivaet svyaz' s apparatnym preryvaniem, a tochka vhoda BogusStop vyklyuchaet preryvaniya ustrojstva i vosstanavlivaet apparatnoe preryvanie. Tochka vhoda BogusGetEvent vozvrashchaet kolichestvo preryvanij, obrabotannyh so vremeni pervogo starta ustrojstva, libo so vremeni poslednego vyzova tochki vhoda BogusGetEvent. (Tochka vhoda BogusGetEvent obnulyaet schetchik preryvanij pri kazhdom ee vyzove.)

    Preryvaniya pri standartnom rezhime raboty sistemy Windows

Pri napisanii drajvera, kotoryj budet vypolnyat'sya v standartnom rezhime raboty sistemy Windows, neobhodimo uchityvat' vozmozhnost' poyavleniya preryvaniya, kogda processor rabotaet v real'nom rezhime. Dazhe esli rabotayut tol'ko prilozheniya sistemy Windows, a ne prilozheniya sistemy MS-DOS, processor chasto pereklyuchaetsya iz real'nogo v zashchishchennyj rezhim. Tak kak sistema Windows 3.1 ne yavlyaetsya operacionnoj sistemoj, a skoree predstavlyaet soboj okruzhenie pol'zovatel'skogo interfejsa, ona vozlagaet vypolnenie opredelennogo kolichestva osnovnyh funkcij, vklyuchaya funkciyu vvoda-vyvoda fajlov, na operacionnuyu sistemu (a imenno MS-DOS). Poetomu kogda prilozhenie sistemy Windows vypolnyaet funkciyu MS-DOS vvoda-vyvoda fajla i processor pri etom rabotaet v real'nom rezhime, ustrojstvo mozhet preryvat' CPU. Po umolchaniyu, esli biblioteka DLL obespechila svyaz' s preryvaniem, to sistema Windows pereklyuchit CPU v zashchishchennyj rezhim dlya obrabotki preryvaniya i, kak tol'ko programma ISR zavershit rabotu, pereklyuchit CPU obratno v real'nyj rezhim dlya prodolzheniya vypolneniya funkcij sistemy MS-DOS. Hotya eto v men'shej mere otnositsya k CPU 80386, pereklyuchenie processora iz zashchishchennogo rezhima v real'nyj rezhim, naprimer na processore 80286, sozdaet ogromnye nakladnye rashody, trebuyushchie kontroliruemogo sbrosa CPU, kotoryj vypolnyaetsya v techenii millisekund. Esli neobhodimo uskorit' srednee vremya otveta, nuzhno predotvratit' pereklyuchenie processora v zashchishchennyj rezhim, esli on poluchaet preryvanie, rabotaya v real'nom rezhime. Obespechenie svyazi s vektorom preryvaniya v zashchishchennom rezhime iz biblioteki DLL sistemy Windows - trivial'no, chto i pokazano v programme SetPMVector, predstavlennoj v listinge 4 (programma bogus.c). Ustanovlenie svyazi s vektorom proizvoditsya takim zhe sposobom, kak i v sisteme MS-DOS, - s pomoshch'yu funkcii setvector sistemy MS-DOS. Odnako v otlichie ot vyzova v sisteme MS-DOS, v sisteme Windows pri obrashchenii k funkcii peredayutsya selektor i smeshchenie, a ne segment i smeshchenie. YAdro sistemy Windows sledit za vsem. Funkcii sleduet peredat' normal'nyj selektor i smeshchenie (natural'nyj ukazatel' far dlya sistemy Windows), a ne segment i smeshchenie (natural'nyj ukazatel' far dlya sistemy MS-DOS). Odnako, kak uzhe upominalos', ustanovleniya svyazi s vektorom preryvaniya v zashchishchennom rezhime nedostatochno. Neobhodimo takzhe obespechit' svyaz' s vektorom preryvaniya v real'nom rezhime, a eto ne trivial'naya zadacha. ______________________________________________________________________ /*EM BOGUS.C - Drajver fiktivnogo ustrojstva biblioteki DLL * * SUMMARY (Rezyume) * Bazovye funkcii LibMain, WEP * * COMMENTS (Kommentarii) * * WARNINGS (Preduprezhdeniya) * */ #include #include "bogusa.h" #include "pic.h" #include "dpmi.h" #define EXPORT _export _loadds #include "bogus.h" #define FAKE_PORT 0x141 /* Uroven' fiktivnosti (bogosity) - 9.4 */ #define FAKE_IRQ 11 /* Uroven' fiktivnosti (bogosity) - 9.8 */ #define FAKE_CTL_START 0x01 /* komanda "nachat'" fiktivnogo porta (ustanavlivaetsya v nul') */ #define FAKE_CTL_EOI 0x02 /* EOI fiktivnogo porta */ #define FAKE_STAT_BUSY 0x01 /* indikaciya zanyatosti fiktivnogo porta (zero=>busy) */ #define FAKE_STAT_IRQ 0x02 /* IRQ fiktivnogo porta (zero=>IRQ) */ #define FAKE_STAT_ERROR 0x04 /* oshibka vvoda-vyvoda (zero=>error) (sbrasyvaetsya pri chtenii) */ /* Ustanovit' peremennye dlya nashego nomera preryvaniya */ #if (FAKE_IRQ<8) #define INT_DEV (INT_MASTER_0+(FAKE_IRQ & 7)) #define PIC00 INTA00 #define PIC01 INTA01 #else #define INT_DEV (INT_SLAVE_0+(FAKE_IRQ & 7)) #define PIC00 INTB00 #define PIC01 INTB01 #endif #define INT_MASK (1 << (FAKE_IRQ & 7)) BOOL FAR PASCAL LibMain(HANDLE hInstance /* obrabotchik bibliotechnogo ekzemplyara*/ ,WORD wDataSeg /* segment dannyh po umolchaniyu */ ,WORD cbHeap /* razmer dinamicheskoj oblasti po umolchaniyu */ ,LPSTR lpszCmdLine) ; /* komandnaya stroka */ int FAR PASCAL WEP(int fSystemExit) ; #pragma alloc_text(INIT_TEXT,LibMain) /* derzhat' vmeste s LIBENTRY.ASM */ #pragma alloc_text(FIXED_TEXT,WEP) HANDLE hLibInstance ; FARPROC lpfnPrevISR ; /* Sohranennaya predydushchaya programma ISR*/ DWORD lpfnPrevRMISR ; /* Sohranennaya predydushchaya programma ISR real'nogo rezhima*/ HANDLE hReflector ; DWORD DPMI_AllocateRMCallback(FARPROC lpfnCallback, _RMCS FAR *lpRMCS) { DWORD dwRet ; _asm { push ds lds si,lpfnCallback les di,lpRMCS mov ax,DPMI_ALLOCRMC int IVEC_DPMI pop ds jc lbl1 mov word ptr dwRet,dx ; vozvrat adresa obratnogo vyzova mov word ptr dwRet+2,cx jmp short lbl2 lbl1: mov word ptr dwRet,ax ; kod oshibki v registre ax mov word ptr dwRet+2,0 ; vozvratit' seg=0,esli proizoshla oshibka lbl2: } return dwRet ; } DWORD DPMI_FreeRMCallback(FARPROC lpfnCallback) { DWORD wRet ; _asm { mov dx,word ptr lpfnCallback mov cx,word ptr lpfnCallback+2 mov ax,DPMI_FREERMC int IVEC_DPMI jc lbl1 xor ax,ax lbl1: mov wRet,ax } return wRet ; } DWORD DPMI_GetRMVector(int iVector) { DWORD dwRet ; _asm { mov ax,DPMI_GETRMVEC mov bl,byte ptr iVector int 31h mov word ptr dwRet,dx mov word ptr dwRet+2,cx } return dwRet ; } void DPMI_SetRMVector(int iVector, DWORD lpfnRMISR) { _asm { mov ax,DPMI_SETRMVEC mov bl,byte ptr iVector mov dx,word ptr lpfnRMISR mov cx,word ptr lpfnRMISR+2 int 31h } } FARPROC GetPMVector(int iVector) { FARPROC dwRet ; _asm { mov bl,byte ptr iVector mov ah,35h int 21h mov word ptr dwRet,bx mov word ptr dwRet+2,es ; Sohranit' } return dwRet ; } void SetPMVector(int iVector, FARPROC lpfnISR) { _asm { push ds lds dx,lpfnISR mov al,byte ptr iVector mov ah,25h int 21h ; Ustanovit' nashu programmu ISR pop ds } } HANDLE AllocIntReflector(int iVector, FARPROC lpfnCallback) { DWORD dwDosMem ; LPSTR lpLowRMISR ; DWORD lpfnRMCallback ; _RMCS FAR *lpSaveRegs ; /* Raspredelit' pamyat' DOS dlya programmy obsluzhivaniya preryvaniya ISR, *rabotayushchej v real'nom rezhime */ dwDosMem = GlobalDosAlloc(16 + sizeof (int) + sizeof (_RMCS) ; if (dwDosMem == 0) return 0; lpLowRMISR = (LPSTR) MAKELONG(0,LOWORD(dwDosMem)) ; lpSaveRegs = (_RMCS FAR *) (&lpLowRMISR[16]) ; /* Raspredelit' obratnyj vyzov (callback), rabotayushchij v real'nom * rezhime */ lpfnRMCallback = DPMI_AllocateRMCallback((FARPROC)lpfnCallback, lpSaveRegs) ; if (HIWORD((DWORD)lpfnRMCallback == 0) { GlobalDosFree(LOWORD(dwDosMem)) ; return 0; } /* Sgenerirovat' kod v nizhnih adresah pamyati (tol'ko 6 bajtov)*/ lpLowRMISR[0] = 0x9A ; /* Vyzov ukazatelya na FAR */ *((DWORD FAR *)&(lpLowRMISR[1])) = lpfnRMCallback ; lpLowRMISR[5] = 0xCF ; /*IRET */ *((int FAR *)&(lpLowRMISR[6])) = iVector ; /* Ustanovit' svyaz' s vektorom preryvanij real'nogo rezhima */ DPMI_SetRMVector(iVector,MAKELONG(0,HIWORD(dwDosMem))) ; return (HANDLE) LOWORD(dwDosMem) ; /* vozvrat obrabotchika-otrazhatelya */ } void FreeIntReflector(HANDLE hReflector) { LPSTR lpLowRMISR ; DWORD lpfnRMCallback ; /* Poluchit' adres nizhnego ISR v zashchishchennom rezhime */ lpLowRMISR = (LPSTR)MAKELONG(0,(WORD)hReflector) ; /* Sleduet ubedit'sya, chto eto otrazhatel' */ if ((lpLowRMISR[0] != 0x9A) || (lpLowRMISR[5] != 0xCF)) return ; /* vyhod, esli ne otrazhatel' */ /* Vybrat' adres obratnogo vyzova i osvobodit' obratnyj vyzov */ lpfnRMCallback = *((DWORD FAR *)&((lpLowRMISR[1])) ; DPMI_FreeRMCallback(lpfnRMCallback) ; /* Osvobodit' programmu obsluzhivaniya preryvanij real'nogo rezhima*/ GlobalDosFree((WORD)hReflector) ; } /*XP< LibMain - osnovnaya bibliotechnaya tochka vhoda */ * * ENTRY (vhod) * * EXIT (vyhod) * * RETURNS (vozvrat) * Esli inicializaciya zavershaetsya uspeshno prinimaet znachenie, ravnoe * TRUE, v protivnom sluchae - FALSE * * WARNINGS (preduprezhdeniya) * * CALLS (vyzovy) * * NOTES (primechanie) * Nastoyashchaya bibliotechnaya tochka vhoda nahoditsya v assemblernom module * LIBENTRY.ASM, a v dannuyu tochku prosto peredaetsya upravlenie * */ BOOL FAR PASCAL LibMain(HANDLE hInstance /* obrabotchik bibliotechnogo ekzemplyara*/ ,WORD wDataSeg /* segment dannyh po umolchaniyu */ ,WORD cbHeap /* razmer dinamicheskoj oblasti po umolchaniyu */ ,LPSTR lpszCmdLine) ; /* komandnaya stroka */ /*>*/ { lpszCmdLine = lpszCmdLine ; /* Izbegat' preduprezhdeniya -W4 */ wDataSeg = wDataSeg ; cbHeap = cbHeap ; hInstance = hInstance ; /* |to mozhet ponadobit'sya pozzhe dlya dostupa k resursam iz nashego *ispolnitel'nogo modulya */ return TRUE ; } /*XP< WEP - procedura vyhoda v sisteme Windows */ * * ENTRY (vhod) * fSystemExit ukazyvaet na zavershenie sessii v sisteme Windows. V * protivnom sluchae proishodit tol'ko razgruzka dannoj biblioteki DLL. * RETURNS (vozvrat) * Vsegda vozvrashchaetsya znachenie 1 * * WARNINGS (preduprezhdeniya) * Iz-za oshibok v sisteme Windows 3.0 i bolee rannih versiyah (a * vozmozhno i v bolee pozdnih versiyah) dannaya funkciya dolzhna byt' *pomeshchena v fiksirovannyj segment. |ti zhe oshibki privodyat k tomu, chto * znachenie DS somnitel'no, a poetomu nel'zya ego ispol'zovat' (takzhe * kak i lyubye staticheskie dannye). * * V lyubom sluchae, nesomnenno ne nado nichego delat' v etoj tochke. * * CALLS (vyzovy) * Net * NOTES (primechaniya) * |to standartnaya procedura vyhoda DLL. * */ int FAR PASCAL WEP(int fSystemExit) /*>*/ { fSystemExit = fSystemExit /* Izbegat' preduprezhdeniya -W4 */ return 1 ; /* vsegda ukazyvaet na uspeshnoe zavershenie */ } int EXPORT FAR PASCAL BogusCheck(void) { BYTE bPortVal ; _asm { mov dx,FAKE_PORT in al,dx ; Prisutsvuet fiktivnoe ustrojstvo ? mov bPortVal,al } return ((bPortVal & 0x80) == 0) ; /* Vozvrashchaet znachenie TRUE, esli ustrojstvo prisutstvuet */ } void EXPORT FAR PASCAL BogusStart(HWND hWnd, WPARAM wParam) { wParamEvent = wParam ; hWndEvent = hWnd ; if (!lpfnPrevISR) { /* Sohranit' predydushchuyu programmu ISR i zagruzit' novuyu */ _asm cli lpfnPrevISR = GetPMVector(INT_DEV) ; SetPMVector(INT_DEV,(FARPROC)IntSvcRtn) ; _asm sti /* Sohranit' predydushchuyu programmu ISR real'nogo rezhima i *otrazit' na novuyu */ lpfnPrevRMISR = DPMI_GetRMVector(INT_DEV) ; hReflector = AllocIntReflector(INT_DEV,(FARPROC)BogusCallback) ; /* Razreshit' preryvanie i nachat' operaciyu vvoda-vyvoda na *ustrojstve */ _asm { cli in al,PIC01 ; razreshit' preryvanie and al,NOT INT_MASK out PIC01,al sti mov al,NOT FAKE_CTL_START mov dx,FAKE_PORT out dx,al ;nachat' operaciyu vvoda-vyvoda na ustrojstve } } } int EXPORT FAR PASCAL BogusGetEvent(void) { WORD wCountRet ; _asm { mov ax,SEG wCount mov es,ax xor ax,ax xchg ax,es:wCount ;poluchit' schetchik, ustanovit' v nul' mov wCountRet,ax } return wCountRet ; } void EXPORT FAR PASCAL BogusStop(void) { hWndEvent - 0x0000 ; /*komanda dlya ISR "zavershit' rabotu"*/ if (!lpfnPrevISR) return ; /* vozvratit'sya, esli programma ne startovala */ _asm { mov dx,FAKE_PORT l1: in al,dx rcr al,1 jnc l1 ; cikl, esli zanyato cli in al,PIC01 or al,INT_MASK out PIC01,al ; maskirovat' uroven' preryvaniya sti } DPMI_SetRMVector(INT_DEV, lpfnPrevRMISR) ; /* Vosstanovit' vektor real'nogo rezhima */ FreeIntReflector(hReflector) ; /* Osvobodit' otrazhatel' */ SetPMVector(INT_DEV, lpfnPrevISR) ; /* Vosstanovit' vektor zashchishchennogo rezhima */ lpfnPrevISR = NULL ; } /* konec fajla*/ _____________________________________________________________________

    Listing 4. Programma bogus.c

    Interfejs sistemy MS-DOS dlya zashchishchennogo rezhima

CHtoby ustanovit' svyaz' s vektorom real'nogo rezhima iz koda sistemy Windows zashchishchennogo rezhima, neobhodimo rabotat' s interfejsom sistemy MS-DOS dlya zashchishchennogo rezhima (MS-DOS Protected Mode Interface - DPMI). (Tekushchaya versiya DPMI predstavlyaet soboj uroven' 1.0, no sistema Windows naibolee polno realizuet tol'ko uroven' 0.9. Nekotorye funkcii urovnya 1.0 realizovany v sisteme Windows 3.1.) Funkciya DPMI_SetRMVector vyzyvaet interfejs DPMI, chtoby ustanovit' vektor real'nogo rezhima. Mozhno videt', chto interfejs DPMI vzaimodejstvuet cherez registry (registr AX soderzhit funkcional'nyj kod) i INT31h. Avtor vklyuchil vysokourovnevyj interfejs v dannuyu i drugie funkcii DPMI (dostupen tol'ko na diske kodov ili v interaktivnom rezhime), chtoby mozhno bylo imet' dostup k interfejsu DPMI iz yazyka Si i vydelil kod, napisannyj na yazyke assembler, na sluchaj, esli vozniknet neobhodimost' ispol'zovat' chto-to otlichnoe ot kompilyatora Si firmy Microsoft. Funkciya DPMI_AllocateRMCallback vyzyvaet interfejs DPMI, chtoby raspredelit' obratnyj vyzov (callback), predstavlyayushchij soboj adres, vyzyvaemyj iz real'nogo rezhima, kotoryj peredaet upravlenie kodu zashchishchennogo rezhima. Naprimer, programma TSR sistemy MS-DOS mozhet vyzvat' kod v biblioteke DLL sistemy Windows cherez obratnyj vyzov. Funkciya DPMI_AllocateRMCallback prinimaet dva parametra: adres koda zashchishchennogo rezhima, kotoryj budet vyzyvat'sya obratno, i registrovuyu strukturu, kotoraya obnovlyaetsya pri vypolnenii real'nogo obratnogo vyzova, takim obrazom kod zashchishchennogo rezhima mozhet issledovat' soderzhimoe registrov real'nogo rezhima vo vremya obratnogo vyzova. Funkciya DPMI_FreeRMCallback osvobozhdaet vse struktury, kotorye byli raspredeleny v rezul'tate obrashcheniya k funkcii DPMI_AllocateRMCallback. Funkciya DPMI_FreeRMCallback dolzhna vyzyvat'sya tol'ko togda, kogda bol'she net neobhodimosti v obratnom vyzove.

    Programma ISR v real'nom rezhime

Nesmotrya na to, chto avtor rekomendoval obespechivat' razdel'nuyu programmu ISR v real'nom rezhime, v dannom primere eta rekomendaciya ne byla vypolnena. Vmesto etogo, avtor predostavil programmy, neobhodimye pri realizacii programmy ISR na yazyke Si. Fakticheski, dannyj primer ustanavlivaet svyaz' s preryvaniyami real'nogo rezhima tol'ko dlya togo, chtoby pereklyuchit' CPU v zashchishchennyj rezhim dlya obrabotki preryvaniya. Takovo po umolchaniyu povedenie sistemy Windows, kogda s preryvaniyami real'nogo rezhima ne ustanavlivaetsya svyaz' voobshche, takim obrazom avtor rassmatrivaet neskol'ko ciklov, kotorye ne imeyut nikakogo drugogo naznacheniya, krome kak pokazat', kakim obrazom vse rabotaet. Rassmotrim kod dlya tochki vhoda BogusStart. Po sushchestvu on rabotaet tak zhe, kak rabotal by v sisteme MS-DOS. Kod sohranyaet staroe znachenie preryvaniya, obespechivaet svyaz' s tekushchim znacheniem i podaet ustrojstvu znak nachat' rabotu. Odnako vmesto obespecheniya svyazi tol'ko s vektorom zashchishchennogo rezhima, on ustanavlivaet svyaz' kak s vektorom real'nogo rezhima, tak i s vektorom zashchishchennogo rezhima. Ustanavlivaya svyaz' s vektorom real'nogo rezhima, kod vyzyvaet AllocIntReflector, chtoby obespechit' ssylku vektora preryvanij real'nogo rezhima na obratnyj vyzov, kotoryj prosto obrashchaetsya k programme ISR zashchishchennogo rezhima. Tochka vhoda BogusStart podaet znak ustrojstvu nachinat' rabotu odinakovym obrazom pri oboih rezhimah raboty: zashchishchennom i real'nom. Ona razmaskiruet bit IRQ dlya kontrollera PIC i podaet znak ustrojstvu nachinat' rabotu, zapisyvaya 1 v bit START porta upravleniya ustrojstvom. Kak tol'ko prilozhenie obrashchaetsya k dannoj programme, nachinaetsya obrabotka preryvanij i registraciya soobshchenij v sootvetstvii s programmoj ISR. Programma BogusStop trivial'na i prosto otklyuchaet ustrojstvo i razryvaet svyazi, ustanovlennye programmoj BogusStart. Itak, ostalos' privesti primer prikladnoj programmy, chtoby pokazat' rabotu operacij vvoda-vyvoda.

    Prilozhenie WINTEST

Prilozhenie wintest.c, pokazyvayushchee rabotu vvoda-vyvoda (sm. listing 5), sostoit glavnym obrazom iz nemodul'nogo dialogovogo bloka, v kotorom nepreryvno vysvechivaetsya kolichestvo preryvanij, obrabotannyh s nachala raboty programmy. Programma MainDlgProc vyzyvaet programmu BogusStart vo vremya vypolneniya WM_INITDIALOG, peredavaya v kachestve parametra obrabotchik okna dialogovogo bloka. Programma ISR registriruet soobshcheniya k dannomu obrabotchiku v teh sluchayah, kogda schetchik preryvanij izmenyaetsya ot nulya k edinice. Programma MainDlgProc sohranyaet tekushchee summarnoe znachenie schetchika v peremennoj wCountTotal. Vsyakij raz, kogda dialog poluchaet soobshchenie WM_COMMAND s parametrom wParam, ravnym IDM_BOGUSEVENT, programma obnovlyaet summarnyj schetchik, otobrazhaemyj v dialogovom bloke. Sleduet otmetit', chto hotya programma ISR registriruet soobshchenie tol'ko togda, kogda schetchik izmenyaetsya ot nulya k edinice, vozmozhna (i ves'ma veroyatno) obrabotka kolichestva preryvanij do togo, kak soobshchenie WM_COMMAND fakticheski budet peredano dialogovoj procedure. Metodika, pokazannaya v dannoj programme, pri kotoroj programma ISR registriruet soobshchenie tol'ko pri pervom perehode, a programma BogusCheck chistit schetchik, obespechivaet tochnyj podschet kolichestva preryvanij, dazhe esli na urovne prilozheniya nel'zya uchest' kazhdoe preryvanie v moment ego vozniknoveniya. Pri vypolnenii dannoj programmy mozhno nablyudat', chto schetchik preryvaniya v dialogovom bloke nepreryvno uvelichivaetsya, ukazyvaya kolichestvo vypolnennyh operacij vvod-vyvoda. _____________________________________________________________________ #include #include "bogus.h" #include "wintest.h" HANDLE hPgmInstance ; #define IDM_BOGUSEVENT 0x3000 void CenterWindow(HWND hWnd) { int xSize, ySixe, xPos, yPos ; RECT rc ; xSize = GetSystemMetrics(SM_CXSCREEN) ; ySize = GetSystemMetrics(SM_CYSCREEN) ; GetWindowRect(hWnd, &rc) ; xPos = (xSize - (rc.right - rc.left)) / 2 ; yPos = (ySize - (rc.bottom - rc.top)) / 2 ; SetWindowRect(hWnd, NULL, xPos, yPos, 0, 0, SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOZORDER) ; } LRESULT _loadds FAR PASCAL MainDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { static WORD wCountTotal = 0; WORD wCount ; lParam = lParam ; switch (msg) { case WM_INITDIALOG: RemoveMenu(GetSystemMenu(hwndDlg,0), SC_CLOSE,MF_BYCOMMAND) ; BogusStart(hwndDlg, IDM_BOGUSEVENT) ; break ; case WM_SHOWWINDOW: if (wParam) CenterWindow(hwndDlg) ; break ; case WM_COMMAND: switch (wParam) { case IDM_BOGUSEVENT: wCount = BogusGetEvent() ; while 9wCount) { wCountTotal += wCount ; wCount = BogusGetEvent() ; } SetDlgItemInt(hwndDlg, IDM_COUNT, wCountTotal, FALSE); break ; case IDCANCEL: EndDialog(hwndDlg, 0) ; break ; } break ; default: return FALSE ; } return TRUE ; } int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, intnCmdShow) { hPgmInstance = hInstance ; hPrevInstance = hPrevInstance ; lpCmdLine = lpCmdLine ; nCmdShow = nCmdShow ; if (!hPrevInstance) { if (BogusCheck()) { if (MessageBox(0, "Press OK to begin bogus I/O", "WinTest", MB_OKCANCEL|MB_APPLMODAL) == IDOK) { DialogBox(hPgmInstance, "MainDlg", 0, (FARPROC) MainDlgProc) ; BogusStop() ; } } else MessageBox(0, "Bogus device not found", "WinTest", MB_ICOMMAND|MB_OK|MB_APPLMODAL) ; } else MessageBox(0, "Another instance already running", "WinTest", MB_ICONEXCLAMATION|MB_OK|MB_APPLMODAL) ; return 0 ; } /* konec fajla */ ______________________________________________________________________

    Listing 5. Programma wintest.c.

    Drajver virtual'nogo ustrojstva

Fajl vxd2.asm (listingi 6 i 7) predstavlyaet soboj ishodnyj kod drajvera fiktivnogo ustrojstva. Sleduet otmetit', chto dlya togo, chtoby postroit' etot drajver, neobhodimo imet' komplekt drajverov ustrojstv (Device Driver Kit - DDK) sistemy Windows firmy Microsoft, t.k. kod napisan dlya 32-bitovogo assemblera, predusmotrennogo v komplekte DDK (MASM5). Rezul'tiruyushchij modul' mozhet byt' skomponovan tol'ko DDK-komponovshchikom LINK386 i utilitoj poslekomponovochnoj obrabotki ADDHDR. Krome togo, dannyj ishodnyj kod ssylaetsya na opredelennoe kolichestvo vklyuchaemyh fajlov (include files), kotorye vhodyat v sostav tol'ko komplekta DDK. Kak bylo ukazano, tipichnyj drajver VxD soderzhit obyazatel'nye vklyuchaemye fajly, a krome togo on nachinaetsya s vyzova makrosa Declare _Virtual_Device, kotoryj sozdaet blok dannyh, opisyvayushchij virtual'nyj drajver dlya yadra sistemy Windows. |tot blok dannyh, fakticheski, - edinstvennoe oboznachenie, eksportiruemoe iz drajvera VxD. Vse ostal'nye tochki vhoda yavlyayutsya proizvodnymi ot dannyh, soderzhashchihsya vnutri. Krome vsego prochego, dannyj makros opisyvaet imya ustrojstva, poryadok ego inicializacii i ego tochki vhoda. Virtual'nyj drajver VxD mozhet obsluzhivat' zaprosy prilozhenij kak v real'nom, tak i v zashchishchen- nom rezhimah. Tochki vhoda dlya takogo obsluzhivaniya takzhe opisyvayutsya dannym makrosom. _____________________________________________________________________ PAGE ,132 title VxD2B.ASM - Primer drajvera ustrojstva #2b ;EM VxD2B - Primer drajvera ustrojstva #2b ; ; Copyright 1992, Cherry Hill Software ; All rights reserved ; ; SUMMARY (Rezyume) ; Dannyj drajver imitiruet preryvaemoe ustrojstvo. Port ; upravleniya (vyhod) imeet sleduyushchee naznachenie bitov: ; ; Bit 0 - Nachat' (Start) vvod-vyvod. Zapis' nulya v dannyj bit ; nachinaet peresylku vvoda-vyvoda. Peresylka dlitsya ; okolo 1/10 sekundy. Zapis' edinicy v etot bit ne ; daet rezul'tata. ; ; Bit 1 - Ustrojstvu posylaetsya EOI. Zapis' nulya v dannyj bit ; privodit k posylke priznaka "Konec preryvaniya" (End- ; of-interrupt - EOI) ustrojstvu i udalyaet lyuboj zapros ; na otlozhennoe preryvanie. Zapis' edinicy v etot bit ne ; daet rezul'tata. ; Vse ostal'nye bity: Vsegda zapisyvayutsya edinicy dlya dal'nejshej ; sovmestimosti. ; ; Pri chtenii porta sleduyushchie znacheniya vozvrashchayutsya: ; ; Bit 0 - Pervonachal'no prisvaivaetsya znachenie 1, bit ; sbrasyvaetsya, kogda bit 1 vyhodnogo porta sbrasyvaetsya, ; i ustanavlivaetsya, kogda dobavlyaetsya zapros na ; preryvanie. Dannyj bit raven nulyu, kogda ustrojstvo ; peredaet dannye i ustanavlivaetsya v 1, chtoby ukazat', ; kogda peredacha zavershena. ; ; Bit 1 - Pervonachal'no prisvaivaetsya znachenie 1, bit ; sbrasyvaetsya, kogda dobavlyaetsya zapros na preryvanie i ; ustanavlivaetsya, kogda ustrojstvo udalyaet zapros na ; preryvanie. Znachenie dannogo bita, ravnoe nulyu, ; ukazyvaet na otlozhennoe preryvanie, bit ustanavlivaetsya ; v 1, esli net otlozhennogo preryvaniya. ; ; Vse ostal'nye bity: vozvrashchaemoe znachenie ignoriruetsya dlya ; dal'nejshej sovmestimosti. ; ; WARNINGS (Preduprezhdeniya) ; ; .386p .xlist include vmm.inc include debug.inc include v86mmgr.inc include vpicd.inc include ..\include\bogus.inc .list VM_Not_Executable equ VM_Not_Executeable ; acckk; subttl VxD Declaration/Definition page VxD2B_Init_Order equ VNETBIOS_Init_Order+100 ; Dannaya operaciya vypolnyaetsya posle zapuska virtual'noj seti VxD2B_Device_ID equ Bogus_Device_ID Declare_Virtual_Device VXD2, 1, 0, Vxd2B_Control, VxD2B_Device_ID, \ VxD2B_Init_Order VxD_DATA_SEG ; ; Struktura deskriptora virtual'nogo preryvaniya ; ; Dannaya struktura peredaetsya VPIDC_Virtualize_IRQ. V dannoj ; strukture opisyvaetsya uroven' preryvaniya, procedura preryvaniya ; apparatnyh sredstv, i procedura, kotoruyu vyzyvaet VPICD, kogda ; preryvanie dispetchiruetsya v virtual'noj mashine VM i kogda VM ; vozvrashchaetsya iz preryvaniya. ; IRQD VPICD_IRQ_Descriptor hIRQ dd -1 ; obrabotchik IRQ hOwner dd -1 ; obrabotchik, vladeyushchij VM hTimeout dd 0 ; obrabotchik k obratnomu vyzovu po tajm-autu bFakeData dd 01111111b ; imitirovat' dannye porta vvoda-vyvoda VxD_DATA_ENDS subttl Dispatch VxD Control page VxD_LOCKED_CODE_SEG BeginProc CheckOwner, NO_LOG cmp ebx,hOwner jne short col ret ; vyjti, esli vyzyvaetsya vladelec col: cmp hOwner,-1 jne short co2 ; propustit', esli vyzov ne vladel'ca mov hOwner,ebx ;ustanovit' vladel'ca ret co2: mov al,-1 ret EndProc CheckOwner BeginProc TimeoutProc mov hTimeout,0 ;pochistit' obrabotchik cmp edx,hOwner ; vse eshche tot zhe vladelec? jne short tol ; propustit', esli net test bFakeData,FAKE_STAT_BUSY ;otlozhennyj vvod-vyvod? jnz short tol ; propustit', esli net cmp hOwner,-1 ; imeetsya li vladelec? je short tol ; propustit', esli net mov eax,hIRQ mov ebx,hOwner VxDcall VPICD_Set_Int_Request ;dobavit' preryvanie mov al,bFakeData and al,NOT (FAKE_STAT_IRQ) ; ukazyvaet takzhe v porte sostoyaniya or al,FAKE_STAT_BUSY ; ukazyvaet, chto bol'she ne zanyato mov bFakeData,al tol: ret End Proc TimeoutProc ;IP Port_IO_Callback - vypolnyaet dostup k FAKE_PORT ; ; ENTRY (vhod) ; EAX - vyhodnoe znachenie (dlya vyhodnyh operatorov) ; EBX - obrabotchik k tekushchemu VM ; ECX - tip operacii vvoda-vyvoda ; DS,ES - FLAT ; ; EXIT (vyhod) ; EAX - vhodnoe znachenie (dlya vhodnyh operatorov) ; ; WARNINGS (preduprezhdeniya) ; ; NOTES (primechaniya) ; Sleduet otmetit', chto my dazhe ne smotrim registrovyj frejm ; klienta. ; ; My prosto chitaem i uvelichivaem. ; ; CALLS (vyzovy) BeginProc Port_IO_Callback, NO_LOG Dispatch_Byte_IO Fall_through,Port_Output_Callback Port_Input_Callback: call CheckOwner jc short ioexit mov al,bFakeData or bFakeData,FAKE_STAT_ERROR ; pochistit' otlozhennuyu oshibku ioexit: ret Port_Output_Callback: call CheckOwner jc short ioexit ;ignorirovat' vvod-vyvod, esli ne vladelec test al,FAKE_CTL_START jnz short,poc1 ;propustit', esli ne nachinaetsya vvod-vyvod test bFakeData,FAKE_START_BUSY jz short,poc1 ;propustit', esli uzhe zanyato test bFakeData,FAKE_START_IRQ jz short,poc1 ;propustit', esli otlozhennoe IRQ push eax push edx and bFakeData,NOT (FAKE_STAT_ERROR) ; predpolozhit' oshibku mov eax,100 ; obratnyj vyzov v 1/10 sekundy mov edx,hOwner ; peredat' vladel'ca obratnomu vyzovu mov esi,OFFSET32 TimeoutProc VMMcall Set_VM_Time_Out pop edx pop eax or esi,esi jz short,poc1 ;propustit', esli oshibka and bFakeData,NOT (FAKE_STAT_BUSY) ; ukazat' na zanyatost' or bFakeData,FAKE_STAT_ERROR ; v protivnom sluchae pochistit' indikaciyu oshibki mov hTimeout,esi ;sohranit' obrabotchik tajm-auta poc1: test al,FAKE_CTL_EOI jnz short poc2 ; propustit', esli ne posylaetsya EOI test bFakeData,FAKE_STAT_IRQ ;preryvanie otlozheno? jnz short poc2 ; propustit', esli net or bFakeData,FAKE_STAT_IRQ ;pokazat', chto preryvanie uzhe ne- otlozhenoe push eax mov eax,hIRQ VxDcall VPICD_Clear_Int_Request pop eax poc2: ret EndProc Port_IO_Callback ; ECX == 0 if unmasking (enabling), ECX != 0 if masking (disabling). BeginProc VxD2_Mask_Change_Proc call CheckOwner jc short mcp9 ; ignorirovat', esli net vladel'ca jcxz mcp9 ; propustit', esli ne maskirovano (vklyucheno) ; ; Vladelec osvobozhdaet upravlenie. Razreshaetsya drugoj VM vojti v ; sistemu. ; mov hOwner,-1 ; pochistit' vladel'ca mcp9: ret EndProc VxD2_Mask_Change_Proc ; Vyzyvaetsya, kogda vypolnyaetsya programma ISR BeginProc VxD2_VInt_Proc mov eax,High_Pri_Device_Boost VMMCall Adjust_Exec_Priority ;povyshennyj prioritet dlya nachal'noj obrabotki ret EndProc VxD2_VInt_Proc ; vyzyvaetsya pri vozvrate iz programmy ISR (IRETs) BeginProc VxD2_IRET_Proc mov eax,-(High_Pri_Device_Boost) VMMCall Adjust_Exec_Priority ;vosstanovit' prioritet ret EndProc VxD2_IRET_Proc ifdef DEBUG BeginProc VxD2B_Debug_Query Trace_Out "VxD2 has no debug command support." clc ret End Proc VxD2B_Debug_Query endif ; ; VxD2B_Control ; CtlDisp macro x Control_Dispatch x, VxD2B_&x endm Begin_Control_Dispatch VxD2B CtlDisp Device_Init ifdef DEBUG CtlDisp Debug_Query endif End_Control_Dispatch VxD2B VxD_LOCKED_CODE_ENDS VxD_CODE_SEG VxD_CODE_ENDS subttl VxD Initialization page VxD_ICODE_SEG page ; EP VxD2B_Device_Init - Nekriticheskaya inicializaciya ustrojstva ; ; ENTRY (vhod) ; EBP - frejm klienta ; EBX - sistemnyj obrabotchik VM ; DS,ES - FLAT ; ; EXIT (vyhod) ; SUCCESS (uspeshnyj) ; Carry clear ("net perenosa") ; FAILURE (avarijnyj) ; Carry set ("est' perenos") ; ; ; WARNINGS (preduprezhdeniya) ; ; NOTES (primechaniya) ; ; CALLS (vyzovy) ; BeginProc VxD2B_Device_Init Debug_Out "VxD2B_Device_Init" mov edi,OFFSET32 IRQD VxDcall VPICD_Virtualize_IRQ ; virtualizirovat' preryvanie jc short vdi1 ; vyhod, esli oshibka mov hIRQ,eax ; sohranit' obrabotchik mov edx,FAKE_PORT mov esi,OFFSET32 Port_IO_Callback VMMCall Install_IO_Handler VMMCall Enable_Global_Trapping ; clc ; net oshibki vdi1: ret EndProc VxD2B_Device_Init VxD_ICODE_ENDS VxD_REAL_INIT_SEG VxD2B_Real_Init LABEL FAR ;vyzyvaetsya pered tem, kak sistema Windows vhodit v zashchishchennyj rezhim mov ax,Device_Load_OK ;pozvolyaet VxD zagruzit'sya xor bx,bx ; net isklyuchennyh (Exclude) stranic EMM xor si,si ; net elementov ekzemplyarov dannyh ; peredat' edx nemodificirovannym ret VxD_REAL_INIT_ENDS END VxD2B_Real_Init ; Konec fajla _____________________________________________________________________

    Listing 6. Programma vxd2.asm

_____________________________________________________________________ LIBRARY VXD2 DESCRIPTION 'Enhanced Windows VXD2(B) Device (Version 1.0)' EXETYPE DEV386 SEGMENTS _LTEXT PRELOAD NONDISCARDABLE _LDATA PRELOAD NONDISCARDABLE _ITEXT CLASS 'ICODE' DISCARDABLE _IDATA CLASS 'ICODE' DISCARDABLE _TEXT CLASS 'PCODE' NONDISCARDABLE _DATA CLASS 'PCODE' NONDISCARDABLE EXPORTS VXD2_DDB @1 _____________________________________________________________________

    Listing 7. Programma vxd2.def

    Sobytiya, upravlyayushchie ustrojstvom

Po mere togo, kak sistema Windows v svoej rabote prohodit razlichnye stadii, nachinaya so stadii inicializacii samoj sistemy, cherez inicializaciyu virtual'noj mashiny VM i tak dalee, kazhdyj ustanovlennyj drajver VxD vyzyvaetsya neodnokratno, a imenno odin raz na kazhduyu stadiyu. V tablice, privedennoj nizhe, perechislyayutsya fazy sistemy Windows i glavnye sobytiya, dlya kotoryh vyzyvaetsya kazhdyj drajver VxD. Tablica Upravlyayushchie soobshcheniya drajvera VxD -------------------+-------------------------------------------------- Sys_Critical_Init | Pervoe upravlyayushchee sobytie; preryvaniya | otklyuchayutsya. Drajver VxD opredelyaet gotovnost' | ustrojstva. -------------------+-------------------------------------------------- Device_Init | Preryvaniya razreshayutsya; drajver VxD inicializiru- | et ustrojstvo; mogut byt' vyzvany programmy i | drajvery sistemy DOS. -------------------+-------------------------------------------------- Init_Complete | Ukazyvaet, chto vse drajvery VxD proshli stadiyu | Device_Init. -------------------+-------------------------------------------------- System_Exit | Ukazyvaet, chto sistema Windows gotovitsya k | zakrytiyu i vozvratu v sistemu DOS. Pamyat' dlya | sistemy DOS vosstanovlena v sostoyanie, kotoroe | bylo do raboty sistemy Windows. -------------------+-------------------------------------------------- Sys_Critical_Exit | Poslednee upravlyayushchee sobytie; preryvaniya | otklyuchayutsya. -------------------+-------------------------------------------------- Create_VM | Vyzyvaetsya pered momentom sozdaniya virtual'noj | mashiny VM; drajver VxD ukazyvaet, dostupny li | resursy dlya sozdaniya virtual'noj mashiny VM. -------------------+-------------------------------------------------- VM_Critical_Init | Vtoraya faza sozdaniya virtual'noj mashiny VM. -------------------+-------------------------------------------------- VM_Init | Tret'ya faza sozdaniya virtual'noj mashiny VM. Sys_VM_Init | Drajver VxD mozhet avarijno zavershit' rabotu | virtual'noj mashiny VM. -------------------+-------------------------------------------------- Query_Destroy | Pozvolyaet drajveru VxD predupredit' pol'zovatelya | o zatrudneniyah pri razrushenii virtual'noj mashiny | VM. -------------------+-------------------------------------------------- VM_Terminate | Pervaya stadiya uspeshnogo zaversheniya virtual'noj Sys_VM_Terminate | mashiny VM. Esli eto sistemnaya virtual'naya | mashina VM, to soobshchenie ukazyvaet, chto | proizvoditsya normal'noe, vyzvannoe pol'zovatelem, | zavershenie sistemy Windows. -------------------+-------------------------------------------------- VM_Not_Executeable | Virtual'naya mashina VM zakryvaetsya. Pervaya stadiya | avarijnogo zaversheniya virtual'noj mashiny VM. -------------------+-------------------------------------------------- Drajver VxD primera vypolnyaet upravlenie tol'ko fazoj Device_Init. Na etoj stadii ustanavlivaetsya svyaz' s portom vvoda- vyvoda i urovnem preryvaniya 11, a takzhe proizvoditsya ih virtualizaciya. Obychno drajver VxD virtualiziruet porty vvoda-vyvoda i preryvanie v sootvetstvii s fizicheskim apparatnym oborudovaniem. No v dannom sluchae drajver VxD mozhet virtualizirovat' i delaet eto s portom i preryvaniem, kotorye ne imeyut sootvetstvuyushchego podklyuchennogo apparatnogo oborudovaniya. Kod Install_IO_Handler vyzyvaetsya, chtoby virtualizirovat' edinstvennyj port vvoda-vyvoda. Zatem vsyakij raz, kogda osushchestvlyaetsya dostup k opisannomu portu vvoda-vyvoda iz virtual'noj mashiny VM, programma upravleniya virtual'noj mashinoj sistemy Windows (Virtual Machine Manager - VMM) vyzyvaet obratno drajver VxD dlya togo, chtoby razreshit' emu imitirovat' operacii vvoda-vyvoda. Kod VPICD_Virtualize_IRQ vyzyvaetsya, chtoby virtualizirovat' uroven' preryvaniya. Vypolnyaya ego, mozhno imitirovat' preryvanie apparatnogo oborudovaniya (v chastnosti IRQ 11) v virtual'noj mashine.

    "Fiktivnoe" ustrojstvo

Kogda k portu vvoda-vyvoda (141) ustrojstva osushchestvlyaetsya dostup virtual'noj mashinoj VM (libo v real'nom, libo v zashchishchennom rezhime), to mashina vyzyvaet programmu drajvera VxD Port_IO_Callback (sm. Listing 6). V etoj programme podprogramma Dispatch_Byte_IO svodit bol'shoe kolichestvo vozmozhnyh tipov dostupa vvoda-vyvoda (a imenno: byte, word, dword, string i t.d.) k dvum: bajtovomu vvodu i bajtovomu vyvodu. Dlya ustrojstva iz primera bajtovyj vvod predstavlyaet soboj chtenie iz registra sostoyaniya ustrojstva. On vozvrashchaet prosto peremennuyu, kotoraya sohranyaetsya v pamyati. Bajtovyj vyvod - nemnogo bolee slozhnaya operaciya, tak kak predstavlyaet fakticheskuyu rabotu ustrojstva. Pri zapuske ustrojstva takzhe zapuskaetsya tajmer, kotoryj vypolnyaet obratnyj vyzov (k kodu TimeoutProc) v techenii 1/10 sekundy i ustanavlivaet sostoyanie BUSY. Esli vyvod podtverzhdaet priem preryvaniya, to proizvoditsya ochistka virtual'nogo zaprosa na preryvanie putem vyzova koda VPICD_Clear_Int_ Request i ochistka sostoyaniya v registre sostoyaniya. Obratnyj vyzov koda TimeoutProc predstavlyaet zavershenie operacii vvoda-vyvoda na ustrojstve i imenno v dannyj moment on modeliruet preryvanie apparatnogo oborudovaniya k virtual'noj mashine VM putem vyzova koda VPICD_Clear_Int_Request i ochistki sostoyaniya zanyatosti ustrojstva. Drajver ustrojstva v prilozheniyah dostest i wintest budet obychno obrabatyvat' preryvanie putem podtverzheniya priema ego (posylaya EOI) i povtornogo zapuska processa na vsem protyazhenii snova. Sleduet otmetit' procedury VxD2_VInt_Proc i VxD2_IRET _Proc. Na dannye dve procedury sushchestvuet ssylka v strukture, kotoraya peredaetsya kodu VPICD_Virtualize_IRQ. Oni vyzyvayutsya v nachale i konce processa virtualizacii preryvaniya v virtual'nuyu mashinu VM. Vse ih funkcii svodyatsya k uvelicheniyu i sohraneniyu prioriteta virtual'noj mashiny VM, kotoraya vremenno obrabatyvaet dannoe preryvanie. Takim sposobom drajver VxD mozhet upravlyat' prioritetom virtual'noj mashiny VM, kotoraya schitaetsya sootvetstvuyushchej. (Vsegda zhelatel'no, chtoby programma obsluzhivaniya preryvaniya v lyuboj virtual'noj mashine VM imela prioritet vyshe, chem prioritet obychnoj obrabotki v drugih virtual'nyh mashinah VM.)

    Ustanovka drajvera VxD

Posle postroeniya drajvera VxD, do pervogo obrashcheniya k nemu programmy Windows neobhodimo dobavit' ego kak stroku device= v sekciyu [386Enh] koda system.ini. Sistema Windows dolzhna byt' zapushchena zanovo, chtoby vklyuchit' drajver VxD i virtual'noe ustrojstvo. Posle etogo, mozhno vypolnyat' i testirovat' prilozheniya dostest i wintest.

    Zaklyuchenie

Hotya drajvery ustrojstv sistemy Windows kazhutsya v nastoyashchee vremya ochen' slozhnymi, obychnye i virtual'nye drajvery ustrojstv predostavlyayut ogromnoe kolichestvo vozmozhnostej. Odnako sleduet uchityvat', naskol'ko bolee slozhnymi oni dolzhny byt' na mashine MIPS, ekspluatiruyushchej sistemu Windows NT i kod emulyatora 80x86, chtoby obespechit' rabotu virtual'noj mashiny sistemy MS-DOS.

Last-modified: Mon, 14 Feb 2000 17:32:34 GMT
Ocenite etot tekst: