Budapesti M˝ uszaki ´ es Gazdas´ agtudom´ anyi Egyetem Villamosm´ ern¨ oki ´ es Informatikai Kar
Sz´ eless´ av´ u H´ırk¨ ozl´ es ´ es Villamos´ agtan Tansz´ ek
TDK dolgozat Szoftverteljes´ıtm´ eny ´ es optimaliz´ aci´ o vizsg´ alata kernel m´ od´ u eszk¨ ozkezel˝ o programmal
K´esz´ıtette: Hajnal Erik pit6dc
Konzulens: dr. Csurgai-Horv´ ath L´ aszl´ o BME-HVT
Budapest 2012.
Tartalomjegyz´ ek 1 Bevezet´ es 1.1 A dolgozat t´em´ aj´ anak, c´elj´anak ismertet´ese . . . . . . . . . . . . 1.2 A m´er´esi k¨ or¨ ulm´enyek r¨ovid ismertet´ese . . . . . . . . . . . . . .
2 2 3
2 A m´ er´ esek el˝ ok´ esz´ıt´ ese 2.1 Kernel m´ od´ u programoz´as - ´attekint´es . . . . 2.2 A driverfejleszt´es alapjai, legfontosabb l´ep´esei 2.2.1 A driver szolg´ altat´as elk´esz´ıt´ese . . . . 2.2.2 A driver-le´ır´ o .inf f´ajlok . . . . . . . . 2.2.3 Az eszk¨ ozilleszt˝ok digit´alis al´a´ır´asa . . 2.3 A k´ artya API-j´ anak bemutat´asa . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
6 6 6 7 18 21 21
3 A m´ er´ esek bemutat´ asa 3.1 A tesztprogramok . . . . . . . . . . . . . . . 3.1.1 A keretprogram . . . . . . . . . . . . 3.1.2 ArrayCopy . . . . . . . . . . . . . . 3.1.3 Parallel . . . . . . . . . . . . . . . . 3.1.4 Abstraction . . . . . . . . . . . . . . 3.1.5 Fibonacci . . . . . . . . . . . . . . . 3.1.6 StringCPP11 . . . . . . . . . . . . . 3.2 M´er´esek az alapbe´ all´ıt´assal . . . . . . . . . 3.3 A c´elplatform . . . . . . . . . . . . . . . . . 3.4 Pufferes biztons´ agi ellen˝orz´esek kikapcsol´asa 3.5 Az architekt´ ura specifik´al´asa . . . . . . . . 3.6 P´ arhuzamos k´ odgener´al´as . . . . . . . . . . 3.7 A C++11 ´es a mozgat´o konstruktorok . . . 3.8 A GCC ford´ıt´ oval el´ert eredm´enyek . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
27 27 27 28 29 29 29 30 31 32 34 35 36 38 43
¨ 4 Osszefoglal´ as
. . . . . . . . . . . . . .
45
1
1 1.1
Bevezet´ es A dolgozat t´ em´ aj´ anak, c´ elj´ anak ismertet´ ese
A mai vil´ agunkban teljesen term´eszetes, hogy a szoftverfejleszt˝ok a szoftvereket magas szint˝ u programoz´asi nyelvek ´es vizu´alis fejleszt˝oeszk¨oz¨ok seg´ıts´eg´evel k´esz´ıtik. Azonban ehhez a m¨og¨ottes technol´ogia, a magas nyelv˝ u programokat futtat´ o alacsony szint˝ u keretrendszerek magas fok´ u optimaliz´ alts´ aga sz¨ uks´eges – vagy nagyon er˝os hardver. Rengeteg apr´obb alkalmaz´ asn´ al szinte ´eszrevehetetlen a k¨ ul¨onbs´eg egy alacsony ´es egy magas szint˝ u nyelv seg´ıts´eg´evel k´esz´ıtett program k¨oz¨ott a mai rendk´ıv¨ ul gyors hardvereknek k¨ osz¨ onhet˝ oen. Viszont a teljes´ıtm´enyig´enyes alkalmaz´asokn´al, amiket az im´ent eml´ıtett technol´ogi´ak seg´ıts´eg´evel manaps´ag nagyon gyorsan el lehet k´esz´ıteni, sokszor tapasztalhatunk probl´em´akat a sebess´eggel. Ezen dolgozat alapvet˝ o k´erd´ese az, hogy val´oban nyugodt sz´ıvvel felejthetj¨ uk el a k¨ ul¨ onb¨ oz˝ o optimaliz´ aci´ os lehet˝os´egeket ´es b´ızhatunk mindent a ford´ıt´onkra vagy esetleg vannak-e bizonyos lehet˝os´egeink, amelyek ak´ar u ´jak (p´eld´aul az u ´j C++11 szabv´ any), ak´ ar r´egiek, ´am m´ara kiszorultak a f´okuszb´ol (p´eld´aul a ford´ıt´ asi kapcsol´ ok). Azonban adja mag´ at a k´erd´es: el´eg ”okosak” a ford´ıt´oink? Val´oban ki tudnak tal´ alni magukt´ ol mindent? Vagy lehet hogy ´erdemes lenne bizonyos be´ all´ıt´ asokat manu´ alisan elv´egezni, ami esetleg jav´ıthatna az alkalmaz´asaink teljes´ıtm´eny´en? Term´eszetesen a szoftverfejleszt´es ´atalakul´as´anak folyamat´at meg´ all´ıtani nem lehet, a c´elunk sokkal ink´abb p´ar olyan m´eg / m´ar kev´esb´e ismert lehet˝ os´eg megvizsg´ al´ asa, amelyekkel az alkalmaz´asaink teljes´ıtm´eny´et lehetne jav´ıtani. A szoftver eszk¨ oz¨ ok min˝os´ıt´es´et sz´am´ıt´og´ep-hardverrel t´amogatott m´er´esekkel k´ıv´ anjuk elv´egezni. Ehhez egy, az RTD-USA ´altal k´esz´ıtett DM7520 t´ıpus´ u, PCI buszra illeszked˝o m´er´es-adatgy˝ ujt˝o k´arty´at haszn´alunk fel, amelyhez saj´ at illeszt˝ oprogram is k´esz¨ ult a nagyobb flexibilit´as ´erdek´eben. Ez´ altal a szoftver fut´ asi teljes´ıtm´eny´et egzakt m´odon, a hardver k¨ uls˝o portjain kiadott impulzusok m˝ uszeres m´er´es´evel fogjuk elv´egezni. Egy ilyen t´em´ aban nagyon neh´ez teljes k¨or˝ u, minden platform fejleszt˝ oeszk¨ oz p´ aros´ıt´ ast megvizsg´alni, ez´ert a mi v´alaszt´asunk a Windows 7 (x64) platformra ´es a Visual Studio leg´ ujabb, 2012-es v´altozat´ara esett. Emellett egy-egy r¨ ovid ¨ osszehasonl´ıt´as kedv´e´ert a GCC ´altal el˝o´all´ıtott k´odot is megvizsg´ altuk, ´ am ezzel nem foglalkoztunk r´eszleteiben. A dolgozat eredm´enyei seg´ıthetnek m´elyebben meg´erteni a programfejleszt´esi eszk¨ oz¨ ok be´all´ıt´asainak hat´asait, valamint az optimaliz´aci´os lehet˝ os´egek kihaszn´ alhat´ os´ ag´ at. A dolgozat k´et nagyobb r´eszre bonthat´o: az els˝o r´eszben a m´er´esekhez vezet˝o u ´t bemutat´ asa tal´ alhat´ o. Egy ´attekint´es a kernel-m´od´ u programoz´asr´ol, annak el˝ onyeir˝ ol ´es h´ atr´ anyair´ ol, a driverfejleszt´esr˝ol, a m´er´es menet´er˝ol. A m´ asodik r´esz k¨ ul¨ onb¨ oz˝o lehet˝os´egeket pr´ob´al felt´arni a performancia n¨ ovel´ese ´erdek´eben. G´ orcs˝ o al´a ker¨ ul a Windows priorit´askezel´ese, k¨ ul¨onb¨oz˝o kev´esb´e ismert, keveset haszn´alt ford´ıt´asi kapcsol´o, ´es a C++ vil´ag leg´ ujabb a´tt¨ or´ese, a C++11 szabv´ any ny´ ujtotta optimaliz´aci´os lehet˝os´egek is.
2
1.2
A m´ er´ esi k¨ or¨ ulm´ enyek r¨ ovid ismertet´ ese
Mivel a m´er´esek sor´ an pont a nagyon apr´o, ´am m´egis jelen l´ev˝o sebess´egk¨ ul¨ onbs´egeket szeretn´em kimutatni, a Windows ´altal biztos´ıtott szoftveres id˝ om´er´es pontoss´ aga nem elegend˝o. Ez´ert a m´er´esekhez egy data acquisition (m´er´es-adatgy˝ ujt˝ o) k´arty´at haszn´alunk, amelyhez egy - kifejezetten erre a c´elra - ´ altalunk fejlesztett illeszt˝oprogramot haszn´alunk. A k´ artya egy, a RealTime Devices Inc. ´altal k´esz´ıtett DM7520-as t´ıpus´ u m´er´es-adatgy˝ ujt˝ o k´ artya. Ez egy PC/104 form´atum´ u eszk¨oz, amelyet az´ert v´ alasztottunk a m´er´esek elv´egz´es´ere, mert mind a hardver, mind a szoftver dokument´ aci´ oja rendelkez´es¨ unkre ´allt, ami n´elk¨ ul¨ozhetetlen volt a k´es˝obb ismertet´esre ker¨ ul˝ o saj´ at kernel-m´od´ u driver meg´ır´as´ahoz. Mivel a k´arty´at asztali sz´ am´ıt´ og´epben haszn´ altuk, egy PCI-PC/104 konverter seg´ıts´eg´evel helyezt¨ uk be az asztali g´epbe. A m´er´es-adatgy˝ ujt˝o k´artya ki/bemenetei egy I/O termin´ al k´ arty´ an kereszt¨ ul ´erhet˝ok el, amelyre m´ar tetsz˝oleges k¨ uls˝o m´er˝ oeszk¨ oz csatlakoztathat´ o. Az 1. ´abr´an l´athat´o a DM7520 k´artya a PCI konvertermodulra szerelve, tov´abb´a a hozz´a csatlakoztatott I/O modul.
1. ´ abra A m´er´es-adatgy˝ ujt˝ o k´artya PCI konverterrel ´es termin´al-k´arty´aval
A DM7520 egy univerz´ alis, PCI buszra illesztett m´er´es-adatgy˝ ujt˝o eszk¨oz, amely anal´ og ki- ´es bemenetekkel rendelkezik, univerz´alis id˝oz´ıt˝o ´aramk¨or¨oket tartalmaz, digit´ alis I/O portjai ´es m´eg sz´amos egy´eb, ´altalunk nem haszn´alt er˝ oforr´ asa van. A dolgozatban kit˝ uz¨ ott c´elok el´er´es´ehez csup´an a digit´alis I/O portok haszn´ alat´ ara volt sz¨ uks´eg, amelyeknek a korl´atlan hozz´af´er´es´et biztos´ıtja a k´es˝ obbiekben ismertet´esre ker¨ ul˝o eszk¨ozmeghajt´o szoftver. A k´ artya digit´ alis I/O modulja k´et, egyenk´ent nyolc bites csatorn´at biztos´ıt sz´ amunkra, ami a m´er´eseinkben kulcsszerepet t¨olt be. Ezek a portok a PCI 3
interf´eszen kereszt¨ ul mem´ ori´aba ´agyazott eszk¨ozk´ent ´erhet˝ok el, ´es kimenetk´ent programozva logikai 1 vagy 0 ´ert´ek ´ırhat´o ki r´ajuk. A logikai jel porton t¨ ort´en˝ o megjelen´es´et a k´artya hardver fel´ep´ıt´ese csak minim´alis m´ert´ekben korl´ atozza, ´eppen a mem´ori´aba ´agyazott m˝ uk¨od´es miatt. A PCI busz arbitr´ aci´ oja ugyanakkor nagyobb m´ert´ek˝ u, r´aad´asul el˝ore nem kisz´ am´ıthat´ o k´esleltet´est visz be a rendszerbe, de ennek az ideje is nagys´ agrendekkel kisebb, mint a tesztelni k´ıv´ant szoftverek fut´asi idejei. Mindazon´ altal a teszthardver az alaplapi vez´erl˝ok¨on k´ıv¨ ul semmilyen egy´eb PCI eszk¨ ozt nem tartalmazott, hogy az ebb˝ol ad´od´o m´er´esi hib´akat kik¨ usz¨ ob¨ olhess¨ uk. A hardveres id˝ om´er´es menete a k¨ovetkez˝o: A k´arty´ahoz egy I/O egys´eget kapcsolunk, amelynek az egyik kimenet´et r´ak¨otj¨ uk egy oszcilloszk´opra. A m´erend˝ o tartom´ anyba val´ o bel´ep´eskor ´es annak elhagy´asakor a kimeneten megjelen´ıt¨ unk egy impulzust, majd a k´et impulzus k¨oz¨otti t´avols´agot leolvasva megkapjuk a m´erni k´ıv´ ant id˝ot. Term´eszetesen az impulzus kiad´asa ´es az aktu´ alis algoritmus k¨ oz¨ ott m´eg eltelik egy kev´es id˝o am´ıg az ir´any´ıt´as visszaker¨ ul a driverb˝ ol a m´er˝oprogramba, azonban ezt az id˝ot is le tudjuk m´erni ´es ez´ altal pontos´ıtani a m´er´es¨ unket.
2. ´ abra - Egy m´er´es kin´ezete Az 1. ´ abr´ an egy konkr´et m´er´esnek a megjelen´es´et l´atjuk az oszcilloszk´opon. Az els˝ o impulzus az el˝ oz˝ o m´er´es v´eg´et jelzi (a m´er´esek ciklusban futnak, ´ıgy k¨ onnyebben m´erhet˝ oek a kontextusv´alt´asb´ol sz´armaz´o k´esleltet´esek). A m´ asodik impulzus jelzi az aktu´alis m´er´es kezdet´et, az utols´o pedig a m´er´es v´eg´et. A ciklusban fut´ o m´er´eseket a 2. ´abr´an l´athat´o m´odon gy˝ ujtj¨ uk ¨ossze.
4
3. ´ abra - A m´er´esek ¨osszegy˝ ujt´ese Itt m´ ar nem l´ atszik a m´er´esek k¨oz¨otti nanoszekundumos sz¨ unet, azonban milliszekundumban m´erhet˝ o ´ert´ekekn´el ez elhanyagolhat´o m´er´esi hiba. Az oszcilloszk´ op id˝ otartom´ any´ anak megfelel˝o m´ert´ek˝ u n¨ovel´es´evel el´erj¨ uk, hogy 5-10 m´er´est l´ assunk egyszerre, majd a m´ert id˝ok ´atlagol´as´aval meghat´arozzuk a v´egs˝ o id˝ ot.
5
2 2.1
A m´ er´ esek el˝ ok´ esz´ıt´ ese Kernel m´ od´ u programoz´ as - ´ attekint´ es
Miel˝ ott a k´ arty´ ahoz meg´ırt illeszt˝oprogramot bemutatn´ank, ny´ ujtan´ank egy r¨ ovid ´ attekint´est a kernel m´ odban val´o programoz´asr´ol, annak el˝onyeir˝ol ´es h´ atr´ anyair´ ol. Kernel m´ odban sokkal nagyobb a hatalma, ´es ez´altal a felel˝oss´ege is a programoz´ onak. [1] Nem lehet a ”trial-and-error” megk¨ozel´ıt´est alkalmazni, ugyanis itt a ”Program m˝ uk¨ od´ese le´allt” k´eperny˝o helyett minden apr´o hiba STOP hib´ ahoz (ismertebb nevein: k´ek hal´al, Blue Screen of Death) vezet. Lok´ alis g´epen dolgozva el kell felejteni a 21. sz´azadi debug eszk¨oz¨oket; m´eg egy hibak´ odot sem lehet ki´ırni a k´eperny˝ore, hiszen a STOP hiba azonnal megjelenik. A debug eszk¨ oz¨ okh¨ oz hasonl´oan b´ ucs´ ut kell inteni a CRT-nek (C RunTime), amelyet alapb´ ol minden felhaszn´al´oi-m´od´ u program haszn´al, ugyanis alapb´ol a main, illetve WinMain f¨ uggv´enyeinket a CRT h´ıvja meg. A CRT ´ altal biztos´ıtott, sz´eles k¨orben ismert ´es haszn´alt f¨ uggv´enyek ´es oszt´ alyok (printf, fopen, cout, vector, list, stb.) b´ar k¨ozvetlen¨ ul nem el´erhet˝oek kernel-m´ odb´ ol, rengetek CRT-beli f¨ uggv´eny el˝obb-ut´obb egy kernel-m´od´ u API h´ıv´ ask´ent v´egzi, ´ıgy sokszor elegend˝o n´emi kutat´as a megold´ashoz. P´eld´aul a printf f¨ uggv´eny kernel-m´ od´ u megfelel˝oje a DbgPrint f¨ uggv´eny, m´eg a param´eterei is ugyanazok (minim´alis k¨ ul¨onbs´egekkel). A Windows programoz´ asban j´artas emberek ´altal ismert Windows API sem el´erhet˝ o, azonban ezen h´ıv´ asok k´et r´eszre oszthat´oak. Az els˝o csoport olyan h´ıv´ asokat tartalmaz, amelyek azonnal, vagy minim´alis ellen˝orz´eseket k¨ovet˝oen tov´ abb´ıtja a h´ıv´ ast a kernelbe. Ezeket a f¨ uggv´enyeket a printf -hez hasonl´oan lehet haszn´ alni. Ebbe a csoportba tartoznak p´eld´aul a f´ajlokkal kapcsolatos f¨ uggv´enyek. A m´ asodik csoportban olyan f¨ uggv´enyek tal´alhat´oak, amelyek a kernel-m´od´ u fejleszt´esn´el irrelev´ ansak. Ilyen p´eld´aul a multim´edi´aval vagy az ablakokkal kapcsolatos h´ıv´ asok. Ezen funkci´okra kernel-m´odban egyszer˝ uen nincs sz¨ uks´eg, ´ıgy nagy r´esz¨ uknek nincs is kernel-m´od b´eli megfelel˝oj¨ uk. A legnagyobb API, ami a rendelkez´es¨ unkre ´all, az az u ´gynevezett Native API, ami a Windows API (r¨ oviden WINAPI) alatt helyezkedik el. A Native API-ban rengeteg alul-dokument´alt vagy egy´altal´an nem dokument´alt f¨ uggv´eny tal´ alhat´ o, ami tov´ abb nehez´ıti a kernel-m´od´ u fejleszt´est.
2.2
A driverfejleszt´ es alapjai, legfontosabb l´ ep´ esei
A driverfejeszt´es sokr´et˝ u feladat. Meg kell val´os´ıtani a hardver ´es a Windows k¨ oz¨ otti kommunik´ aci´ ot, amihez a hardver r´eszletesebb ismerete sz¨ uks´eges. Implement´ alni kell az eszk¨ oz m˝ uk¨od´es´ehez ´es kezel´es´ehez sz¨ uks´eges logik´at, egyfajta bels˝ o f¨ uggv´enycsoportot. L´etre kell hozni egy API-t, aminek seg´ıts´eg´evel a felhaszn´ al´ oi-m´ od´ u programok kommunik´alhatnak a driverrel. Sz¨ uks´eg van tov´ abb´ a a drivernek a ”le´ır´as´ara”, egy .inf f´ajlra, amely seg´ıts´eg´evel a Windows tudja, hogy pontosan mit is tartalmaz az illeszt˝ oprogram. Ezen fel¨ ul egy felhaszn´al´oi m´od´ u API-t is ´erdemes k´esz´ıteni, ugyanis felhaszn´ al´ oi-m´ odb´ ol a kernel-m´od´ u API-nkat csak a DeviceIoControl
6
nev˝ u f¨ uggv´enyen kereszt¨ ul ´erhetj¨ uk el, ami semminem˝ u k´enyelmet illetve valid´ aci´ ot nem biztos´ıt. 2.2.1
A driver szolg´ altat´ as elk´ esz´ıt´ ese
Az illeszt˝ oprogramok a Windows sz´am´ara service-ekk´ent, magyarul szolg´ altat´ asokk´ent jelennek meg. Az elk´esz¨ ult driver a legminimalisztikusabb esetben k´et f´ ajlk´ent jelenik meg. Lesz egy .inf f´ajlunk, ami le´ırja az illeszt˝ oprogramot, a m´ asik pedig egy .sys f´ajl, ami a futtathat´o k´odot tartalmazza. Ez a r´esz a .sys f´ajl elk´esz´ıt´es´ebe ny´ ujt betekint´est. A Native API-ra ´ep¨ ul˝ o alkalmaz´asok, ´ıgy az illeszt˝oprogramok bel´ep´esi pontja is alap´ertelmez´es szerint a DriverEntry f¨ uggv´eny, melynek szignat´ ur´aja a k¨ ovetkez˝ o: 1 NTSTATUS D r i v e r E n t r y (PDRIVER OBJECT p D r i v e r O b j e c t , PUNICODE STRING pRegistryPath )
Ehelyett azonban a legt¨ obb helyen ezt lehet l´atni: 1 extern ”C” NTSTATUS D r i v e r E n t r y (PDRIVER OBJECT p D r i v e r O b j e c t , PUNICODE STRING p R e g i s t r y P a t h )
Ennek az oka az, hogy a drivereket k¨ ul¨on ford´ıt´oprogramokkal kell leford´ıtani, amelyek viszont alapb´ol C nyelven v´arj´ak a forr´ask´odot. Azonban a C++ ny´ ujtotta rugalmass´ ag (p´eld´aul a v´altoz´ok ”ak´arhol” megengedett deklar´ aci´ oja) miatt rendszerint m´egis C++ lesz a v´alasztott nyelv, ebben az esetben azonban gondoskodni kell r´ola, hogy a DriverEntry f¨ uggv´eny C f¨ uggv´enynek megfelel˝ o dekor´ aci´oval ker¨ ulj¨on bele a .sys f´ajlba. Az els˝ o param´eter a driver¨ unk viselked´es´et szab´alyoz´o DRIVER OBJECT strukt´ ur´ ara egy mutat´ o, a m´asodik pedig a driver¨ unk registry kulcs´ahoz az el´er´esi u ´t. A DriverEntry f¨ uggv´eny feladata ¨osszekapcsolni a Windows-t a driverrel. A DriverEntry fut´ asakor az illeszt˝oprogramunk m´eg nem m˝ uk¨od´esre k´esz, hanem pont ekkor k´esz¨ ul az indul´asra, ebben hivatott a DriverEntry seg´ıt˝o kezet ny´ ujtani a Windows sz´ am´ara. A visszat´er´esi ´ert´ek egy NTSTATUS, ami longk´ent van defini´alva, ezen kereszt¨ ul tudjuk jelezni a m˝ uveleteink sikeress´eg´et. A mi konkr´et DriverEntry-nk a k¨ovetkez˝ok´eppen n´ez ki: 1 extern ”C” NTSTATUS D r i v e r E n t r y (PDRIVER OBJECT p D r i v e r O b j e c t , PUNICODE STRING p R e g i s t r y P a t h ) 2 { 3 p D r i v e r O b j e c t −>D r i v e r U n l o a d = DM7520 DriverUnload ; 4 p D r i v e r O b j e c t −>D r i v e r E x t e n s i o n −>AddDevice = DM7520 AddDevice ; 5 p D r i v e r O b j e c t −>MajorFunction [ IRP MJ PNP ] = DM7520 PnP ; 6 p D r i v e r O b j e c t −>MajorFunction [ IRP MJ CLOSE ] = DM7520 Close ; 7 p D r i v e r O b j e c t −>MajorFunction [ IRP MJ CREATE]= DM7520 Create ; 8 p D r i v e r O b j e c t −>MajorFunction [ IRP MJ DEVICE CONTROL ] = DM7520 DeviceControl ; 9 return STATUS SUCCESS ; 10 }
Ahogy az a k´ odb´ ol is l´athat´o, a PDRIVER OBJECT strukt´ ura f¨ uggv´enymutat´ okon kereszt¨ ul ´eri el az illeszt˝oprogramunk funkci´oit. A 7
DriverUnload a driver rendszerb˝ol val´o elt´avol´ıt´asakor ker¨ ul megh´ıv´asra, ennek felel˝ oss´ege minden az eg´esz drivernek (nem csak egy-egy konkr´et funkci´oj´anak) lefoglalt er˝ oforr´ as felszabad´ıt´ asa. Az AddDevice h´ıv´ as felel az´ert, hogy amikor egy u ´j, a mi driver¨ unk ´altal kezelt eszk¨ ozt csatlakoztatnak a sz´am´ıt´og´ephez, az eszk¨oz megfelel˝oen fel legyen telep´ıtve. A MajorFunction t¨ omb felel a k´artya f˝obb funkci´oinak implement´aci´oj´a´ert, ezekr˝ ol k´es˝ obb lesz sz´ o. A STATUS SUCCESS, ami 0-k´ent van defini´alva jelzi az ´altal´anos sikert. Amikor a DriverEntry visszat´er, a szolg´altat´asunk term´eszetesen nem ´all le, hanem pont hogy ilyenkor ´ all k´eszen a k¨ ul¨onb¨oz˝o h´ıv´asok fogad´as´ara. A k¨ ovetkez˝ o l´ep´es felk´esz´ıteni a drivert arra, hogy az eszk¨ozt behelyezik a rendszerbe, ez´ert felel az AddDevice f¨ uggv´eny, amely a mi eset¨ unkben a k¨ ovetkez˝ ok´eppen n´ez ki: 1 NTSTATUS DM7520 AddDevice (PDRIVER OBJECT p D r i v e r O b j e c t , PDEVICE OBJECT pdo ) { 2 PDEVICE OBJECT f d o ; 3 NTSTATUS s t a t u s = I o C r e a t e D e v i c e ( p D r i v e r O b j e c t , s i z e o f ( DEVICE EXTENSION) , NULL, FILE DEVICE UNKNOWN, FILE DEVICE SECURE OPEN , FALSE, &f d o ) ; 4 DEVICE EXTENSION∗ pdx = (DEVICE EXTENSION∗ ) fdo−>D e v i c e E x t e n s i o n ; 5 i f ( ! NT SUCCESS( s t a t u s ) ) { 6 return s t a t u s ; 7 } 8 pdx−>l o w e r D e v i c e O b j e c t = I o A t t a c h D e v i c e T o D e v i c e S t a c k ( fdo , pdo ) ; 9 i f ( ! pdx−>l o w e r D e v i c e O b j e c t ) 10 { 11 IoDeleteDevice ( fdo ) ; 12 return s t a t u s ; 13 } 14 fdo−>F l a g s |= DO POWER PAGABLE; 15 fdo−>F l a g s |= DO BUFFERED IO ; 16 fdo−>F l a g s &= ˜DO DEVICE INITIALIZING ; 17 s t a t u s = I o R e g i s t e r D e v i c e I n t e r f a c e ( pdo , &DM7520 GUID , NULL, &pdx −>i f n a m e ) ; 18 i f ( ! NT SUCCESS( s t a t u s ) ) 19 { 20 i f ( ! pdx−>i f n a m e . B u f f e r ) 21 { 22 R t l F r e e U n i c o d e S t r i n g (&pdx−>i f n a m e ) ; 23 } 24 I o D e t a c h D e v i c e ( pdx−>l o w e r D e v i c e O b j e c t ) ; 25 IoDeleteDevice ( fdo ) ; 26 return s t a t u s ; 27 } 28 s t a t u s = I o S e t D e v i c e I n t e r f a c e S t a t e (&pdx−>ifname , TRUE) ; 29 i f ( ! NT SUCCESS( s t a t u s ) ) 30 { 31 i f ( ! pdx−>i f n a m e . B u f f e r ) 32 { 33 R t l F r e e U n i c o d e S t r i n g (&pdx−>i f n a m e ) ; 34 } 35 I o D e t a c h D e v i c e ( pdx−>l o w e r D e v i c e O b j e c t ) ; 36 IoDeleteDevice ( fdo ) ; 37 return s t a t u s ; 38 } 39 return STATUS SUCCESS ; 40 }
8
Ez a kicsit testesebb f¨ uggv´eny egy nagyon kritikus illeszt˝ oprogramnak. A k´et kapott param´eter k¨oz¨ ul az els˝o a asodik pedig egy PDEVICE OBJECT PDRIVER OBJECT, a m´ egy mutat´ o. Ezen mutat´ o m¨og¨ott m´ar az ´eppen behelyezett ´es k´ıv´ ant eszk¨ oz tal´ alhat´ o.
pontja az m´ar ismert strukt´ ur´ara konfigur´alni
´ Erdemes m´eg a konkr´et implement´aci´o el˝ott n´eh´any sz´oban megeml´ıteni az u ´n. driver stacket, ami tulajdonk´eppen a driverek absztrakci´os r´etegei. Legalul helyezkednek el az u ´n. PDO-k, Physical Device Object, azaz maga a konkr´et fizikai eszk¨ oz. Ez a r´eteg felel az eszk¨ozzel val´o legalapvet˝obb kommunik´ aci´ oj´ a´ert, mint p´eld´aul az adatok k¨ uld´es´enek ´es fogad´as´anak implement´ aci´ oja, vagy ak´ ar az ´aramell´at´as szab´alyoz´asa. Ezzel a r´eteggel nek¨ unk nem sok dolgunk van, mivel a mi eszk¨oz¨ unk egy teljesen szabv´anyos PCI eszk¨ oz, a Windows alap´ertelmezett m´odszerei t¨ok´eletesen megfelelnek. Erre a szintre ´ep¨ ul a Filter Device Object, ami tulajdonk´eppen egy ”sz˝ ur˝o”, ez a r´eteg nem k¨ otelez˝ o egy illeszt˝oprogramhoz; itt k¨ ul¨onb¨oz˝o cache-el´eseket lehet p´eld´ aul megval´ os´ıtani. Az erre ´ep¨ ul˝o r´eteg az FDO, Function Device Object, ami tulajdonk´eppen a driver logik´aj´at tartalmazza, itt van megval´os´ıtva a kernel-m´ od´ u API. Sz¨ uks´eg szerint az FDO-ra ´ep¨ ulhet m´eg egy sz˝ ur˝or´eteg, azonban n´ alunk erre nincs sz¨ uks´eg. A k´ odban megjelenik k´et r¨ovid´ıt´es k´et v´altoz´on´ev form´aj´aban. Az fdo-ra a funkcion´alis illeszt˝oprogramunkat keresztelt PDEVICE OBJECT reprezent´ alja, ez´ert teljes m´ert´ekben mi vagyunk a felel˝osek. A pdo nev˝ u, szint´en PDEVICE OBJECT t´ıpus´ u v´altoz´onk pedig a fizikai eszk¨ozilleszt˝onkre mutat, ezt javar´eszt a Windows maga menedzseli. A f¨ uggv´enybe val´ o bel´ep´eskor a PDO m´ar l´etezik ´es k´eszen ´all arra, hogy r´ a´ep´ıts¨ unk a driver stack t¨ obbi r´esz´et. Els˝o l´ep´esk´ent l´etrehozzuk az alap FDOt (4). Ehhez csak p´ ar nagyon alapvet˝o param´eterre van sz¨ uks´eg¨ unk, mint az eszk¨ oz t´ıpusa, az ´ altalunk ig´enyelt extra t´arter¨ ulet, az u ´n. device extension m´erete, valamint minim´ alis biztons´aggal kapcsolatos param´eterek. A DEVICE EXTENSION egy t¨obb´e-kev´esb´e megszokott elnevez´ese annak az ´ altalunk defini´ alt strukt´ ur´anak, ami egy az eszk¨ozh¨oz csatolt adatokat tartalmazza, ennek a tartalma minden eseteben a konkr´et eszk¨oz¨on m´ ulik. A mi driver¨ unkben ez a strukt´ ura a k¨ovetkez˝ok´eppen van defini´alva: 1 struct DEVICE EXTENSION 2 { 3 PDEVICE OBJECT l o w e r D e v i c e O b j e c t ; 4 UNICODE STRING i f n a m e ; 5 v o l a t i l e UCHAR∗ l a s 0 ; 6 v o l a t i l e UCHAR∗ l a s 1 ; 7 v o l a t i l e UCHAR∗ l c f g ; 8 };
A lowerDeviceObject az FDO alatt elhelyezked˝o r´etegre mutat, ennek seg´ıts´eg´evel fogjuk tudni azt el´erni. Az ifname (interface name) tartalma az eszk¨ oz¨ unk interf´esz´enek neve, amin kereszt¨ ul majd a felhaszn´al´oi m´od´ u programok el´erik az eszk¨ ozt. A m´asik h´arom mez˝o m´ar a k´arty´ank konkr´et fel´ep´ıt´es´et t¨ ukr¨ ozi, ez a h´ arom mutat´o a k´artya h´arom mem´oriater¨ ulet´ere mutat, ezeken kereszt¨ ul fogunk majd tudni I/O m˝ uveleteket v´egezni. Az IoAttachDeviceToDeviceStack h´ıv´as (10) seg´ıts´eg´evel tudjuk az FDO-t 9
csatlakoztatni a driver stackre, majd a visszakapott PDEVICE OBJECT -et, ami a k¨ ozvetlen¨ ul alattunk elhelyezked˝o r´etegre mutat, elmentj¨ uk a unkbe. DEVICE EXTENSION -¨ Miut´ an l´etrehoztuk ´es csatlakoztattuk az FDO-t, term´eszetesen konfigur´ alni is kell azt. A legfontosabb l´ep´es a k¨ ul¨onf´ele flagek be´all´ıt´asa (16 18). H´ arom olyan csoportja van ezeknek a flageknek, amelyekre mindenf´elek´eppen oda kell figyelni. Az els˝o az ´aramell´at´as. A ´n. lapozhat´o eszk¨ozilleszt˝ok jele. Ez annyit DO POWER PAGABLE flag az u jelent, hogy sz¨ uks´eg eset´en a Windows kilapozhatja a driver¨ unket a mem´ ori´ ab´ ol, ami csak a sz´ am´ıt´og´ep m˝ uk¨od´es´ehez kritikus eszk¨oz¨ok eset´eben jelentene probl´em´ at. Senki nem szeretne arra v´arni, hogy a videok´artya drivere visszaker¨ ulj¨ on a mem´ori´aba, ´am egy m´er´es-adatgy˝ ujt˝o k´artya megnyit´ asakor ez nem jelent gondot. A m´ asodik be´ all´ıtand´ o flag az input/output m´odj´at szab´alyozza. Egy eszk¨ oznek h´ arom k¨ ul¨ onf´ele lehet˝os´ege van az I/O m˝ uveleteinek ul˝o belebonyol´ıt´ as´ ara. Az els˝ o a DO BUFFERED IO, ami a pufferen kereszt¨ ´es kimenetet jelenti. Ebben az esetben az eszk¨oz ´es a driver k¨oz´e a Windows automatikusan l´etrehoz egy puffert, amit ˝o maga kezel sz´amunkra. A m´asodik lehet˝ os´eg a a DO DIRECT IO, ami a DMA haszn´alat´aval ”k¨ozvetlen” utat biztos´ıt a k´ artya ´es a driver k¨oz´e az adatok sz´am´ara. A harmadik be´all´ıt´as, ami egyben az alap´ertelmezett is, az u ´gynevezett ”Neither” (egyik sem) megold´ as. Ebben az esetben a driver csak egy forr´as ´es egy c´el c´ımet l´at ´es neki kell megoldania a m´ asol´ ast. Azonban ez nagyon k¨or¨ ulm´enyes, l´ev´en hogy a k´et c´ım teljesen m´ as kontextusban van (j´o es´ellyel a c´el egy kernel-m´od´ u c´ım a mi driver¨ unkben, a forr´ as azonban szinte biztosan nem, hiszen mi egy FDO-ban vagyunk), pontosan emiatt ezt a megold´ast nagyon ritk´an haszn´ alj´ ak FDO-kban. A v´ alaszt´as az´ert esett a pufferelt megold´asra, mert sokkal kisebb az overhead az adatok mozgat´asakor, mint a DMA-s megold´as eset´en, az adataink alacsony m´erete (1-1 b´ajtot k¨ uld¨ unk egyszerre), miatt pedig a DMA am´ ugy sem ny´ ujtana semmi el˝onyt. Az utols´ o flag, ami n´elk¨ ul¨ozhetetlen, az a DO DEVICE INITIALIZING. Ez a flag alapb´ ol be van ´ all´ıtva, ´es am´ıg ezt ki nem t¨or¨olj¨ uk, az eszk¨ozt nem lehet elind´ıtani, ugyanis ez a flag jelzi, hogy az eszk¨oz m´eg konfigur´aci´o alatt all. A pontoss´ ´ ag kedv´e´ert ´erdemes megjegyezni, hogy az, hogy ezt a flaget az AddDevice f¨ uggv´eny k¨ ozep´en t¨or¨olj¨ uk, nem jelenti azt, hogy az eszk¨ozt azonnal el lehetne ind´ıtani, miel˝ ott a f¨ uggv´eny t¨obbi r´esze lefutna; az eszk¨oz elind´ıt´asa csak az AddDevice f¨ uggv´eny lefut´asa ut´an lehets´eges. K¨ ovetkez˝ o l´ep´esk´ent l´etrehozunk az eszk¨oz¨ unknek egy interf´eszt (19), illetve ahhoz egy nevet. Ezen a n´even kereszt¨ ul lehet el´erni az interf´eszt, amin kereszt¨ ul pedig a k´ arty´ aval val´o interakci´o lehets´eges. Ezt az interf´eszt hozz´a kell rendeln¨ unk egy konkr´et PDO-hoz, valamint sz¨ uks´eg van egy GUID-ra is, ami a mi eszk¨ oz¨ unket azonos´ıtja. A DM7520 GUID n´alunk egy egyszer˝ u konstans GUID, amit a Visual Studio be´ep´ıtett GUID gener´ator´aval k´esz´ıtett¨ unk. Nem el´eg azonban regisztr´alni egy interf´eszt, k¨ ul¨on l´ep´esk´ent enged´elyezni is kell azt (30). Amennyiben minden j´ ol ment, nincs m´as dolgunk mint jelezni az eszk¨oz hozz´ aad´ as´ anak siker´et a STATUS SUCCESS ´ert´ekkel val´o visszat´er´essel (41). A val´ o ´eletben term´eszetesen messze nem megy minden probl´em´ak n´elk¨ ul, ´ıgy tekints¨ uk ´ at r¨ oviden a WDK ´altal biztos´ıtott hibakezel´esi lehet˝os´egeket. 10
A driverfejleszt´eshez haszn´alt WDK f¨ uggv´enyek nagy t¨obbs´ege egy, m´ar eml´ıtett, NTSTATUS -t ad vissza, ezzel jelezve a m˝ uvelet eredm´eny´et. A WDK-ban haszn´ alt konvenci´ o szerint a negat´ıv ´ert´ekek hib´at, a nulla ´es a pozit´ıv ´ert´ekek pedig sikert jeleznek. Azonban a konkr´et ´ert´ekek jelent´es´enek ismerete n´elk¨ ul, az NT SUCCESS makr´o seg´ıts´eg´evel egy boolk´ent tekinthet¨ unk az adott st´ atuszra. Minden egyes rendszerh´ıv´ as ut´an c´elszer˝ u ellen˝orizni, annak sikeress´eg´et. Hiba eset´en minden f´elk´esz konfigur´aci´ot vissza kell vonni. Ezek ut´ an biztos´ıtanunk kell a k´arty´anak a legalapvet˝obb m˝ uveleteket, ilyen p´eld´ aul a k´ artya megnyit´ asa ´es bez´ar´asa. Ehhez azonban tudni kell, hogy hogyan m˝ uk¨ odnek a kernelben az u ¨zenetek. A k¨ ul¨onb¨oz˝o k´er´eseket u ¨zenetek form´aj´aban kapj´ ak meg a driverek, ezeknek a pontos neve IRP (Interrupt Request Packet), ezek tartalmazz´ ak a k´er´est, illetve a k´er´essel kapcsolatos alapvet˝o inform´aci´okat. Amikor egy driver egy adott k´er´est fogad, el kell d¨ontenie mit k´ıv´an vele tenni. Egy egyszer˝ ubb illeszt˝oprogramban javar´eszt k´etf´ele megold´asra van sz¨ uks´eg¨ unk. Az els˝ o a k´er´esnek a teljes´ıt´ese, ebben az esetben a mi driver¨ unk egy-az-egyben teljes´ıti a k´er´est: v´egrehajtja a k´ert m˝ uveletet, majd egy st´ atuszk´ odot mell´ekelve jelzi, hogy az adott k´er´es teljes´ıtve lett. A m´asodik gyakran haszn´ alt lehet˝ os´eg az u ´gynevezett ”Forward & Forget” nev˝ u m´odszer. Ebben az esetben mi nem kezelj¨ uk az adott k´er´est csup´an tov´abb´ıtjuk m´asnak. Ut´ obbira tipikus p´eld´ ak a k¨ ul¨onb¨oz˝o ´aramell´at´assal kapcsolatos k´er´esek, ahol egy FDO-nak rendszerint semmi dolga nincs (esetleg a k´artya alaphelyzetbe a´ll´ıt´ asa), azonban az als´ obb, fizikai eszk¨oz¨okkel foglalkoz´o r´etegnek igen jelent˝ os munk´ ajuk van. A k´er´esek teljes´ıt´ese pedig a k¨ ul¨onb¨oz˝o I/O m˝ uveletekn´el j¨ on el˝ o leggyakrabban, hiszen a Windows nem tudhatja, hogy a mem´ ori´ ab´ ol pontosan melyik c´ımre is k´ene ker¨ ulnie az adatoknak, ennek a meg´ allap´ıt´ as´ a´ert m´ ar az eszk¨ ozilleszt˝o a felel˝os. An´elk¨ ul, hogy minden f¨ uggv´enyt r´eszletezn´enk, a k´et elj´ar´ast haszn´al´o egyegy f¨ uggv´enyt bemutatunk: 1 NTSTATUS DM7520 PnP StopDevice (PDEVICE OBJECT fdo , PIRP p i r p ) 2 { 3 p i r p −>I o S t a t u s . S t a t u s = STATUS SUCCESS ; 4 I o S k i p C u r r e n t I r p S t a c k L o c a t i o n ( p i r p ) ; // Forward and F o r g e t 5 DEVICE EXTENSION∗ pdx = (DEVICE EXTENSION∗ ) fdo−>D e v i c e E x t e n s i o n ; 6 return I o C a l l D r i v e r ( pdx−>l o w e r D e v i c e O b j e c t , p i r p ) ; 7 }
Az itt l´ athat´ o f¨ uggv´eny a k´arty´ank meg´all´ıt´as´a´ert felel, ami p´eld´aul az eszk¨ oz szoftveres elt´ avol´ıt´ asakor j¨ohet el˝o. Kernel-m´od´ u u ¨zenetkezel˝o f¨ uggv´enyekre j´ ol jellemz˝ o m´ odon egy NTSTATUS -szal fogjuk jelezni a sikert. Param´eterk´ent megkapjuk szok´as szerint az FDO-t, valamint a m´ar eml´ıtett IRP-re egy mutat´ ot. A st´ atuszt a m´ ar megszokott m´odon alapb´ol j´ohiszem˝ uen STATUS SUCCESS -re all´ıtjuk. ´ Ezut´an megh´ıvjuk az IoSkipCurrentIrpStackLocation f¨ uggv´enyt, amely annyit csin´al, hogy az IRP-nket eggyel lejjebb k¨ uldi a driver stacken. Azonban az alattunk l´ev˝o drivert m´eg meg is kell h´ıvni, amihez el˝osz¨or is meg kell azt tal´alni (5), majd az IoCallDriver h´ıv´ assal deleg´aljuk az - imm´aron j´o helyen l´ev˝o - k´er´es¨ unket az alattunk elhelyezked˝ o driver-r´eteg fel´e. 11
A k¨ ovetkez˝ o k´ odr´eszlet pedig egy k´er´es teljes´ıt´es´et mutatja be: 1 NTSTATUS DM7520 Create (PDEVICE OBJECT fdo , PIRP p i r p ) 2 { 3 I oCom ple teR eque st ( p i r p , IO NO INCREMENT) ; 4 p i r p −>I o S t a t u s . S t a t u s = STATUS SUCCESS ; 5 return STATUS SUCCESS ; 6 }
Itt a kulcs l´ep´es az IoCompleteRequest h´ıv´as, amellyel jelezhetj¨ uk, hogy az adott k´er´est mi teljes m´ert´ekben teljes´ıtett¨ uk ´es az IRP-vel tov´abbi munka nincs. Ennek megh´ıv´ asa el˝ ott van term´eszetesen maga a k´er´est teljes´ıt˝o logika, amely jelen eset¨ unkben u ¨res, ugyanis itt a k´arty´at megnyit´o k´er´est kezelj¨ uk, amelynek sor´ an azt kell biztos´ıtanunk, hogy a h´ıv´o f´el gond n´elk¨ ul tudjon ´ırni ´es olvasni akar´ o k´er´eseket k¨ uldeni. Mivel az eszk¨ozilleszt˝ot saj´at c´elokra fejlesztett¨ uk, eltekintett¨ unk a k¨ ul¨onf´ele biztons´agi ´ovint´ezked´esekt˝ol amelyeket itt lehetne tenni (pl. blokkolni hogy egyszerre t¨obben is ´ır´asi enged´ellyel rendelkezzenek). A f¨ uggv´eny m´asodik param´eter´evel a k´er´est eredetileg felad´o sz´ alnak a priorit´ as´ at lehetne n¨ovelni (cser´ebe az´ert, hogy eddig a k´er´est teljes´ıt´es´ere kellett v´ arnia), azonban mi ezzel nem k´ıv´antunk ´elni. Mivel l´enyeg´eben semmit nem csin´ altunk, ez´ert a st´atuszt gond n´elk¨ ul ´all´ıthatjuk unk vissza a f¨ uggv´enyb˝ol. STATUS SUCCESS -re, majd t´erhet¨ ´ Erdemes m´eg k¨ ul¨ on sz´ ot ejteni a Plug and Play u ¨zenetekr˝ol. A k¨ ul¨onf´ele Plug and Play u ¨zeneteket (mint pl. a k´artya behelyez´ese vagy elt´avol´ıt´asa) egyszerre, egy f¨ uggv´enyben tudjuk kezelni (ld. a DriverEntry f¨ uggv´eny). 1 NTSTATUS DM7520 PnP (PDEVICE OBJECT fdo , PIRP p i r p ) 2 { 3 PIO STACK LOCATION s t a c k = I o G e t C u r r e n t I r p S t a c k L o c a t i o n ( p i r p ) ; 4 switch ( s t a c k −>MinorFunction ) 5 { 6 case IRP MN START DEVICE : return DM7520 PnP StartDevice ( fdo , pirp ) ; 7 case IRP MN REMOVE DEVICE : return DM7520 PnP RemoveDevice ( fdo , pirp ) ; 8 } 9 // D e f a u l t − ” Forward and F o r g e t ” 10 IoSkipCurrentIrpStackLocation ( pirp ) ; 11 DEVICE EXTENSION∗ pdx = (DEVICE EXTENSION∗ ) fdo−>D e v i c e E x t e n s i o n ; 12 return I o C a l l D r i v e r ( pdx−>l o w e r D e v i c e O b j e c t , p i r p ) ; 13 }
Ez a f¨ uggv´eny fogadja az ¨osszes Plug and Play u ¨zenetet, majd sz¨ uks´eg szerint deleg´ alja az u ¨zeneteket tov´abbi f¨ uggv´enyeknek. Az eddigi kezel˝o f¨ uggv´enyeinkben mindig pontosan tudtuk, hogy milyen u ¨zenettel van dolgunk, ez´ert sosem volt sz¨ uks´eg az IRP tartalm´at vizsg´alni, azonban itt m´ar csak annyit tudunk, hogy valamilyen Plug and Play esem´eny t¨ort´ent. Az IRP tartalmazza a pontos esem´enyt is, ehhez azonban hozz´a kell f´ern¨ unk az IRP-hez, amit az IoGetCurrentIrpStackLocation h´ıv´assal tudunk megtenni. Ezek ut´ an m´ ar meg tudjuk vizsg´alni az u ´n. Minor code-j´at a k´er´esnek, amelyb˝ ol m´ ar kider¨ ul, hogy a PnP-en bel¨ ul pontosan mi is t¨ort´ent. A mi driver¨ unkben a PnP esem´enyek k¨oz¨ ul csak kett˝o ´erdekel, amikor elind´ıtj´ ak a k´ arty´ at, illetve amikor elt´avol´ıtj´ak azt a rendszerb˝ol, ´ıgy ezeket egy-egy k¨ ul¨ on f¨ uggv´eny kezeli. Minden egy´eb PnP k´er´esre a m´ar kor´abban bemutatott Forward & Forget megold´ast alkalmazzuk. 12
Az eszk¨ oz elind´ıt´ asakor a DM7520 PnP StartDevice f¨ uggv´enyt h´ıvjuk meg, ez felel a k´ artya felk´esz´ıt´es´e´ert, hogy tudja fogadni a k¨ ul¨onb¨oz˝o szoftver-eredet˝ u k´er´eseket (ilyen p´eld´ aul az ´ır´ as ´es az olvas´as). A f¨ uggv´eny a k¨ovetkez˝ok´eppen n´ez ki: 1 NTSTATUS DM7520 PnP StartDevice (PDEVICE OBJECT fdo , PIRP p i r p ) 2 { 3 DEVICE EXTENSION∗ pdx = (DEVICE EXTENSION∗ ) fdo−>D e v i c e E x t e n s i o n ; 4 I o S e t D e v i c e I n t e r f a c e S t a t e (&pdx−>ifname , TRUE) ; 5 6 PIO STACK LOCATION s t a c k = I o G e t C u r r e n t I r p S t a c k L o c a t i o n ( p i r p ) ; 7 PCM PARTIAL RESOURCE LIST t r a n s l a t e d = &s t a c k −>P a r a m e t e r s . S t a r t D e v i c e . A l l o c a t e d R e s o u r c e s T r a n s l a t e d −>L i s t [ 0 ] . PartialResourceList ; 8 9 PCM PARTIAL RESOURCE DESCRIPTOR r e s o u r c e s = t r a n s l a t e d −> PartialDescriptors ; 10 ULONG count = t r a n s l a t e d −>Count ; 11 f o r (ULONG i = 0 ; i < count ; ++i ) 12 { 13 i f ( r e s o u r c e s [ i ] . Type == CmResourceTypeMemory ) 14 { 15 switch ( r e s o u r c e s [ i ] . u . Memory . Length ) 16 { 17 case 5 1 2 : // LAS0 18 pdx−>l a s 0 = ( v o l a t i l e UCHAR∗ ) MmMapIoSpace ( r e s o u r c e s [ i ] . u . Memory . S t a r t , 5 1 2 , MmNonCached) ; 19 break ; 20 case 2 5 6 : // LCFG 21 pdx−>l c f g = ( v o l a t i l e UCHAR∗ ) MmMapIoSpace ( r e s o u r c e s [ i ] . u . Memory . S t a r t , 2 5 6 , MmNonCached) ; 22 break ; 23 case 1 6 : // LAS1 24 pdx−>l a s 1 = ( v o l a t i l e UCHAR∗ ) MmMapIoSpace ( r e s o u r c e s [ i ] . u . Memory . S t a r t , 1 6 , MmNonCached) ; 25 break ; 26 } 27 } 28 } 29 30 p i r p −>I o S t a t u s . S t a t u s = STATUS SUCCESS ; 31 I oCom ple teR eque st ( p i r p , IO NO INCREMENT) ; 32 return STATUS SUCCESS ; 33 }
Els˝ o l´ep´esk´ent aktiv´ aljuk az AddDevice f¨ uggv´enyben m´ar regisztr´alt interf´esz¨ unket (4). Ahogyan ott, itt sem kell att´ol tartanunk, hogy az enged´elyez´es ´es a StartDevice f¨ uggv´eny v´ege k¨oz¨ott valaki megpr´ob´alja haszn´ alni a f´elk´esz k´ arty´ ankat, ugyanis az eszk¨oz csak a f¨ uggv´eny visszat´er´ese ut´ an sz´ am´ıt m˝ uk¨ od´esre k´esznek. Ezek ut´an a m´ar l´atott m´odon hozz´af´er¨ unk az IRP m¨ og¨ otti tartalomhoz (6), amelyb˝ol megszerezz¨ uk az eszk¨oz¨ unk er˝ oforr´ asainak list´ ait (7). Ezek ut´an a felismert er˝oforr´asokon v´egigiter´alva bemappelj¨ uk a k´ artya mem´ oriater¨ uleteit a sz´am´ıt´og´ep mem´ori´aj´aba. A mi k´ arty´ ankon h´ arom, k¨ ul¨ onb¨ oz˝ o m´eret˝ u mem´oriater¨ ulet tal´alhat´o (LAS0, LAS1 ´es LCFG), ´ıgy szerencs´ere a m´eret¨ uk ismeret´eben m´ar egy´ertelm˝ uen tudjuk azonos´ıtani ˝ oket. Az MmMapIoSpace f¨ uggv´eny felel a mappel´es´ert, amelynek egyszer˝ uen ´ at kell adni a mappelni k´ıv´ant mem´oriater¨ uletre egy mutat´ot, annak hossz´ at, illetve hogy k´ıv´anunk-e a gyors´ıt´ot´araz´assal ´elni. Mivel mi nanoszekundumos sebess´eggel k´ıv´anunk jeleket kiadni a k´arty´an, ´ıgy nem 13
´el¨ unk ezzel a lehet˝ os´eggel. A f¨ uggv´eny ´altal visszaadott void* -t, amely a unk sz´ am´ıt´ og´ep mem´ ori´ aj´ ara mutat, elmentj¨ uk a DEVICE EXTENSION -¨ megfelel˝ o r´eszeibe, a b´ ajtonk´enti el´er´es ´es a gyors´ıt´ot´araz´as elker¨ ul´ese ´erdek´eben volatile UCHAR* -re ´atalak´ıtva. V´egezet¨ ul jelezz¨ uk a siker¨ unket ´es teljes´ıtj¨ uk a k´er´est. Az el˝ obb bemutat´ asra ker¨ ult, hogy mik´ent foglaljuk le az er˝oforr´asokat a k´ artya haszn´ alat´ ahoz, azonban az is nagyon fontos, hogy ezeket a megfelel˝o id˝ oben visszaadjuk a rendszernek. Ez´ert felel az al´abbi f¨ uggv´eny: 1 NTSTATUS DM7520 PnP RemoveDevice (PDEVICE OBJECT fdo , PIRP p i r p ) 2 { 3 DEVICE EXTENSION∗ pdx = (DEVICE EXTENSION∗ ) fdo−>D e v i c e E x t e n s i o n ; 4 I o S e t D e v i c e I n t e r f a c e S t a t e (&pdx−>ifname , FALSE) ; 5 R t l F r e e U n i c o d e S t r i n g (&pdx−>i f n a m e ) ; 6 MmUnmapIoSpace ( (PVOID) pdx−>l a s 0 , 5 1 2 ) ; 7 MmUnmapIoSpace ( (PVOID) pdx−>l a s 1 , 16) ; 8 MmUnmapIoSpace ( (PVOID) pdx−>l c f g , 2 5 6 ) ; 9 10 p i r p −>I o S t a t u s . S t a t u s = STATUS SUCCESS ; 11 NTSTATUS s t a t u s = I o C a l l D r i v e r ( pdx−>l o w e r D e v i c e O b j e c t , p i r p ) ; 12 I o D e t a c h D e v i c e ( pdx−>l o w e r D e v i c e O b j e c t ) ; 13 IoDeleteDevice ( fdo ) ; 14 return s t a t u s ; 15 }
Ez a f¨ uggv´eny ker¨ ul megh´ıv´asra, amikor az eszk¨ozt elt´avol´ıtj´ak a rendszerb˝ ol, legyen az ak´ ar fizikai elt´avol´ıt´as vagy szoftveres. Els˝o l´ep´esk´ent megsz¨ untetj¨ uk a k´ artya interf´esz´enek el´erhet˝os´eg´et, felszabad´ıtjuk az interf´esz nev´enek fenntartott mem´ ori´at, valamint egyes´evel megsz¨ untetj¨ uk a mappel´eseket mindh´ arom mem´oriater¨ uletre. Ezek ut´an nem egyszer˝ uen teljes´ıtj¨ uk a k´er´est, hanem tov´abb k¨ uldj¨ uk az als´obb r´etegeknek, hiszen ha a k´ arty´ at fizikailag elt´ avol´ıtj´ ak a rendszerb˝ol, akkor arr´ol a fizikai drivernek is tudnia kell. A Forward & Forget m´odszerrel ellent´etben azonban most nem felejtj¨ uk el a k´er´est, hanem megv´arjuk m´ıg az alattunk l´ev˝o r´etegek elv´egzik teend˝ oiket, majd ezek ut´ an lev´alasztjuk a driver¨ unket a driver stackr˝ol az IoDetachDevice h´ıv´ assal, valamint megsemmis´ıtj¨ uk a funkcion´alis driver¨ unket. Ezek a f¨ uggv´enyek biztos´ıtj´ak a k´arty´ank alapvet˝o m˝ uk¨od´es´ehez az ”adminisztr´ aci´ ot”. B´ ar az eszk¨oz¨ unk m´eg egy´altal´an nem funkcion´alis, de m´ar gond n´elk¨ ul be-ki lehet kapcsolni, illetve hozz´a lehet adni ´es el lehet venni a rendszerb˝ ol. Mag´ a´ert a funkcion´alis m˝ uk¨od´es´ert a k¨ovetkez˝o f¨ uggv´eny felel:
14
1 NTSTATUS DM7520 DeviceControl (PDEVICE OBJECT fdo , PIRP p i r p ) 2 { 3 PIO STACK LOCATION s t a c k = I o G e t C u r r e n t I r p S t a c k L o c a t i o n ( p i r p ) ; 4 DEVICE EXTENSION∗ pdx = (DEVICE EXTENSION∗ ) fdo−>D e v i c e E x t e n s i o n ; 5 NTSTATUS s t a t u s = STATUS INVALID DEVICE REQUEST ; 6 p i r p −>I o S t a t u s . I n f o r m a t i o n = 0 ; 7 8 switch ( s t a c k −>P a r a m e t e r s . D e v i c e I o C o n t r o l . I o C o n t r o l C o d e ) 9 { 10 case DM7520 WRITE BUFFERED : 11 { 12 // . . . 13 break ; 14 } 15 case DM7520 READ BUFFERED : 16 { 17 // . . . 18 break ; 19 } 20 } 21 I oCom ple teR eque st ( p i r p , IO NO INCREMENT) ; 22 p i r p −>I o S t a t u s . S t a t u s = s t a t u s ; 23 return s t a t u s ; 24 }
A driver API-j´ anak bemutat´as´an´al l´atni fogjuk, hogy az eszk¨ozzel val´o kommunik´ aci´ ohoz minden esetben a Windows API ´altal biztos´ıtott DeviceIoControl f¨ uggv´enyt fogjuk haszn´alni. Ezek a f¨ uggv´enyh´ıv´asok jutnak el ´ k´er´esek form´ aj´ aban a fent l´ athat´o f¨ uggv´eny¨ unkh¨oz. Altal´ anoss´agban ez a f¨ uggv´eny felel az´ert, hogy a k´arty´ara specifikus funkcion´alis k´er´eseket fogadja ´es v´egrehajtsa. A mi eset¨ unkben csup´an k´et funkci´ot defini´altunk a k´arty´ahoz, az ´ır´ ast ´es az olvas´ ast. Kezd´esk´ent megszerezz¨ uk az IRP-hez tartoz´o inform´ aci´ okat ´es be´ all´ıtjuk a visszaadand´o ´ert´ekek alap´ertelmez´eseit. Az information jelent´ese mindig az adott k´er´est˝ol f¨ ugg, ´ıgy a legegyszer˝ ubb ezt kezdetben lenull´ azni. A status kezdeti ´ert´ek´enek a STATUS INVALID DEVICE REQUEST ´ert´eket v´alasztottuk, ezzel garant´ alva a megfelel˝ o hibajelz´est amennyiben valaki egy ´erv´enytelen k´er´est k¨ uld. Az IoControlCode tartalmazza a pontos k´er´est (mint a Plug and Play eset´eben a MinorCode), amelynek lehets´eges ´ert´ekeit mi defini´altuk a k¨ ovetkez˝ o m´ odon: 1 // IOCTL c o d e s 2 #define DM7520 WRITE BUFFERED \ 3 CTL CODE(FILE DEVICE UNKNOWN, 0 x800 , METHOD BUFFERED, FILE WRITE ACCESS) 4 #define DM7520 READ BUFFERED \ 5 CTL CODE(FILE DEVICE UNKNOWN, 0 x801 , METHOD BUFFERED, FILE READ ACCESS)
A CTL CODE egy speci´ alis makr´o, amely seg´ıts´eg´evel l´etre lehet hozni a n´egy r´eszb˝ ol ´ all´ o IOCTL (I/O Control) k´odokat. A k´od els˝o tartalmazza, hogy a k´ od milyen eszk¨ ozre vonatkozik (p´eld´aul grafikus k´artya, merevlemez). A m´er´es-adatgy˝ ujt˝ o k´ arty´ akhoz a Windows nem biztos´ıt semmilyen k¨ ul¨onleges b´ an´ asm´ odot, ez´ert az ”egy´eb” kateg´ori´at jel¨ol˝o FILE DEVICE UNKNOWN ´ert´eket v´ alasztottuk. A k´ od m´asodik r´esze egy, ´altalunk megadott, azonos´ıt´oja
15
a k´er´esnek. Harmadikk´ent kell megadni, hogy ehhez a k´er´eshez milyen t´ıpus´ u I/O-t k´ıv´ anunk haszn´ alni, ami a mi eset¨ unkben mindig pufferelt. V´eg¨ ul pedig a k´er´eshez sz¨ uks´eges jogokat kell megadni, azaz ahhoz hogy ezt a k´er´est valaki elk¨ uldhesse, milyen hozz´ af´er´esi joggal kell rendelkeznie a k´arty´ahoz. Ezek ut´ an v´egrehajtjuk az ´ır´ast / olvas´ast, majd v´eg¨ ul teljes´ıtj¨ uk az IRP-t. Amennyiben a program a switch egyik ´ag´aba sem l´ep be, u ´gy a st´atusz marad STATUS INVALID DEVICE REQUEST, ellenkez˝o esetben pedig az adott ´ag felel˝ os a st´ atusz be´ all´ıt´ as´ a´ert. Az I/O m˝ uveletekhez egy I/O m˝ uveletet le´ır´o strukt´ ur´at haszn´alunk, amelynek defin´ıci´ oja a k¨ ovetkez˝o: 1 struct DM7520 IO DESCRIPTOR 2 { 3 DM7520 : : MEMORY LOCATION memloc ; 4 DM7520 : : SIZE s i z e ; 5 UINT16 o f f s e t ; 6 UINT32 data ; 7 };
Az els˝ o tag a memloc, amely azt tartalmazza, hogy a m˝ uvelet a k´arty´anak melyik mem´ oriatartom´ any´ ara vonatkoznak. A size mez˝o mutatja meg, hogy h´ any b´ ajtnyi adatot szeretn´enk ´ırni / olvasni. Ez a k´et tag egy-egy enumer´ aci´ oval adhat´ o meg, ezek a k¨ovetkez˝ok´eppen n´eznek ki: 1 enum MEMORY LOCATION : char 2 { LAS0 = 0x0 , LAS1 = 0x1 , LCFG = 0 x2 } ; 3 enum SIZE : char 4 { BYTE = 0x1 , WORD = 0 x02 , DWORD = 0 x04 } ;
Az offset a m˝ uvelet mem´ oriarekeszen bel¨ uli pontos hely´et mondja meg, a data pedig az adat amit ´ırni k´ıv´anunk, illetve amit beolvastunk. Az ´ır´ asi ´ ag a k¨ ovetkez˝ ok´eppen n´ez ki: 1 switch ( s t a c k −>P a r a m e t e r s . D e v i c e I o C o n t r o l . I o C o n t r o l C o d e ) { 2 case DM7520 WRITE BUFFERED : 3 { 4 DM7520 IO DESCRIPTOR∗ i o D e s c = (DM7520 IO DESCRIPTOR∗ ) p i r p −> AssociatedIrp . SystemBuffer ; 5 s t a t u s = STATUS SUCCESS ; 6 p i r p −>I o S t a t u s . I n f o r m a t i o n = i o D e s c −>s i z e ; 7 v o l a t i l e UCHAR∗ a d d r e s s ; 8 switch ( i o D e s c −>memloc ) { 9 case DM7520 : : LAS0 : a d d r e s s = pdx−>l a s 0 ; break ; 10 case DM7520 : : LAS1 : a d d r e s s = pdx−>l a s 1 ; break ; 11 case DM7520 : : LCFG: a d d r e s s = pdx−>l c f g ; break ; 12 } 13 a d d r e s s += i o D e s c −>o f f s e t ; 14 switch ( i o D e s c −>s i z e ) 15 { 16 case DM7520 : : BYTE: WRITE REGISTER UCHAR ( (UCHAR∗ ) a d d r e s s , ( UINT8) i o D e s c −>data ) ; break ; 17 case DM7520 : :WORD: WRITE REGISTER USHORT( (USHORT∗ ) a d d r e s s , ( UINT16 ) i o D e s c −>data ) ; break ; 18 case DM7520 : :DWORD: WRITE REGISTER ULONG ( (ULONG∗ ) a d d r e s s , ( UINT32 ) i o D e s c −>data ) ; break ; 19 } 20 break ; 21 } 22 }
16
Pufferelt I/O eset´en a Windows szolg´altat nek¨ unk egy puffert, ahol el´erj¨ uk a felhaszn´ al´ oi m´ odb´ ol ´ atk¨ uld¨ ott adat m´asolat´at, ez tal´alhat´o a SystemBuffer altal meghat´ ´ arozott helyen, ahol nek¨ unk norm´alis esetben egy I/O m˝ uveletet le´ır´ o strukt´ ura van. ´Ir´ as eset´en az Information az eszk¨ozre ´ırt b´ajtok sz´am´at tartalmazza. Ezek ut´ an az address nev˝ u v´altoz´onkban be´all´ıtjuk a kezd˝oc´ımet az ´ atadott strukt´ ura f¨ uggv´eny´eben, majd hozz´aadjuk az offsetet. Ezek ut´an a m´eret f¨ uggv´eny´eben a Windows Driver Kit ´altal biztos´ıtott makr´okon kereszt¨ ul v´egrehajtjuk az ´ır´ ast az eszk¨ ozre. Az olvas´ as sokban hasonl´ıt az ´ır´asra, azonban van p´ar fontos k¨ ul¨onbs´eg k¨ oz¨ ott¨ uk. A megval´ os´ıt´ as a k¨ ovetkez˝o: 1 // . . . 2 case DM7520 READ BUFFERED : 3 { 4 DM7520 IO DESCRIPTOR∗ i o D e s c = (DM7520 IO DESCRIPTOR∗ ) p i r p −> AssociatedIrp . SystemBuffer ; 5 s t a t u s = STATUS SUCCESS ; 6 p i r p −>I o S t a t u s . I n f o r m a t i o n = s i z e o f (DM7520 IO DESCRIPTOR) ; 7 v o l a t i l e UCHAR∗ a d d r e s s ; 8 switch ( i o D e s c −>memloc ) 9 { 10 case DM7520 : : LAS0 : a d d r e s s = pdx−>l a s 0 ; break ; 11 case DM7520 : : LAS1 : a d d r e s s = pdx−>l a s 1 ; break ; 12 case DM7520 : : LCFG: a d d r e s s = pdx−>l c f g ; break ; 13 } 14 a d d r e s s += i o D e s c −>o f f s e t ; 15 switch ( i o D e s c −>s i z e ) 16 { 17 case DM7520 : : BYTE: i o D e s c −>data = READ REGISTER UCHAR ( (UCHAR ∗ ) a d d r e s s ) ; break ; 18 case DM7520 : :WORD: i o D e s c −>data = READ REGISTER USHORT( (USHORT ∗ ) a d d r e s s ) ; break ; 19 case DM7520 : :DWORD: i o D e s c −>data = READ REGISTER ULONG ( (ULONG ∗ ) a d d r e s s ) ; break ; 20 } 21 break ; 22 }
Az ´ır´ ashoz hasonl´ oan itt is sz¨ uks´eg¨ unk van inputra egy I/O le´ır´o strukt´ ura form´ aj´ aban, amelyet a m´ ar eml´ıtett SystemBuffer v´altoz´on kereszt¨ ul ´er¨ unk el. Az Information ´ert´ek´enek itt az olvasott b´ajtok sz´am´at ´all´ıtjuk be, azonban fontos odafigyelni, hogy a ”beolvasott b´ajtok sz´ama” kifejez´est felhaszn´al´oi m´ odb´ ol ´ertelmezz¨ uk! A driver szempontj´ab´ol csak a size-nak megfelel˝o mennyis´eg˝ u b´ ajtot olvasunk be, azonban a felhaszn´al´onak egy eg´esz DM7520 IO DESCRIPTOR strukt´ ur´at visszajuttatunk, ez´ert annak az ´ert´ek´et adjuk vissza inform´ aci´ o gyan´ ant. Ezek ut´an pontosan ugyanaz t¨ort´enik mint az ´ır´ asn´ al, azzal a k¨ ul¨onbs´eggel term´eszetesen, hogy most a READ REGISTER kezdet˝ u makr´okat haszn´aljuk, majd az ´ert´eket elt´aroljuk a strukt´ ur´ ankban. Az I/O m˝ uveletekn´el nem kell amiatt agg´odnunk, hogy van-e hozz´ af´er´es¨ unk a megadott pufferekhez, vagy hogy azok m´odosulhatnak-e id˝ ok¨ ozben. Mivel pufferelt I/O-r´ol besz´el¨ unk, ez´ert a Windows a DeviceIoControl h´ıv´ asokn´ al az ´atadott param´etereket lem´asolja nek¨ unk ´es egy kernel-m´ od´ u mem´ oriater¨ uleten l´ev˝o pufferbe teszi ˝oket, amit mi szabadon m´ odos´ıthatunk. Olvas´ as eset´eben a SystemBuffer els˝o Information b´ajtja automatikusan visszam´ asol´ odik a Windows ´altal a felhaszn´al´oi m´od´ u pufferbe, 17
´ıgy nek¨ unk ezzel nem kell foglalkoznunk, att´ol eltekintve, hogy r¨ogz´ıtve van, hogy mely mem´ oriac´ımre kell ´ırnunk a visszaadand´o ´ert´ekeket. ´ Erdemes m´eg a hibamegel˝oz´es c´elj´ab´ol megeml´ıteni, hogy a WDK ford´ıt´ oprogramjai automatikusan v´egrehajtanak egy statikus k´odanal´ızist a driver¨ unk¨ on, amelynek munk´aj´at nagyban megk¨onny´ıti, ha a k¨ ul¨onf´ele f¨ uggv´enyeinkr˝ ol el˝ ore megmondjuk, hogy milyen c´elt k´ıv´annak bet¨olteni. Ezt a k¨ ovetkez˝ ok´eppen lehet megtenni: 1 2 3 4 5 6 7
extern ”C” DRIVER INITIALIZE D r i v e r E n t r y ; DRIVER UNLOAD DM7520 DriverUnload ; DRIVER ADD DEVICE DM7520 AddDevice ; DRIVER DISPATCH DM7520 Close ; DRIVER DISPATCH DM7520 Create ; DRIVER DISPATCH DM7520 DeviceControl ; DRIVER DISPATCH DM7520 PnP ;
Az´ altal, hogy a k´ odanaliz´ ator l´atja, hogy a DM7520 AddDevice f¨ uggv´eny elvileg az eszk¨ oz hozz´ aad´ as´ at k´ene hogy kezelje, meg tudja vizsg´alni, hogy megfelel-e p´ ar minimumk¨ ovetelm´enynek, amelyeket az erre a c´elra ´ırt f¨ uggv´enyeknek teljes´ıteni¨ uk kell. Ezzel l´enyeg´eben el is k´esz´ıtett¨ unk az illeszt˝oprogramunk forr´ask´odj´at, amit a WDK ´ altal biztos´ıtott speci´alis ford´ıt´okkal le tudunk ford´ıtani egy .sys f´ ajll´ a. Azonban egy .sys f´ ajllal ¨onmag´aban a rendszer m´eg nem tud mit kezdeni, sz¨ uks´eg¨ unk van egy m´asodik f´ajlra, amely le´ırja a driver¨ unket. Ezt egy .inf f´ ajlban tudjuk megtenni. A k¨ovetkez˝oekben ennek a f´ajlnak a r¨ovid bemutat´ asa k¨ ovetkezik. 2.2.2
A driver-le´ır´ o .inf f´ ajlok
Egy driver telep´ıt´ese a Windows rendszerekben mindig egy .inf f´ajlon kereszt¨ ul indul. Ezek a f´ ajlok hat´ arozz´ ak meg, hogy melyik eszk¨ozh¨oz pontosan milyen szolg´ altat´ asok ´es driverek tartoznak, milyen l´ep´eseket kell az oper´aci´os rendszernek v´egrehajtania az eszk¨oz¨ok telep´ıt´es´ehez. Tekints¨ uk ´at a mi konkr´et .inf f´ ajlunkat, hogy pontosan milyen inform´aci´okat is kell megadni: 1 2 3 4 5 6
[ Version ] Signature Class Cl a ss G ui d Provider DriverVer
= = = = =
”$Windows NT$” RTDDataModule {D695ED6A−630D−4D83−8D8B−F1F0AC107AD0} %DM75 20 In fProv ider% 04/04/2012 ,1.1.2.2
A legt¨ obb .inf f´ ajl a k¨ otelez˝o Version r´eszleggel indul, ebben vannak a legalapvet˝ obb inform´ aci´ ok, amelyek m´eg nem is a konkr´et driverr˝ol vagy eszk¨ ozr˝ ol, hanem egy term´ekcsal´adr´ol sz´ol, ugyanis egy .inf f´ajl t¨obb eszk¨ozt ´es t¨ obb drivert is le´ırhat. A Signature azonos´ıtja a rendszer t´ıpus´at, amelyre mi telep´ıteni k´ıv´ anjuk az eszk¨ oz¨ unket. Ennek k´et ´ert´eke lehet: $Windows NT$ ´es $Chicago$. Mindkett˝ o pontosan ugyanazt jelenti, m´eghozz´a hogy Windows alap´ u rendszerr˝ ol besz´el¨ unk. Jelenleg teh´at l´enyeg´eben jelent´ekeny ez az ´ert´ek, azonban kihagyni nem lehet, k¨ ul¨onben ´erv´enytelen lesz a f´ajlunk. A Class bejegyz´es azonos´ıtja a mi term´ekoszt´alyunkat (tov´abbi p´eld´ak: videok´ arty´ ak, monitorok, perif´eri´ak). Mivel a m´er´es-adatgy˝ ujt˝o k´arty´ak nem 18
annyira elterjedtek, hogy saj´ at kateg´ori´ajuk legyen alapb´ol a Windows-ban, ´ıgy l´etrehoztunk mi egyet. Az oszt´alyokhoz minden esetben tartozik egy GUID, amelyet a ClassGuid bejegyz´esben kell megadni; ezt az ´ert´eket szint´en mi gener´ altuk. A Provider -n´el az eszk¨oz / illeszt˝oprogram k´esz´ıt˝oj´et szok´as megadni. Azonban .inf f´ ajlokban nem szok´as k¨ozvetlen¨ ul a sz¨ovegeket megadni, helyette csak egy azonos´ıt´ot, a sz¨ovegeket pedig egy k¨ ul¨on erre a c´elra kialak´ıtott r´eszlegben gy˝ ujteni. V´egezet¨ ul a driver verzi´oj´at, illetve d´ atum´ at sz¨ uks´eges megadni, ez alapj´an tudja a Windows, hogy melyik driver u ´jabb a m´ asikn´ al. A k¨ ovetkez˝ o r´eszlegek a m´asoland´o f´ajlok forr´asait ´es c´eljait hivatottak be´ all´ıtani: 1 2 3 4 5 6 7 8 9 10 11
[ SourceDisksNames ] 1 = %DM7520 DiskName% [ S o u r c e D i s k s F i l e s . amd64 ] DM7520 . s y s = 1 , \ x64 [ S o u r c e D i s k s F i l e s . x86 ] DM7520 . s y s = 1 , \ x86 [ DestinationDirs ] D e f a u l t D e s t D i r = 12 ; Windows\ System32 \ D r i v e r s
A SourceDisksNames r´eszlegben kell megadni a telep´ıt˝olemezek elnevez´eseit, a ”K´erem helyezze be az XYZ telep´ıt˝olemezt...” t´ıpus´ u u ¨zenetekhez. A mi eset¨ unkben csak egyetlen egyet adtunk meg, aminek a nev´et a string poolban adtuk meg. Ezek ut´an j¨on a SourceDisksFiles r´eszleg, amelyeket a .amd64 ´es .x86 kieg´esz´ıt´esekkel k¨ ul¨on lehet v´alasztani 32 ´es 64-bites esetekre. Mindk´et esetben a DM7520.sys f´ajl az egyetlen m´asoland´o, ez maga a driver szolg´ altat´ asunk. Mindk´et esetben az els˝o telep´ıt˝olemezen van a f´ ajl, azon bel¨ ul pedig az x64 vagy x86 mapp´aban, az ´eppen fut´o rendszer param´etereit˝ ol f¨ ugg˝ oen. Ezut´ an j¨on, hogy hov´a is kell a f´ajlokat m´asolni, ezt lehet a DestinationDirs r´eszlegben megadni. A mi eset¨ unkben elegend˝o egy alap´ertelmezett mappa, amelyre a Windows sz´amos, sz´amokkal jel¨olt lehet˝ os´eget biztos´ıt sz´ amunkra. Mi a 12 jel˝ u, Windows\System32\Drivers mapp´ ara mutat´ ot v´ alasztottuk, ahol a legt¨obb driver elhelyezkedik. Ezt k¨ oveti a registry-vel kapcsolatos k´et bejegyz´es: 1 2 3 4 5
[ ClassInstall32 ] AddReg = DM7520 Class AddReg [ DM7520 Class AddReg ] HKR, , , , % DM7520 ClassName%
A ClassInstall32 r´eszleg mondja meg, hogy az adott eszk¨ozoszt´aly telep´ıt´esekor (teh´ at amikor a rendszer el˝osz¨or tal´alkozik egy olyan t´ıpus´ u eszk¨ ozzel) miket kell csin´ alnia. Ebben az egyetlen k¨otelez˝oen kit¨oltend˝o bejegyz´es az AddReg, aminek egy m´asik r´eszlegre kell mutatnia, amely le´ırja, hogy hol ´es milyen bejegyz´eseket kell l´etrehozni a registry-ben. A mi eset¨ unkben egyetlen egy bejegyz´est k´ıv´anunk l´etrehozni azzal az egyetlen unk semmit nem kell a sorral a DM7520 Class AddReg r´eszlegben. Mivel nek¨ registry-ben t´ arolni, a lehet˝ o legt¨obb be´all´ıt´ast kihagytuk, ´ıgy egy egyszer˝ u 19
bejegyz´est hoztunk l´etre DM7520 ClassName n´even, amit a sz¨ovegeket t´arol´o r´eszben oldunk fel. Ezut´ an m´ ar a konkr´et eszk¨oz¨oket azonos´ıt´o r´eszek k¨ovetkeznek (a mi eset¨ unkben csak egy eszk¨ oz van): 1 2 3 4 5 6 7 8
[ Manufacturer ] %DM7520 Manufacturer% = DM7520 , NTamd64 , NTx86 [ DM7520 . ntamd64 ] %D M 7 5 2 0 D e v i c e D e s c r i p t i o n% = D M 7 5 2 0 I n s t a l l , PCI\VEN 1435&DEV 7520 [ DM7520 . ntx86 ] %D M 7 5 2 0 D e v i c e D e s c r i p t i o n% = D M 7 5 2 0 I n s t a l l , PCI\VEN 1435&DEV 7520
A Manufacturer r´eszlegben lehet a gy´art´okat, az eszk¨oz¨oket ´es a platformokat ¨ osszekapcsolni. A bejegyz´esek kulcsa mindig egy-egy gy´art´onak a neve (ami a string poolban mutat), ´ert´eke pedig egy adott eszk¨ozcsal´adot le´ır´o r´eszleg, valamint ut´ ana vessz˝ovel elv´alasztva a k¨ ul¨onb¨oz˝o t´amogatott platformok. Mi egy term´ekcsal´adot ´es k´et platformot adtunk meg, amely k´et tov´ abbi (´ am azonos) r´eszleget eredm´enyez. A r´eszleg neve a megadott term´ekcsal´ ad azonos´ıt´ oja ´es a platform azonos´ıt´oja egy ponttal elv´alasztva. Ezekben a r´eszlegekben lehet a term´ekcsal´adon bel¨ ul a term´ekeket megadni. Itt a kulcsok mindig az adott eszk¨oznek a neve (string poolban), az ´ert´ekek pedig az eszk¨ oznek a telep´ıt´es´er˝ol sz´ol´o r´eszleg neve, valamint az eszk¨oznek az egyedi azonos´ıt´ oja, ami tartalmazza a gy´art´o (vendor) azonos´ıt´oj´at, az eszk¨ oz´et, valamint az illeszt´es t´ıpus´at is (PCI). A telep´ıt´esr˝ ol sz´ ol´ o r´eszleg a k¨ovetkez˝ok´eppen n´ez ki: 1 2 3 4 5
[ DM7520 Install ] C o p y F i l e s = @DM7520 . s y s [ DM7520 Install . S e r v i c e s ] A d d S e r v i c e = DM7520 srvc , 2 , D M 7 5 2 0 S e r v i c e I n s t a l l
Az els˝ o r´eszlegben meg kell adni a f´ajlok neveit, amelyeket ´at k´ıv´anunk m´ asolni. Ezek azonban nem magukra a f´ajlokra hivatkoznak, hanem a SourceDisksFiles r´eszlegben megadott bejegyz´esekre! A DM7520 Install.Services r´eszleg a telep´ıt´esnek a szolg´altat´asokr´ol sz´ol´o r´esze. M´ıg az el˝ oz˝ o r´eszben csak f´ajlokat m´asoltunk, most u ´j szolg´altat´asok l´etrehoz´ as´ ara van lehet˝ os´eg. Az AddService direkt´ıva els˝o param´etere a l´etrehozni k´ıv´ ant szolg´ altat´ as neve. Ezt k¨oveti a flag param´eter, amelynek a 2 ´ert´eke azt jelenti, hogy a megadott szolg´altat´as egy Plug and Play function driver. Az utols´ o param´eter pedig egy hivatkoz´as amely a szolg´altat´as telep´ıt´es´enek konkr´et param´etereit ´ırja le. Ez a k¨ovetkez˝o: 1 2 3 4 5 6
[ DM7520 ServiceInstall ] DisplayName = %DM7520 ServiceName% ServiceType = 1 ; SERVICE KERNEL DRIVER StartType = 3 ; SERVICE DEMAND START E r r o r C o n t r o l = 1 ; SERVICE ERROR NORMAL S e r v i c e B i n a r y = %12%\DM7520 . s y s
Itt m´ ar a konkr´et szolg´ altat´asunkat ´ırjuk le, megadva el˝osz¨or a szolg´altat´as felhaszn´ al´ ok sz´ am´ ara megjelen˝o nev´et (ig´eny szerint egy r¨ovid le´ır´as is 20
adhat´ o). Ezek ut´ an meg kell adni a szolg´altat´as t´ıpus´at, ami a mi eset¨ unkben egy kernel-m´ od´ u driver. Sz¨ uks´eg van tov´abb´a arra is, hogy ez az adott szolg´ altat´ as mikor induljon el. Itt olyan lehet˝os´egek lenn´enek m´eg p´eld´aul, mint a ”minden indul´ askor”, a ”minden indul´askor k´esleltetve”, vagy a ”manu´ alis ind´ıt´ as sz¨ uks´eges”. Mi azt v´alasztottuk, hogy induljon el automatikusan, de csak akkor ha sz¨ uks´eg van r´a. Be´all´ıtand´o tov´abb´a, hogy a szolg´ altat´ as hogyan kezelje a k¨ ul¨onb¨oz˝o fell´ep˝o hib´akat. Egy merevlemez-illeszt˝ o eset´eben ´erthet˝o lehet a hib´aknak a szigor´ ubb kezel´ese, azonban mi megel´egedt¨ unk az alapbe´all´ıt´assal. V´egezet¨ ul meg kell adni, hogy a rendszer hol tal´ alja mag´ at a szoftver¨ unket. Itt szint´en el˝oj¨on a m´ar l´atott Windows\System32\Drivers mapp´at jel¨ol˝o 12-es sz´am. Az utols´ o r´eszleg pedig a string pool, ami a k¨ovetkez˝ok´eppen n´ez ki: 1 2 3 4 5 6 7
[ Strings ] DM7520 ClassName DM7520 DeviceDescription DM7520 DiskName D M7520 In fProv ider DM7520 Manufacturer DM7520 ServiceName
= = = = = =
”RTD DataModule ” ”DM7520 D e v i c e ” ”DM7520 d r i v e r i n s t a l l a t i o n d i s c ” ”RTD Embedded T e c h n o l o g i e s , I n c . ” ”RTD Embedded T e c h n o l o g i e s , I n c . ” ”DM7520 d e v i c e d r i v e r ”
Itt eg´eszen egyszer˝ uen kulcsk´ent azonos´ıt´okat adunk meg, amelyekre a f´ajl t¨ obbi r´esz´en sz´ azal´ekjelek k¨oz¨ott tudunk hivatkozni, ´ert´ekk´ent pedig az azonos´ıt´ o hely´ere becser´elni k´ıv´an sz¨oveget lehet megadni. Ezzel a megold´assal az ¨ osszes sz¨ oveg¨ unket egy helyre tudjuk gy˝ ujteni, ami sok szempontb´ol is hasznos (pl. lokaliz´ aci´ o). Az .inf ´es a szoftver¨ unket tartalmaz´o .sys f´ajlok birtok´aban m´ar behelyezhetj¨ uk az eszk¨ ozt a rendszerbe, amelyet a Windows nem fog felismerni, ´ıgy az eszk¨ ozkezel˝ oben manu´alis kell a driver hely´et megadni. Ezek ut´ an az eszk¨ oz automatikusan feltelep¨ ul ´es haszn´alhat´o. 2.2.3
Az eszk¨ ozilleszt˝ ok digit´ alis al´ a´ır´ asa
A 64-bites Windows 7 rendszerben alapb´ol csak ´es kiz´ar´olag digit´alisan al´a´ırt eszk¨ ozilleszt˝ oket lehet telep´ıteni a drivernek ´alc´azott v´ırusok elleni v´edelem miatt. Term´eszetesen a mi apr´o teszt driver¨ unkh¨oz nem szerezt¨ unk be semmilyen tan´ us´ıtv´ anyt, azonban szerencs´ere a Windows ezen v´edelmi mechanizmus´ at sokf´ele m´ odon meg lehet ker¨ ulni. Az egyik legegyszer˝ ubb, ha az ember bel´ep a boot men¨ ube (F8-at lenyomva az oper´aci´os rendszer bet¨ olt˝ od´es´enek kezdetekor), majd ott a ”Disable driver signature enforcement” opci´ ot v´ alasztja. Ennek hat´ as´ara a Windows u ´gy indul el, hogy al´a´ıratlan eszk¨ ozkezel˝ oket is lehet telep´ıteni. Ez a hat´as a rendszer u ´jraind´ıt´as´aig ´erv´enyes, ut´ ana deaktiv´ al´ odik. Ebben az esetben a driver¨ unk ´es az eszk¨oz¨ unk telep´ıtve marad, azonban nem lehet elind´ıtani egy hiba¨ uzenet miatt (A driver nincs al´ a´ırva).
2.3
A k´ artya API-j´ anak bemutat´ asa
Az eddigiek sor´ an megtudtuk hogyan tudjuk a saj´at driver¨ unket elk´esz´ıteni. Azonban ¨ onmag´ aban egy driver nehezen haszn´alhat´o. Egy fels˝o szint˝ u driver
21
(egy funkcion´ alis driver) rendszerint biztos´ıt valamilyen API-t, annak ´erdek´eben, hogy a k´ artya m˝ uk¨od´es´et bels˝oleg nem ismer˝o emberek is tudjanak r´ a programokat fejleszteni. Egy driver API elk´esz´ıt´ese l´enyeg´eben semmiben sem t´er el egy b´ armilyen k¨ onyvt´ar meg´ır´as´at´ol. Lehet˝os´eg¨ unk van ak´ar mag´at a forr´ ask´ odot is ´ atadni (p´eld´aul .h ´es .cpp f´ajlokk´ent), ak´ar egy statikus k¨ onyvt´ arat (.h ´es .lib f´ ajlokkal), vagy ak´ar egy DLL-t is a c´elokt´ol f¨ ugg˝oen. A mi eset¨ unkben t¨ orekedt¨ unk az egyszer˝ us´egre, ´ıgy a felhaszn´al´oi alkalmaz´asunk keretein bel¨ ul fejlesztett¨ uk ki az API-t is. Ez ellent mond bizonyos programoz´ asi elveknek, azonban egy ekkora m´eret˝ u projektn´el k´ets´eg k´ıv¨ ul sokkal egyszer˝ ubb. El˝ osz¨ or is meg kell fontoljuk, hogy mekkora hatalmat k´ıv´anunk a felhaszn´al´o (teh´ at a ”k¨ uls˝ o” programoz´ o) kez´ebe adni. Itt is fel lehet ´all´ıtani a k´et extr´em esetet: 1 // Case 1 : Extreme t r u s t 2 void Write ( DM7520 : : MEMORY LOCATION memloc , unsigned o f f s e t , void ∗ data , unsigned dataLength ) ; 3 void Read ( DM7520 : : MEMORY LOCATION memloc , unsigned o f f s e t , void ∗ b u f f e r , unsigned l e n g t h ) ; 4 5 // Case 2 : Extreme d i s t r u s t − r e t u r n v a l u e s a l w a y s i n d i c a t e s u c c e s s 6 bool ResetCard ( ) ; 7 bool S t a r t T i m e r ( ) ; 8 bool ReadRegisterA ( ) ; 9 bool W r i t e R e g i s t e r B ( char data ) ; 10 bool SetSomeBoolValue ( bool v a l u e ) ;
Az els˝ o esetben teljesen megb´ızunk a programoz´oban, minimalisztikus API-t alak´ıtunk ki, ennek ellen´ere azonban mindent meg lehet vele csin´alni. Term´eszetesen r´ ab´ızni a programoz´ora, hogy pontosan tudja, hogy melyik mem´ oriac´ımre ´ırhat ´es milyen ´ert´ekeket lehet oda´ırni, ez h´ usz ´evvel ezel˝ott volt elfogadhat´ o. A m´ asik v´eglet pedig amikor minden egyes elv´egezhet˝o m˝ uveletre k¨ ul¨ on f¨ uggv´enyt biztos´ıtunk. A mai szoftverfejleszt´es ebbe az ir´anyba halad, azonban a mi munk´ ankhoz ink´abb az els˝o megold´asra hasonl´ıt´o API-t hoztunk l´etre: 1 DEFINE GUID( DM7520 GUID , 2 0 x f 1 4 a f 8 5 1 , 0 x9aeb , 0 x 4 1 f 3 , 0xb3 , 0 xa9 , 0x7d , 0 x51 , 0 x19 , 0 x56 , 0 xce , 0 x33 ) ; 3 4 // IOCTL c o d e s 5 #define DM7520 WRITE BUFFERED \ 6 CTL CODE(FILE DEVICE UNKNOWN, 0 x800 , METHOD BUFFERED, FILE WRITE ACCESS) 7 #define DM7520 READ BUFFERED \ 8 CTL CODE(FILE DEVICE UNKNOWN, 0 x801 , METHOD BUFFERED, FILE READ ACCESS) 9 10 c l a s s DM7520 11 { 12 public : 13 enum MEMORY LOCATION : char 14 { LAS0 = 0x0 , LAS1 = 0x1 , LCFG = 0 x2 } ; 15 enum SIZE : char 16 { BYTE = 0x1 , WORD = 0 x02 , DWORD = 0 x04 } ; 17 ˜DM7520 ( ) ; 18 s t a t i c void C l o s e D e v i c e ( DM7520∗ d e v i c e ) ;
22
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
s t a t i c DM7520∗ OpenDevice ( ) ; UINT32 Read (MEMORY LOCATION memloc , UINT16 o f f s e t , SIZE s i z e ) ; void Write (MEMORY LOCATION memloc , UINT16 o f f s e t , SIZE s i z e , UINT32 data ) ; private : DM7520 ( ) ; DM7520 ( const DM7520∗ ) ; DM7520 (HANDLE h a n d l e ) ; HANDLE h a n d l e ; }; struct DM7520 IO DESCRIPTOR { DM7520 : : MEMORY LOCATION memloc ; DM7520 : : SIZE s i z e ; UINT16 o f f s e t ; UINT32 data ; };
A GUID-ot, az IOCTL k´ odokat, valamint a DM7520 IO DESCRIPTOR strukt´ ur´ at csak a teljess´eg kedv´e´ert szerepeltetj¨ uk, ezeket kor´abban m´ar bemutattuk. A k´ artya alapvet˝o manipul´aci´oj´ahoz a DM7520 nev˝ u oszt´alyt hoztuk l´etre, amelynek publikus f¨ uggv´enyeivel meg lehet nyitni ´es be lehet z´ arni a k´ arty´ at, valamint az ´ır´as ´es olvas´as funkci´okat is ezen az oszt´alyon kereszt¨ ul lehet elv´egezni. Mivel szerett¨ unk volna egy mindig konzisztens allapotb´ ´ ol indul´ o oszt´ alyt megval´os´ıtani, a k¨ ul¨onb¨oz˝o konstruktorokat priv´att´a tett¨ uk, ´ıgy az oszt´ alyt csak az OpenDevice f¨ uggv´enyen kereszt¨ ul lehet p´eld´ anyos´ıtani. Erre az´ert volt sz¨ uks´eg, mert a konstruktorb´ol csak nagyon korl´ atozottan tudtuk volna a hibakezel´est megoldani. A k´ arty´ ankhoz minden esetben egy HANDLE t´ıpus´ u v´altoz´on kereszt¨ ul fogunk tudni hozz´ af´erni, amelyet az OpenDevice f¨ uggv´enyen kereszt¨ ul tudunk biztos´ıtani az oszt´ alyunknak: 1 DM7520∗ DM7520 : : OpenDevice ( ) 2 { 3 HDEVINFO h d i = SetupDiGetClassDevs (&DM7520 GUID , 0 , NULL, DIGCF PRESENT | DIGCF DEVICEINTERFACE) ; 4 i f ( h d i == INVALID HANDLE VALUE) 5 { 6 return n u l l p t r ; 7 } 8 SP DEVICE INTERFACE DATA d i d ; 9 d i d . c b S i z e = s i z e o f (SP DEVICE INTERFACE DATA) ; 10 i f ( ! S e t u p D i E n u m D e v i c e I n t e r f a c e s ( hdi , 0 , &DM7520 GUID , 0 , &d i d ) ) 11 { 12 SetupDiDestroyDeviceInfoList ( hdi ) ; 13 return n u l l p t r ; 14 } 15 ULONG b u f f e r S i z e ; 16 S e t u p D i G e t D e v i c e I n t e r f a c e D e t a i l ( hdi , &did , NULL, 0 , &b u f f e r S i z e , 0) ; 17 PSP DEVICE INTERFACE DETAIL DATA i f D e t a i l = ( PSP DEVICE INTERFACE DETAIL DATA) new char [ b u f f e r S i z e ] ; 18 i f D e t a i l −>c b S i z e = s i z e o f (SP DEVICE INTERFACE DETAIL DATA) ; 19 i f ( ! S e t u p D i G e t D e v i c e I n t e r f a c e D e t a i l ( hdi , &did , i f D e t a i l , b u f f e r S i z e , &b u f f e r S i z e , 0 ) ) 20 { 21 SetupDiDestroyDeviceInfoList ( hdi ) ; 22 delete [ ] i f D e t a i l ;
23
23 24 25 26
27 28 29 30 31 32 33 }
return n u l l p t r ; } SetupDiDestroyDeviceInfoList ( hdi ) ; HANDLE h a n d l e = C r e a t e F i l e ( i f D e t a i l −>DevicePath , GENERIC READ | GENERIC WRITE, 0 , 0 , OPEN EXISTING , FILE ATTRIBUTE NORMAL, 0 ) ; delete [ ] i f D e t a i l ; i f ( h a n d l e == INVALID HANDLE VALUE) { return n u l l p t r ; } return new DM7520 ( h a n d l e ) ;
Ez a f¨ uggv´eny felel a k´ artya el´er´es´e´ert, ´es siker eset´en egy, a k´artya manipul´ aci´ oj´ ara val´ o oszt´ alyra ad egy mutat´ot, ellenkez˝o esetben nulla a visszat´er´esi ´ert´ek. Ez a f¨ uggv´eny igen nagy m´ert´ekben ´ep´ıt a Windows API egyik ”al-API-j´ ara”, az u ´gynevezett SetupAPI-ra, amellyel a hardverekr˝ol lehet inform´ aci´ ot k´erni, illetve kezelni ˝oket. A SetupDiGetClassDevs f¨ uggv´ennyel egy adott eszk¨ ozoszt´alyba tartoz´o eszk¨oz¨oket lehet lek´erni. Az els˝ o param´eter hat´ arozza meg az oszt´alyt, ahova mi a k´arty´anknak megfelel˝o ´ RTDDataModule eszk¨ ozoszt´ aly GUID-j´at adtuk meg. Erdekes tov´abb´a m´eg a negyedik param´eter, amivel azt lehet szab´alyozni, hogy mely eszk¨oz¨oket kapjuk meg. A mi flag-kombin´aci´onk azt jelenti, hogy csak az ´eppen aktu´ alisan jelenl´ev˝ o ´es haszn´alhat´o interf´esszel rendelkez˝o hardvereket szeretn´enk l´ atni. Ez a f¨ uggv´eny egy HDEVINFO t´ıpust szolg´altat, amely csak egy nyers er˝ oforr´ as, ¨ onmag´ aban nem lehet bel˝ole inform´aci´okat kinyerni. Ebben ny´ ujt seg´ıts´eget a SetupDiEnumDeviceInterfaces met´odus, amelynek seg´ıts´eg´evel v´egig iter´ alhatunk az eszk¨ozeinken. Mivel a mi tesztprogramunkat csak bels˝ o tesztel´esre haszn´ aljuk, tudjuk, hogy mindig maximum egyetlen egy ilyen k´ artya lesz a rendszerben, ´ıgy k¨ ul¨on¨osebb vizsg´al´od´as n´elk¨ ul elfogadjuk az els˝ o tal´ alatot, amit elment¨ unk egy SP DEVICE INTERFACE DATA strukt´ ur´ aba. B´ ar a program tesztk¨ornyezetben val´o fut´asra k´esz¨ ult, az´ert az alapvet˝ o hibakezel´est nem hagytuk ki, ´ıgy minden egyes hiba eset´en felszabad´ıtjuk az eszk¨ ozinform´aci´os list´ankat a speci´alisan erre a c´elra val´o SetupDiDestroyDeviceInfoList h´ıv´assal. Miut´ an kez¨ unkben van a felt´etelezett eszk¨oz, megpr´ob´alunk hozz´af´erni az interf´esz´ehez, ehhez a SetupDiGetDeviceInterfaceDetail nev˝ u f¨ uggv´eny lesz a seg´ıts´eg¨ unkre, ennek haszn´ alata azonban n´emik´epp meg lett bonyol´ıtva. Param´eterk´ent ´ at kell neki adni, hogy mely eszk¨ozr˝ol k´ıv´anunk inform´aci´ot megtudni, valamint egy puffert illetve annak m´eret´et, ahov´a az adatokat m´ asolni tudja. Azonban az interf´eszt le´ır´o strukt´ ura minden eszk¨ozn´el m´as m´erettel rendelkezik, ez´ert el˝ obb a f¨ uggv´enyt puffer ´atad´asa n´elk¨ ul (17) kell megh´ıvni, ami a bufferSize nev˝ u v´altoz´onkba bele´ırja a sz¨ uks´eges m´eretet. Ezek ut´ an m´ ar l´etre lehet hozni a PSP DEVICE INTERFACE DETAIL DATA strukt´ ur´ankat, amelyhez - els˝ore tal´ an meglep˝ o m´ odon - egy bufferSize m´eret˝ u karaktert¨omb¨ot rendel¨ unk hozz´ a. Ez az´ert van ´ıgy, mert ez a strukt´ ura l´enyeg´eben csak sz¨ovegeket tartalmaz, ´es az API k´esz´ıt˝ oi ezzel a l´ep´essel k´ıv´antak az ¨osszes sz¨ovegnek elegend˝ o mem´ oriater¨ uletet foglaltatni. Ezt a k´es˝obbiekben term´eszetesen minden esetben felszabad´ıtjuk. A megfelel˝o m´eret birtok´aban m´ar megh´ıvhatjuk m´ asodj´ ara is a f¨ uggv´eny¨ unket, amely ez´ uttal m´ar a sz´amunkra
24
sz¨ uks´eges inform´ aci´ okat adja meg. Ebben a pillanatban minden a kez¨ unkben van ahhoz, hogy hozz´af´erj¨ unk a k´ arty´ ahoz, ´ıgy az inform´ aci´ os list´ankat fel is szabad´ıtjuk, majd a CreateFile h´ıv´ assal l´etrehozzuk a HANDLE -t az eszk¨oz¨ unkh¨oz. Els˝o param´eterk´ent a megnyitand´ o ”f´ ajl” el´er´esi u ´tj´at kell megadni, amelyet az im´ent megszerzett interf´esz-le´ır´ o strukt´ ur´ ank tartalmaz. Fontos m´eg, hogy milyen jogosults´ agokkal k´ıv´ anjuk az eszk¨ozt megnyitni. Egy kereskedelmi-c´el´ u driverben c´elszer˝ u lehet k¨ ul¨ on csak olvashat´o hozz´af´er´est biztos´ıt´o megnyit´asi m´ odokat is t´ amogatni, azonban a mi eset¨ unkben ez nem volt fontos. Az OPEN EXISTING flag a megnyit´as m´odj´at szab´alyozza ´es azt jelenti, hogy csak akkor nyissa meg a f´ ajlt, hogyha az m´ar l´etezik (alapb´ol egy nem l´etez˝o f´ ajl megnyit´ asakor l´etrej¨ on egy u ´j f´ajl). Az ¨osszes t¨obbi param´eter figyelmen k´ıv¨ ul hagyhat´ o. V´egezet¨ ul egy dinamikus mem´oriafoglal´ast k¨ovet˝oen t´er¨ unk vissza, amely term´eszetesen potenci´ alis mem´oriasziv´arg´asokhoz vezethet, azonban ha valaki betartja azt a szab´ alyt, hogy egy megnyitott f´ajlt be is kell z´arni, akkor nem lesz gond. Mint az l´ athat´ o, a k´arty´at manipul´al´o oszt´alyt egy param´eterezett konstruktoron kereszt¨ ul hozzuk l´etre, amely semmi m´ast nem csin´al csak be´ all´ıtja a HANDLE tagv´ altoz´o ´ert´ek´et az ´atadott param´eter ´ert´ek´ere: 1 DM7520 : : DM7520 (HANDLE h a n d l e ) 2 : handle ( handle ) { }
Miut´ an az eszk¨ oz¨ unkkel val´o m˝ uveleteket elv´egezt¨ uk, a CloseDevice nev˝ u statikus f¨ uggv´ennyel lehet azt biztons´agosan lez´arni. A f¨ uggv´eny csak megsemmis´ıti az oszt´ alyt, ami cser´ebe megh´ıvja az oszt´aly destruktor´at, ami bez´ arja a HANDLE -j´et: 1 2 3 4 5 6 7 8 9 10 11 12
void DM7520 : : C l o s e D e v i c e ( DM7520∗ d e v i c e ) { delete d e v i c e ; } DM7520 : : ˜ DM7520 ( ) { i f ( handle ) { CloseHandle ( handle ) ; } }
Az ´ır´ as´ert felel˝ os met´ odus a k¨ovetkez˝o: 1 void DM7520 : : Write ( DM7520 : : MEMORY LOCATION memloc , UINT16 o f f s e t , DM7520 : : SIZE s i z e , UINT32 data ) 2 { 3 ULONG b y t e s W r i t t e n ; 4 DM7520 IO DESCRIPTOR i o D e s c ; 5 i o D e s c . memloc = memloc ; 6 ioDesc . o f f s e t = o f f s e t ; 7 ioDesc . s i z e = s i z e ; 8 i o D e s c . data = data ; 9 D e v i c e I o C o n t r o l ( handle , DM7520 WRITE BUFFERED, (LPVOID) &i o D e s c , s i z e o f ( i o D e s c ) , NULL, 0 , &b y t e s W r i t t e n , NULL) ; 10 }
Mint az l´ athat´ o, semminem˝ u ellen˝orz´est nem v´egz¨ unk el a k´er´es tov´ abb´ıt´ asa el˝ ott. Ez a driver¨ unk sz˝ uk felhaszn´al´asa miatt van, egy ´altal´anos 25
c´el´ u illeszt˝ oprogram eset´eben c´elszer˝ u lenne m´eg a kernel-m´odba val´o atkapcsol´ ´ as el˝ ott megbizonyosodni r´ola, hogy ´erv´enyes-e egy´altal´an a k´er´es. Itt m´ ar megjelenik a m´ ar eml´ıtett DeviceIoControl nev˝ u f¨ uggv´eny. Ennek haszn´ alat´ aval lehet az eszk¨ozeinknek k¨ ul¨onf´ele k´er´eseket k¨ uldeni. A param´eterek k¨ oz¨ ott megjelenik maga az eszk¨oz¨ unk (handle), a konkr´et utas´ıt´as unket, illetve annak m´eret´et. (DM7520 WRITE BUFFERED), ´atadjuk a puffer¨ Ezek ut´ an j¨ onne a kimeneti puffer, azonban ´ır´asn´al erre nincs sz¨ uks´eg. A bytesWritten v´ altoz´ onkba fog beker¨ ulni az ´ırt b´ajtok sz´ama (a driverben ez volt az Information). Az utols´o param´eterre pedig csak aszinkron I/O eset´en van sz¨ uks´eg, amit˝ ol mi eltekint¨ unk. A DeviceIoControl bek¨ uldi a k´er´es¨ unket kernel-m´ odba, a driver stack tetej´ere, ahol az el˝obb-ut´obb feldolgoz´asra ker¨ ul. Ekkor az als´ o r´eteg mindig jelzi a felette l´ev˝onek, hogy a k´er´est v´egrehajtott´ ak, m´ıg el nem jut a driver stack tetej´ere, ahonnan az ir´any´ıt´as el˝ obb-ut´ obb visszaker¨ ul a DeviceIoControl -hoz, ami ezek ut´an visszat´er. Az ´ır´ ashoz nagyon hasonl´ o m´odon t¨ort´enik az olvas´as: 1 UINT32 DM7520 : : Read ( DM7520 : : MEMORY LOCATION memloc , UINT16 o f f s e t , DM7520 : : SIZE s i z e ) 2 { 3 DM7520 IO DESCRIPTOR i o D e s c ; 4 i o D e s c . memloc = memloc ; 5 ioDesc . o f f s e t = o f f s e t ; 6 ioDesc . s i z e = s i z e ; 7 ULONG b y t e s R e t u r n e d ; 8 D e v i c e I o C o n t r o l ( handle , DM7520 READ BUFFERED, (LPVOID) &i o D e s c , s i z e o f ( i o D e s c ) , (LPVOID) &i o D e s c , s i z e o f ( i o D e s c ) , & b y t e s R e t u r n e d , NULL) ; 9 return i o D e s c . data ; 10 }
Az egyetlen l´enyeges k¨ ul¨ onbs´eg, hogy itt m´ar kimeneti puffer¨ unk is van, ami t¨ ort´enetesen megegyezik a bemeneti pufferrel. Azonban mivel a driver mindig m´ asolatokkal t¨ ort´enik, ´ıgy ebb˝ol nem lesz gond. Mivel tesztel´esi c´elra k´esz¨ ult a driver¨ unk, a visszat´er´esi ´ert´ek egy 32-bites jel¨oletlen eg´esz, m´eg akkor is, ha esetleg az olvasni k´ıv´ ant mennyis´eg csak egyetlen b´ajt. Ezzel l´enyeg´eben teljess´e v´alt az API-nk, abb´ol a szempontb´ol, hogy mindenre k´epes. B´ ar nem a mai, sok absztrakci´os r´eteget alkalmaz´o fejleszt´esnek megfelel˝ oen k´esz¨ ult, a haszn´alata relat´ıve egyszer˝ u: 1 2 3 4
DM7520∗ d e v i c e = DM7520 : : OpenDevice ( ) ; d e v i c e −>Write ( DM7520 : : LAS0 , 0 x100 , DM7520 : : BYTE, 0 x42 ) ; UINT32 r e s u l t = d e v i c e −>Read ( DM7520 : : LAS1 , 0x06C , DM7520 : : BYTE) ; DM7520 : : C l o s e D e v i c e ( d e v i c e ) ;
26
3
A m´ er´ esek bemutat´ asa
A m´er´eseket a k¨ ovetkez˝ o hardverkonfigur´aci´on v´egezt¨ uk el: Oper´ aci´ os rendszer Windows 7 x64 Processzor Intel Core 2 Duo E8500 @ 3.16 GHz Mem´ oria (RAM) 4GB Videok´ artya Intel G33 Express Integrated Video Adapter M´er´es-adatgy˝ ujt˝ o k´ artya RTD DM7520 Oszcilloszk´ op HAMEG HMO2022 1. t´ abl´ azat - A hardverkonfigur´aci´onk A m´er´eseinket t¨ obb k¨ ul¨ onb¨oz˝o algoritmuson v´egezt¨ uk el, amelyek mind egy-egy konkr´et t´ıpus´ u munkav´egz´esre voltak ki´elezve. A k¨ovetkez˝okben a tesztprogramok, valamint a tesztel´esi keretprogram r¨ovid ismertet´ese k¨ ovetkezik.
3.1
A tesztprogramok
3.1.1
A keretprogram
A keretprogramnak nevezett k´odr´eszlet nem m´as, mint a k´odnak az a r´esze, amely az ¨ osszes tesztesetben megegyezik: a k´artya felkonfigur´al´asa, valamint a m´er´esi hat´ arok jelz´ese. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#include ”DM7520 . h” #include <Windows . h> // G l o b a l t e s t c a s e s p e c i f i c
initialisations ...
i n t main ( ) { DM7520∗ d e v i c e = DM7520 : : OpenDevice ( ) ; d e v i c e −>Write ( DM7520 : : LAS0 , 0 x100 , DM7520 : : BYTE, 0 x42 ) ; // R e s e t d e v i c e −>Write ( DM7520 : : LAS0 , 0x07C , DM7520 : : BYTE, 0 x05 ) ; // Port1 output while ( true ) { // Per−i t e r a t i o n
initialisations ...
d e v i c e −>Write ( DM7520 : : LAS0 , 0 x074 , DM7520 : : BYTE, 0xFF ) ; // Write d a t a S l e e p ( 1 ) ; // The o s c i l l o s c o p e i s s l o w d e v i c e −>Write ( DM7520 : : LAS0 , 0 x074 , DM7520 : : BYTE, 0 x00 ) ; // Write d a t a // The t e s t i n g a l g o r i t h m . . . d e v i c e −>Write ( DM7520 : : LAS0 , 0 x074 , DM7520 : : BYTE, 0xFF ) ; // Write d a t a d e v i c e −>Write ( DM7520 : : LAS0 , 0 x074 , DM7520 : : BYTE, 0 x00 ) ; // Write d a t a
24 } 25 DM7520 : : C l o s e D e v i c e ( d e v i c e ) ; 26 return 0 ; 27 }
27
A DM7520.h f´ ajl a m´ ar kor´abban bemutatott API-t tartalmazza, m´ıg a Windows.h-ra a Sleep f¨ uggv´eny miatt van sz¨ uks´eg. Ezek ut´an a tesztesetek glob´ alis v´ altoz´ oi / deklar´ aci´ oi k¨ovetkeznek. A main f¨ uggv´enyben megnyitjuk a k´ arty´ at, bet¨ oltj¨ uk az alapbe´all´ıt´asokat (9), majd be´all´ıtjuk az egyik portot kimenetnek. K¨ ovetkez˝ ok´ent egy v´egtelen ciklus j¨on, amelynek egy iter´aci´oj´aban az adott teszteset egyszeri lefut´ asa t¨ ort´enik. El˝osz¨or is vannak olyan inicializ´aci´os l´ep´esek, amelyeket nem tudunk glob´alisan megtenni, musz´aj minden egyes iter´ aci´ o elej´en be´ all´ıtani. Ezt k¨ovet˝oen kiadunk egy impulzust a k´arty´an, ez jelenti az algoritmus fut´ as´ anak kezdet´et. A jel visszav´etele el˝ott azonban v´ arunk egy milliszekundumot, ugyanis az oszcilloszk´opunk nagyobb id˝ otartamok vizsg´ alat´ an´ al sokszor nem vette ´eszre a mikroszekundum nagys´ ag´ u ideig jelen l´ev˝ o jelet. Az impulzus kiad´asa ut´an lefut maga a tesztelend˝ o algoritmus, ut´ ana pedig kiadunk m´eg egy impulzust, jelezv´en az algoritmus lefut´ as´ anak v´eg´et. V´egezet¨ ul a ciklusb´ ol kil´epve bez´arjuk az eszk¨oz¨ unket ´es visszat´er¨ unk. Term´eszetesen a k´ od jelenlegi form´aj´aban ez lehetetlen lenne, azonban ezt a tesztprogramot k´et k¨ ul¨ onf´ele m´odon haszn´altuk. Az egyik a k´odban l´athat´o, v´egtelen ciklusos m´ odon. Ebben az esetben felesleges a m´asodik impulzus kiad´ asa (jobban mondva ink´abb elhanyagolhat´o), tov´abb´a az eszk¨oz bez´ ar´ as´ ahoz sem jutunk el soha. Ebben a m´odban mindig ”er˝oszakos m´odon”, feladatkezel˝ ob˝ ol lett bez´ arva a programunk. Mivel a Windows egy program bez´ ar´ asakor felszabad´ıt minden hozz´a tartoz´o er˝oforr´ast, ez´ert nem kell agg´ odnunk amiatt, hogy a k´arty´ahoz esetleg maradnak bez´aratlan hozz´ af´er´esek. A m´ asik haszn´ alati m´ odja a szoftvernek az volt, hogy a ciklusfelt´etel egy billenty˝ u lenyom´ asa volt. Ebben az esetben egy billenty˝ u le¨ ut´esekor lefutott egy iter´ aci´ o az algoritmusb´ ol. Ilyenkor m´ar a m´asodik impulzusra is sz¨ uks´eg van az algoritmus befejez˝ od´es´enek ´erz´ekel´es´ehez. Volt term´eszetesen egy billenty˝ u, amelynek lenyom´ as´ ara a program kil´epett. 3.1.2
ArrayCopy
Ez a tesztprogram a mem´ ori´ an bel¨ ul adatm´asol´asra lett kialak´ıtva. C´elja a ford´ıt´ o k¨ ul¨ onf´ele p´ arhuzamos´ıt´asi k´epess´egeinek vizsg´alata volt. 1 2 3 4 5 6 7 8 9
// G l o b a l i n i t i a l i s a t i o n s : #define N 1024∗1024∗768 char a [N] , b [N ] ; // The a l g o r i t h m : f o r ( i n t i = 0 ; i < N; ++i ) { a[ i ] = b[ i ]; }
Mint az l´ athat´ o, t¨ omb¨ ok egyszer˝ u m´asol´as´ar´ol van sz´o, amelyek a mai nagy mennyis´eg˝ u adattal dolgoz´ o alkalmaz´asokban rengetegszer el˝ofordulnak. A hardver¨ unk korl´ atozott k´epess´egei miatt v´eg¨ ul a 768 MB-os blokkok m´asol´asa mellett d¨ ont¨ ott¨ unk. Mivel a ciklusnak az iter´aci´o teljesen f¨ uggetlenek egym´ast´ol, ´ıgy nagy m´ert´ekben p´ arhuzamos´ıthat´ o lenne (t¨obb sz´alon), valamint rem´enyeink szerint a gener´ alt g´epi k´ odban rengeteg vektorm˝ uvelettel is tal´alkozni fogunk. 28
3.1.3
Parallel
Ez a teszt szint´en a ford´ıt´ o p´ arhuzamos´ıt´asi k´epess´egeit pr´ob´alja m´erni, azonban itt behoztuk a k´epbe a lebeg˝ opontos utas´ıt´asokat is. 1 2 3 4 5 6 7 8 9
// G l o b a l i n i t i a l i s a t i o n s : #define N 1000 double a [N ] ; i n t b [N ] ;
// The a l g o r i t h m : f o r ( i n t i = 0 ; i < N; ++i ) { a [ i ] = rand ( ) / ( double ) ( rand ( ) + 1 ) ∗ ( − 3 7 . 0 ) ∗ ( double ) rand ( ) / ( double ) ( rand ( ) + 1 ) ; 10 b [ i ] = rand ( ) / ( rand ( ) + 1 ) ∗ 37 ∗ rand ( ) / ( rand ( ) + 1 ) ; 11 }
3.1.4
Abstraction
Enn´el a tesztn´el az absztrakci´os r´eteget teljes´ıtm´enyre vet¨ ul˝o hat´asait vizsg´ altuk. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// G l o b a l i n i t i a l i s a t i o n s #define N 50 typedef i n t ( ∗ f u n c t i o n ) ( i n t ) ; f u n c t i o n f u n c t i o n s [N ] ; i n t myFunction ( i n t { i f ( n == 0 ) { for ( int i = 0 ; { functions [ i ] } } return ( n < N) ? }
n = 0)
i < N; ++i ) = myFunction ;
f u n c t i o n s [ n ] ( n + 1) : 0 ;
// The a l g o r i t h m f o r ( i n t i = 0 ; i < N; ++i ) { a[ i ] = b[ i ]; }
Ezzel az els˝ ore tal´ an kicsit elbonyol´ıtottan meg´ırt rekurzi´oval az volt a c´elunk, hogy kiker¨ ulj¨ uk a ford´ıt´onak a rekurzi´ot optimaliz´al´o be´all´ıt´asait (pl. egyszer˝ u cikluss´ a alak´ıt´ as). Mivel itt a f¨ uggv´eny-pointer t¨omb¨ unk csak fut´ askor ker¨ ul felt¨ olt´esre, amely r´aad´asul nem is biztos hogy lefut, ´ıgy a ford´ıt´o k´enytelen minden egyes k´ odban l´athat´o f¨ uggv´enyh´ıv´ashoz k´odot gener´alni, nem tudja kioptimaliz´ alni azt. Az algoritmusnak egy iter´aci´oj´aban N 2 f¨ uggv´enyh´ıv´ as zajlik le. 3.1.5
Fibonacci
A Fibonacci nev˝ u tesztprogramunk a Fibonacci sz´amok kisz´amol´as´ar´ol sz´ol, amelynek a l´ep´esei egym´ ast´ ol f¨ uggenek, nehez´ıtve ezzel a p´arhuzamos k´od gener´ al´ as´ at. 29
1 2 3 4 5 6 7 8 9 10 11 12
// Per−i t e r a t i o n i n i t i a l i s a t i o n s i n t x1 = 1 , x2 = 1 , c u r r e n t = 1 , tmp ; // The a l g o r i t h m #define N 1000000000L f o r ( i n t 6 4 i = 0 ; i < N; ++i ) { tmp = c u r r e n t ; c u r r e n t = x1 + x2 ; x2 = x1 ; x1 = tmp ; }
3.1.6
StringCPP11
Ez az algoritmus a mai programjainkban rendk´ıv¨ ul gyakran el˝ofordul´o t¨ ombm´ asol´ as´ anak egy speci´ alis eset´et, a sz¨ovegm´asol´ast veszi g´orcs˝o al´a. Ehhez a tesztprogramhoz ´ırtunk egy rendk´ıv¨ ul primit´ıv sz¨oveget kezel˝o oszt´ alyt, amely tartalmazza az alap konstruktorokat ´es a destruktort. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// G l o b a l i n i t i a l i s a t i o n s c l a s s MyString { char∗ s t r ; public : MyString ( ) : {}
str ( nullptr )
MyString ( const char∗ s t r ) : { strcpy ( str , str ) ; }
s t r (new char [ s t r l e n ( s t r ) + 1 ] )
MyString ( const MyString& mystr ) : ) + 1]) { s t r c p y ( s t r , mystr . s t r ) ; }
s t r (new char [ s t r l e n ( mystr . s t r
16 17 18 19 20 MyString& operator=(const char∗ s t r ) 21 { 22 delete s t r ; 23 s t r = new char [ s t r l e n ( s t r ) + 1 ] ; 24 strcpy ( str , str ) ; 25 return ∗ t h i s ; 26 } 27 28 MyString& operator=(const MyString& mystr ) 29 { 30 i f ( t h i s == &mystr ) 31 return ∗ t h i s ; 32 return operator=(mystr . s t r ) ; 33 } 34 35 ˜ MyString ( ) 36 { 37 delete s t r ; 38 } 39 } ;
30
40 41 42 43 44 45 46
extern const char∗ p i ; MyString GetMyString ( ) { return MyString ( p i ) ; }
A pi nev˝ u k¨ uls˝ o v´ altoz´ onk egy m´asik f´ajlban van defini´alva, ami egy k¨ or¨ ulbel¨ ul 9100 karakterb˝ ol ´ all´o, a π sz´amjegyeit tartalmaz´o karakterl´anc. 1 2 3 4 5 6
// The a l g o r i t h m MyString mystr ; f o r ( i n t i = 0 ; i < 1 0 0 0 0 0 ; ++i ) { mystr = GetMyString ( ) ; }
3.2
M´ er´ esek az alapbe´ all´ıt´ assal
Alapbe´ all´ıt´ as alatt a Visual Studio 2012 ´altal alapb´ol felkonfigur´alt Release m´ odot ´ertj¨ uk, 64-bites ford´ıt´assal. Ez a k¨ovetkez˝o ford´ıt´asi kapcsol´okat eredm´enyezte [5] (meghagytuk a Visual Studio ´altal alapb´ol l´etrehozott sorrendet): /GS Extra k´ od gener´ al´ asa a puffer t´ ulcsordul´asok ´eszrev´etele ´erdek´eben. /GL Teljes k¨ or˝ u programoptimaliz´al´as. En´elk¨ ul csak modulonk´ent (ford´ıt´asi egys´egenk´ent) t¨ ort´enik optimaliz´aci´o. /W3 A figyelmeztet´esek szintj´et szab´alyoz´o kapcsol´o. optimaliz´ aci´ oban.
Irrelev´ans az
/Gy Az alapb´ ol k¨ ul¨ on-k¨ ul¨ on elhelyezked˝o f¨ uggv´enyek u ´n. f¨ uggv´enycsomagokba val´ o csomagol´ as´ at enged´elyezi. /Zc:wchar t Hat´ as´ ara a ford´ıt´o a wchar t-t be´ep´ıtett t´ıpusk´ent kezeli. Irrelev´ ans. /Zi A debug inform´ aci´ o gener´al´ast befoly´asolja. Hat´as´ara az inform´aci´ot egy k¨ ul¨ on .pdb f´ ajlba teszi. Irrelev´ans. /Gm- Kikapcsolja az u ´n. minim´alis u ´jraford´ıt´ast, teh´at hi´aba m´odosul csak egyetlen ford´ıt´ asi egys´eg, az ¨osszes u ´jra lesz ford´ıtva. Irrelev´ans. /O2 Optimaliz´ aci´ os be´ all´ıt´ as a maxim´alis sebess´eg el´er´ese ´erdek´eben. /Fd”x64Testvc110.pdb” A .pdb f´ajl hely´et hat´arozza meg. Irrelev´ans. /fp:precise A lebeg˝ opontos m˝ uveletek sebess´eg-pontoss´ag ar´any´at hat´arozza meg. Ez a legpontosabb be´all´ıt´as, ´es egyben az alapbe´all´ıt´as is. /D ” MBCS” K¨ ul¨ onb¨ oz˝ o szimb´olumokat #define-ol. /errorReport:prompt A ford´ıt´oban l´ev˝o bels˝o hib´ak kezel´es´et manipul´alja. Alapb´ ol ha egy ilyen hiba el˝oj¨on, sz´ol a felhaszn´al´onak, majd tov´abb´ıtja a Microsoft fel´e. Irrelev´ ans 31
/WX- Deaktiv´ alja a ”minden figyelmeztet´es hiba” funkcionalit´ast. Irrelev´ans. /Zc:forScope Seg´ıts´eg´evel a for ciklus inicializ´aci´os v´altoz´oi csak a ciklus v´eg´eig tartanak (ellenkez˝o esetben a cikluson k´ıv¨ uli legk¨ozelebbi scope-ig). Irrelev´ ans. /Gd Az alap´ertelmezett h´ıv´ asi konvenci´o a
cdecl.
/Oi Enged´elyezi a k¨ ul¨ onb¨ oz˝ o f¨ uggv´enyh´ıv´asok speci´alis inline-f¨ uggv´enyekk´e val´ o optimaliz´ aci´ oj´ at. /MD A runtime k¨ onyvt´ arak (CRT) t¨obbsz´al´ u, DLL v´altozat´at fogja haszn´alni a programunk. /Fa”x64Test” A gener´ alt assembly k´od hely´et hat´arozza meg. Irrelev´ans. /EHsc A kiv´etelkezel´es m´ odj´at hat´arozza meg. Ez a be´all´ıt´as enged´elyezi a C++ kiv´etelek haszn´ alat´at ´es felt´etelezi, hogy az extern ”C”-vel ell´atott f¨ uggv´enyek sosem dobnak kiv´etelt. /nologo Megakad´ alyozza a ford´ıt´o indul´askor l´athat´o log´oj´anak mutat´as´at. Irrelev´ ans. /Fo”x64Test” A gener´ alt t´ argyk´od hely´et hat´arozza meg. Irrelev´ans. /Fp”x64Testtest.pch” Az el˝oford´ıtott header f´ajl hely´et szabja meg. Irrelev´ ans. Ezzel a be´ all´ıt´ assal a k¨ ovetkez˝o eredm´enyeket ´ert¨ uk el (Id˝o alatt a tov´ abbiakban mindig az egy iter´aci´ohoz sz¨ uks´eges ´atlagos id˝ot ´ertj¨ uk): Teszteset Id˝ o ArrayCopy 328.55 ms Parallel 889.07 ms Fibonacci 532.27 ms Abstraction 264.56 ms StringCPP11 1560 ms 2. t´ abl´ azat - Az alap m´er´esi eredm´enyeink A tov´ abbiakban egyes´evel megvizsg´alunk p´ar lehets´eges optimaliz´aci´os l´ep´est, illetve annak hat´ asait a k´odra.
3.3
A c´ elplatform
Manaps´ ag m´ ar egyik nagy processzorokkal foglalkoz´o v´allalat sem gy´art olyan a´ltal´ anos felhaszn´ al´ as´ u PC / Notebook processzort, amely ne t´amogatn´a az x86-64 utas´ıt´ ask´eszletet, azaz ne lenne k´epes 64-bites m´odban futni. Az al´ abbi, 2010-ben k´esz¨ ult statisztika szerint a Windows futtat´o sz´am´ıt´og´ep kevesebb mint fele volt csak 64-bites, addig a szoftveroptimaliz´aci´o egyik fontos ter¨ ulet´en, a j´ at´ekpiacon a felhaszn´al´ok 59,70 sz´azal´eka futtatott 64-bites Windows 7 oper´ aci´ os rendszert 2012 szeptember´eben a Steam rendszer statisztik´ ai szerint. [4] 32
3. t´ abl´ azat - A Windows platformok megoszl´asa
Mindezek ellen´ere a mai napig rendk´ıv¨ ul kev´es szoftverb˝ol ´erhet˝o el 64-bites v´ altozat, annak ellen´ere, hogy a mi m´er´eseink alapj´an jelent˝os sebess´egk¨ ul¨ onbs´egeket lehetne el´erni ezekkel. Term´eszetesen a 64-bites t´ amogat´ as majdnem hogy megk´etszerezi a term´ekt´amogat´ashoz sz¨ uks´eges munk´ at, hiszen k´et k¨ ul¨ onb¨ oz˝ o term´eket forgalmazn´anak egyszerre. Minden k´ odot k´etszer kell leford´ıtani, ´es a bin´arisoknak nem szabad ¨osszekeveredni¨ uk. A k´es˝ obbi patch-eket is mindig k´et k¨ ul¨on v´altozatban kell biztos´ıtani. Ami a ford´ıt´ ast illeti, ha egy k´ odot j´ol ´ırunk meg, akkor mindennem˝ u v´altoztat´as n´elk¨ ul ´ at lehet adni a 64-bites ford´ıt´onak. Egy tov´ abbi probl´ema ezen a t´eren, hogy ez m´ar t´ ul ”fejlett” egy ´atlag sz´ am´ıt´ og´ep-felhaszn´ al´ onak. A legt¨obben nem tudj´ak milyen rendszert futtatnak, ´es ha most elkezden´enek a boltokban t¨obbf´ele v´altozatot is forgalmazni, rengetegen venn´enek rossz v´altozatot, amely tov´abbi term´ekt´ amogat´ asi ´es jogi probl´em´akhoz vezethet. Ezen a probl´em´an n´emik´epp seg´ıthet a mai let¨ olt´eses v´ as´arl´as, ahol automatikusan lehetne ´erz´ekelni a sz´ am´ıt´ og´ep konfigur´ aci´ oj´ at ´es ez´altal a megfelel˝o v´altozatot telep´ıteni. Ezt a megold´ ast azonban m´eg csak a nagyobb v´allalatok megold´asaiban lehet felfedezni. P´eld´ aul a Visual Studio webes telep´ıt˝oje telep´ıt´es k¨ozben vizsg´alja meg a rendszert, ´es t¨ olti le a sz¨ uks´eges komponenseket. A teszteredm´enyeink egy´ertelm˝ uen kimondt´ak, hogy 64-bites rendszeren l´ atv´ anyos a k¨ ul¨ onbs´eg a 32- illetve a 64-bites szoftverek fut´asi sebess´egei k¨ozt: Teszteset Id˝ o (x64) Id˝ o (x86) Sebess´ egn¨ oveked´ es ArrayCopy 328.55 ms 546.19 ms 39.85% Parallel 889.07 ms 995.56 ms 10.70% Fibonacci 532.27 ms 1273 ms 58.19% Abstraction 264.56 ms 304.11 ms 13.01% StringCPP11 1560 ms 1790 ms 12.85% 4. t´ abl´ azat - x86 kontra x64 teszteredm´enyek Mint l´ athat´ o, a legrosszabb esetben is t¨obb, mint 10 sz´azal´ekos teljes´ıtm´enyn¨ oveked´est ´ert¨ unk el. Azonban a nagy sz´amokkal dolgoz´o 33
alkalmaz´ asok, mint p´eld´ aul a mi Fibonacci teszt¨ unk is, kihaszn´alhatj´ak a 64-bites operandus´ u m˝ uveletek el˝onyeit, ´es ´ıgy ak´ar m´asf´elszeres sebess´egn¨ oveked´est is el´erhet¨ unk. Ennek a tesztnek az eredm´enye az lett, hogy egy´ertelm˝ uen bel´attuk, hogy jelent˝ os el˝ ony¨ uk van a 64-bites programoknak a 64-bites rendszereinken, ´ıgy azt javasoljuk, hogy amennyiben erre lehet˝os´eg¨ unk van, biztos´ıtsunk a szoftver¨ unkb˝ ol 64-bites v´ altozatot is.
3.4
Pufferes biztons´ agi ellen˝ orz´ esek kikapcsol´ asa
Az alap´ertelmezettk´ent be´ all´ıtott kapcsol´ok k¨oz¨ott tal´alkozhattunk a /GS ford´ıt´ asi kapcsol´ oval, amelynek hat´as´ara a ford´ıt´onk automatikusan k´odot gener´ al a f¨ uggv´enyvisszat´er´esekhez, t¨omb¨ok indexel´es´ehez, illetve egy´eb olyan utas´ıt´ asokhoz, amelyekn´el egy puffer t´ ulcsordul´as befoly´asolhatn´a az eredm´enyt. Egy p´elda erre a k¨ovetkez˝o: egy rosszindulat´ u hacker egy olyan adat megad´ as´ aval amely t´ ulcsord´ıtja a puffert, ´at tudja ´ırni a stack-en a visszat´er´esi c´ımet, potenci´ alisan rossz indulat´ u k´odnak ´atadv´an ezzel az ir´ any´ıt´ ast. Azonban ha belegondolunk ennek a kapcsol´onak a gyakorlati jelent˝os´eg´ebe, hogy ez mi´ert is j¨ ohetett l´etre, mi a k¨ovetkez˝ore jutottunk: r´egen, amikor m´eg kev´es volt a t´ arter¨ ulet ´es a processzorok feldolgoz´ok´epess´ege, kritikus volt min´el t¨ om¨ orebb ´es gyorsabb k´ odot ´ırni. Viszont amikor elkezdtek az ´atlag sz´am´ıt´og´ep param´eterei javulni, a szoftverfejleszt´es sokkal biztons´agosabb´a v´alt. Legal´abbis elkezd˝ odtek a mozgalmak efel´e, ez´ert a Microsoft u ´gy d¨ont¨ott, hogy a ford´ıt´ojuk automatikusan gener´ alni fogja ezeket az apr´o ellen˝orz´eseket, hiszen sosem fog mindenki biztons´ agos k´ odot ´ırni. Term´eszetesen ezek az ellen˝orz´esek nem teljes k¨ or˝ uek, ´ıgy nem hanyagolhatjuk el a k´odunkban a biztons´agi ellen˝orz´eseket, azonban egy minim´ alis v´edelmet ny´ ujtanak. Ugyanakkor egy biztons´agosan meg´ırt k´ od eset´eben tulajdonk´eppen feleslegesen kidobott teljes´ıtm´enynek is nevezhetj¨ uk ezt a funkcionalit´ ast. A tesztjeinkben mi kikapcsoltuk a pufferellen˝orz˝o k´od gener´al´as´at, amelyet a /GS- kapcsol´ oval ´ert¨ unk el. Ez a k¨ovetkez˝o eredm´enyekhez vezetett:
Teszteset ArrayCopy Parallel Fibonacci Abstraction StringCPP11
Id˝ o (/GS) Id˝ o (/GS-) Sebess´ egn¨ oveked´ es 328.55 ms 306.51 ms 6.71% 889.07 ms 854.66 ms 3.87% 532.27 ms 520.72 ms 2.17% 264.56 ms 264.46 ms 0.04% 1560 ms 1490 ms 4.49% 5. t´ abl´ azat - A /GS kapcsol´o hat´asa
Mint az l´ athat´ o, az alkalmaz´asaink sebess´ege konzisztensen javult, azonban meglehet˝ osen csek´ely m´ert´ekben. Felmer¨ ul a k´erd´es, hogy ´erdemes lehet-e lemondani egy biztons´ agi r´etegr˝ol p´ar sz´azal´eknyi sebess´egn¨oveked´es el´er´ese ´erdekeben. Mi u ´gy gondoljuk hogy mivel az u ´j oper´aci´os rendszerek mindig egyre t¨ obb ´es t¨ obb biztons´ agot ny´ ujtanak nek¨ unk, amelyek megv´edik az alkalmaz´ asainkat a k¨ uls˝ o behatol´asok ellen, ez´ert meg´erheti. Amennyiben a k´ odunkban u ¨gyel¨ unk az alapvet˝o biztons´agra, mint p´eld´aul a puffereinkbe
34
ker¨ ul´es el˝ ott az adatok hossz´anak ellen˝orz´ese, u ´gy nem fog cs¨okkenni a programunk v´edetts´ege. Ebben az esetben l´enyeg´eben ”ingyen”, mindennem˝ u h´ atul¨ ut˝ o n´elk¨ ul kapjuk a p´ ar sz´azal´eknyi sebess´egjavul´ast, amelyet r´aad´asul egyetlen egyszer˝ u ford´ıt´ asi kapcsol´o megad´as´aval el is tudunk ´erni.
3.5
Az architekt´ ura specifik´ al´ asa
A Visual Studio 2012 optimaliz´aci´os be´all´ıt´asai k¨oz¨ott tal´alhatunk egy ”minim´ alis architekt´ ura” nev˝ u be´all´ıt´ast. A k¨ovetkez˝o lehet˝os´egek k¨oz¨ ul v´ alaszthatunk: /arch:IA32 Csak az x87 FPU utas´ıt´ask´eszlet´et haszn´alja. /arch:SSE Enged´elyezi a Streaming SIMD Extensions utas´ıt´ask´eszletet. /arch:SSE2 Enged´elyezi az SSE2 utas´ıt´ask´eszletet. /arch:AVX Enged´elyezi a 2011-ben deb¨ ut´alt Advanced Vector Instructions utas´ıt´ ask´eszletet. Egy adott utas´ıt´ ask´eszlet enged´elyez´ese automatikusan enged´elyezi a list´ aban felette szerepl˝ oket is. Sajnos a tesztsz´ am´ıt´ og´ep¨ unk processzora nem t´amogatja az AVX utas´ıt´ asokat, ez´ert csak az SSE2 utas´ıt´asokat tudtuk bekapcsolni. Azonban ´erdemes megjegyezni, hogy a gener´alt k´od az AVX utas´ıt´ask´eszletet specifik´ alva elt´ert a csak SSE2-t haszn´al´o k´odt´ol, ez´ert elk´epzelhet˝o, hogy annak haszn´ alat´ aval nagyobb sebess´egn¨oveked´est lehetne el´erni. Az x87 FPU lebeg˝ opontos seg´edprocesszor megjelen´ese ´ota rengeteg id˝o telt el, rengeteg u ´j technol´ ogia jelent meg ´es a processzorokkal szembeni elv´ ar´ asok is teljesen ´ atalakultak. Am´ıg r´egen a processzor ´orajel´et mindig csak n¨ ovelve nem volt jelent˝ os a p´ arhuzamos´ıt´as gondolata, addig manaps´ag szinte egy´ altal´ an nem emelkednek a processzorok ´orajelei, azonban a p´arhuzamos m˝ uvelet v´egrehajt´ as m´ert´eke egyre csak n˝o. Ebben a feladatk¨orben seg´ıtenek az els˝ osorban p´ arhuzamos adatfeldolgoz´asra ´es lebeg˝opontos sz´am´ıt´asokra kialak´ıtott SSE ´es AVX utas´ıt´ask´eszletek. Ezek n´elk¨ ul a processzorban csak ”mesters´eges” p´ arhuzamos v´egrehajt´as j¨on l´etre, p´eld´aul az utas´ıt´asok pipeline-ol´ as´ aval. Az SSE utas´ıt´ask´eszlet viszont rengeteg olyan u ´j utas´ıt´ast tartalmazott, amelyekkel egy adott m˝ uveletet egyszerre lehetett t¨obb adaton v´egezni, ez a SIMD (Single Instruction Multiple Data - Egy utas´ıt´as, t¨obb adat) utas´ıt´ asok l´enyege. A minim´ alis processzorarchitekt´ ura megemel´es´evel szinte biztosan javul az alkalmaz´ asunk teljes´ıtm´enye, amennyiben az tartalmaz b´arminem˝ u SIMD utas´ıt´ ass´ a alak´ıthat´ o r´eszeket (ide tartoznak p´eld´aul a sz¨ovegekkel val´o m˝ uveletv´egz´esek is) vagy lebeg˝opontos m˝ uveleteket. Viszont a r´egebbi processzorok, amelyek nem t´ amogatj´ak az u ´jabb utas´ıt´ask´eszletet nem fogj´ak tudni futtatni a programot, az ugyanis ”´erv´enytelen utas´ıt´as” hiba¨ uzenettel le fog ´ allni. Az optim´ alis minimum meg´allap´ıt´as´ahoz tekints¨ uk az al´abbi statisztik´ at:
35
Technol´ ogia Elterjedts´ eg SSE2 99.78% SSE3 99.18% SSE4.1 57.26% 6. t´ abl´ azat - Az SSE technol´ogi´ak elterjedts´ege A t´ abl´ azatban tal´ alhat´ o ´ert´ekek a Steam Hardver K´erd˝o´ıv´eb˝ol sz´armaznak, amelynek profilja a sz´ am´ıt´ og´epes j´at´ekok. Emiatt es´elyes, hogy az ´atlagn´al n´emik´epp magasabb ´ert´ekeket l´atunk. Ett˝ol f¨ uggetlen¨ ul azonban kijelenthetj¨ uk, hogy az SSE2-es utas´ıt´ask´eszletet nyugodtan enged´elyezhetj¨ uk, hiszen az SSE3-at is csak a felhaszn´al´ok egy nagyon sz˝ uk r´eteg´enek nem t´ amogatja a processzora. Ezzel szemben az SSE4, amely az AVX el˝ott n´egy ´evvel, 2007-ben ker¨ ult a processzorokba, alig t¨obb mint a felhaszn´al´ok fel´enek g´ep´eben tal´ alhat´ o meg. Ezek alapj´ an azt mondhatjuk, hogy m´ar csak a korszer˝ us´eg miatt is ´erdemes minden programot az SSE2 utas´ıt´ask´eszlettel ford´ıtani (A Visual Studio nem biztos´ıt lehet˝ os´eg k¨ ul¨on az SSE3, SSSE3 ´es az SSE4 utas´ıt´ ask´eszletek haszn´ alat´ ara), ha az alkalmaz´asunk az adott rendszeren gond n´elk¨ ul fut (teh´ at t´ amogatva van az utas´ıt´ask´eszlet), akkor semminem˝ u h´atr´any nem ´er minket, maximum gyorsabban fog futni a szoftver¨ unk. Alacsony elterjedts´ege miatt azonban sem az SSE4-et ´es ebb˝ol kifoly´olag az AVX-et sem ´erdemes m´eg minim´ alisan t´ amogatott processzorarchitekt´ urak´ent elv´arni. A mi tesztjeinket teh´ at /arch:SSE2 ford´ıt´asi kapcsol´oval ford´ıtottuk, amely a k¨ ovetkez˝ o eredm´enyeket hozta: Teszteset ArrayCopy Parallel Fibonacci Abstraction StringCPP11
Id˝ o Id˝ o (/arch:SSE2) Sebess´ egn¨ oveked´ es 328.55 ms 320.88 ms 2.33% 889.07 ms 840.12 ms 5.51% 532.27 ms 516.52 ms 2.96% 264.56 ms 264.51 ms 0.02% 1560 ms 1390 ms 10.90% 7. t´ abl´ azat - A /arch kapcsol´o hat´asa
A tesztjeink alapj´ an kijelenthetj¨ uk, hogy a teszteseteinkben nem mutatkozott jelent˝ osebb m´ert´ek˝ u sebess´egn¨oveked´es, az utols´o tesztet lesz´ am´ıtva. Ennek ellen´ere azonban javasoljuk a minim´alisan elv´art architekt´ ura megemel´es´et el˝ obb-ut´obb, hiszen az egy fejlettebb technol´ogi´at haszn´ al, amelyb˝ ol h´ atr´ anyunk biztosan nem, de el˝ony¨ unk esetleg lehet.
3.6
P´ arhuzamos k´ odgener´ al´ as
Mi´ ota a processzorok ´ orajeleinek frekvenci´ai abbahagyt´ak a trendszer˝ u n¨ oveked´es¨ uket a p´ arhuzamos´ıt´asra cs´ uszott ´at a f´okusz. Az informatika ezen a´gazata jelenleg a t¨ obbmagos, t¨obbprocesszoros, t¨obb sz´am´ıt´og´epes rendszerekr˝ ol sz´ ol. Egy szoftverfejleszt˝o sz´am´ara a p´arhuzamos´ıt´as t¨obb szinten b´ır jelent´essel. A fejleszt˝o ak´ar az eg´esz p´arhuzamos´ıt´ast a kez´ebe veheti, ˝ o maga ir´ any´ıthatja a sz´alakat. Ebben az esetben azonban rengeteg id˝o megy el arra, hogy trivi´ alisan p´arhuzamos´ıthat´o feladatokat menedzseljen 36
k´ezzel, ami r´ aad´ asul a k´ od olvashat´os´ag´an is ronthat. Egy nagyon egyszer˝ u p´elda erre mondjuk egy t¨omb m´asol´asa. Ha a ford´ıt´o semmilyen p´ arhuzamos´ıt´ ast nem v´egez el, hanem ehelyett mindent a programoz´onak kell megcsin´ alnia, akkor a k´ od jelent˝os r´esze a k¨ ul¨onb¨oz˝o sz´alak ir´any´ıt´as´ar´ol fog sz´ olni. A m´ asik v´eglet az, amikor mindent a ford´ıt´ora b´ızunk. Azonban sok esetben a ford´ıt´ oknak es´ely¨ uk sincs kital´alni, hogy mely r´eszek p´ arhuzamos´ıthat´ oak ´es melyek nem an´elk¨ ul, hogy a programoz´o erre b´ arminem˝ u utal´ ast tenne. Tegy¨ uk hozz´a, hogy a vektorutas´ıt´asokat jelen esetben nem tekintj¨ uk p´ arhuzamos´ıt´asnak, hiszen most a t¨obbsz´al´ u alkalmaz´ asokr´ ol van sz´ o. Mivel a sz´alak kezel´ese jelenleg m´eg nem egy nagyon sz´eles k¨ orben elterjedt dolog, ez´ert a Visual Studio ezt a hatalmat nem veszi ki a kez¨ unkb˝ ol, ´es ¨ onmag´ at´ ol soha nem hoz l´etre k¨ ul¨on sz´alakat az alkalmaz´ asainkban. A k´et v´eglet k¨ oz¨ ott helyezkedik el a ford´ıt´ok dolg´anak megk¨onny´ıt´es´en alapul´ o, direkt´ıv´ akon kereszt¨ uli p´arhuzamos k´odgener´al´as. Ebben az esetben k¨ ul¨ onb¨ oz˝ o direkt´ıv´ akkal tudunk a ford´ıt´onknak ”tippeket” adni, hogy a k¨ ovetkez˝ o k´ odr´eszletet esetleg megpr´ob´alhatn´a p´arhuzamos´ıtani. Ehhez term´eszetesen ´ atadhatunk extra inform´aci´okat a ford´ıt´onak, megk¨onny´ıtve ezzel a dolg´ at. Ilyen extra inform´aci´o lehet p´eld´aul, hogy garant´aljuk, hogy egy adott v´ altoz´ ot csak a k¨ ovetkez˝o k´odr´eszlet haszn´alja, teh´at egy m´asik sz´al azzal soha nem foglalkozik. A m´odszer h´atr´anya, hogy nem rendelkez¨ unk akkora ir´ any´ıt´ assal a sz´ alak felet, mint az els˝o esetben. El˝onye viszont, hogy mivel csak direkt´ıv´ akr´ ol besz´el¨ unk, ´ıgy tulajdonk´eppen nem r´esze a t´enyleges forr´ ask´ odunknak, ´ıgy ki tudjuk kapcsolni a p´arhuzamos k´od gener´al´as´at (p´eld´ aul egy direkt´ıv´ aval, amellyel be-ki kapcsolhatjuk a p´arhuzamos´ıt´asr´ol sz´ ol´ o direkt´ıv´ ak figyelembe v´etel´et), hogy a k´odba bele kellene ny´ ulnunk, esetleges hib´ akat el˝ oid´ezve ez´ altal. Erre a megold´asra alapul a Visual Studio a´ltal alapb´ ol is t´ amogatott OpenMP nev˝ u API. Azonban nem sz¨ uks´eges u ´j API-kat haszn´alni, ugyanis a Visual Studi´onak van egy saj´ at be´ all´ıt´ asa, amelynek seg´ıts´eg´evel a ford´ıt´o automatikusan tudja a ciklusainkat p´ arhuzamos´ıtani. Persze a ciklusok csak egy nagyon apr´o r´esz´et teszik ki a k´ odunknak, azonban jelenl´et¨ uk messze nem elhanyagolhat´o, ´es a be´ all´ıt´ as nagyon egyszer˝ u haszn´alata miatt egy pr´ob´at mindenf´elek´eppen meg´erhet mindenkinek. Ez a bizonyos kapcsol´o a /Qpar amely az ´altalunk megjel¨ olt ciklusokat megvizsg´alja ´es amennyiben lehet˝os´eget l´at r´a, p´ arhuzamos´ıtja ˝ oket. A ciklusaink megjel¨ ol´ese nem tesz m´ast, mint egy egyszer˝ u direkt´ıva haszn´ alat´ at. Ez a direkt´ıva a #pragma loop(hint parallel(n)). Az n egy nemnegat´ıv eg´esz param´eter, amellyel megmondhatjuk, hogy a ciklusunkat h´ any sz´ alra ossza sz´et a ford´ıt´o. Ha a param´etert 1-re ´all´ıtjuk, azaz arra k´erj¨ uk a ford´ıt´ ot, hogy egy sz´alon pr´ob´alja p´arhuzamos´ıtani a ciklusunkat, az olyan mint ha semmit sem ´ırtunk volna. Viszont a param´eter nulla ´ert´eke eset´en a ford´ıt´ o megpr´ ob´ alja a maximaliz´alni a sz´alak sz´am´at. A tesztprogramunkat ez a k¨ ovetkez˝ok´eppen befoly´asolta: 1 2 3 4 5
// ArrayCopy : a l g o r i t h m #pragma l o o p ( h i n t p a r a l l e l ( 0 ) ) f o r ( i n t i = 0 ; i < N; ++i ) { a[ i ] = b[ i ]; }
37
A t¨ obbi tesztesetben hasonl´ok´eppen v´altozott a k´odunk. Sajnos a hardver¨ unk korl´ atolts´ aga miatt a mi eset¨ unkben az egyszerre fut´o sz´alak sz´ ama kett˝ oben maximaliz´ al´ odott. Ennek ellen´ere az eredm´enyeinken ´ıgy is seg´ıtett ez az apr´ o tr¨ ukk: Teszteset ArrayCopy Parallel Fibonacci StringCPP11
Id˝ o Id˝ o (/Qpar) Sebess´ egn¨ oveked´ es 328.55 ms 300.21 ms 8.63% 889.07 ms 824.12 ms 7.31% 532.27 ms 502.18 ms 5.65% 1560 ms 1430 ms 8.33% 8. t´ abl´ azat - A /Qpar kapcsol´o hat´asa
Az eredm´enyeink alapj´ an elmondhatjuk, hogy a szoftvereink teljes´ıtm´enye egy´ertelm˝ uen megn˝ ott, ´ atlagosan 7.5%-kal. B´ar t´eny, hogy itt m´ar nem volt el´eg egy egyszer˝ u ford´ıt´ asi kapcsol´ot be´all´ıtani, a forr´ask´odba is bele kellett ´ırni, azonban ennek a m´ert´eke minim´alis, tov´abb´a ak´ar minden ciklus el´e is beilleszthetj¨ uk a #pragma loop(hint parallel(0)) sort, hiszen amennyiben a ciklus nem p´ arhuzamos´ıthat´ o, akkor semmilyen hiba¨ uzenetet nem fogunk kapni, eg´eszen egyszer˝ uen olyan lesz, mint ha a direkt´ıv´ankat oda sem ´ırtuk ¨ volna. Osszegezve teh´ at, amennyiben semmilyen m´as p´arhuzamos´ıt´asi m´ odszert nem haszn´ alunk (saj´at kez˝ u sz´alkezel´es, OpenMP, stb.), ´es szeretn´enk minim´ alis er˝ ofesz´ıt´essel jav´ıtani alkalmaz´asaink teljes´ıtm´enyein, akkor a Visual Studio saj´ at direkt´ıv´akon kereszt¨ uli megold´asa j´o v´alaszt´as lehet.
3.7
A C++11 ´ es a mozgat´ o konstruktorok
A C++ leg´ ujabb, sokak ´ altal C++0x-k´ent emlegetett, majd v´eg¨ ul C++11 n´evre keresztelt szabv´ anya tavaly ´erte el hivatalosan a k´esz ´allapot´at. B´ar a Visual Studio a mai napig csak egy r´esz´et t´amogatja a szabv´anynak, van p´ar olyan, a j¨ ov˝ oben tal´ an kulcsfontoss´ag´ uv´a v´al´o szerkezet, amelyet m´ar a Visual Studio 2010-es verzi´ oja is t´ amogatott. Optimaliz´aci´os szempontb´ol ezek k¨oz¨ ul tal´ an a mozgat´ o konstruktor ´es a mozgat´o-hozz´arendel˝o oper´ator a legjelent˝ osebb. Mivel err˝ ol a t´em´ar´ol m´eg relat´ıve kev´es szakirodalom l´etezik, r¨ oviden ismertetj¨ uk a probl´em´at, amelyre ezek az u ´j konstrukci´ok megold´ast ny´ ujtanak. Tekints¨ uk a StringCPP11 tesztest¨ unkben l´etrehozott sz¨ovegkezel˝o oszt´ alyunkat: 1 c l a s s MyString 2 { 3 char∗ s t r ; 4 public : 5 6 MyString ( ) : str ( nullptr ) 7 {} 8 9 MyString ( const char∗ s t r ) : 10 { 11 strcpy ( str , str ) ; 12 }
s t r (new char [ s t r l e n ( s t r ) + 1 ] )
38
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
MyString ( const MyString& mystr ) : ) + 1]) { s t r c p y ( s t r , mystr . s t r ) ; }
s t r (new char [ s t r l e n ( mystr . s t r
MyString& operator=(const char∗ s t r ) { delete s t r ; s t r = new char [ s t r l e n ( s t r ) + 1 ] ; strcpy ( str , str ) ; return ∗ t h i s ; } MyString& operator=(const MyString& mystr ) { i f ( t h i s == &mystr ) return ∗ t h i s ; return operator=(mystr . s t r ) ; } ˜ MyString ( ) { delete s t r ; } }; extern const char∗ p i ; MyString GetMyString ( ) { return MyString ( p i ) ; }
Ezek ut´ an mi p´eld´ aul a k¨ovetkez˝o m´odon megh´ıvjuk a GetMyString f¨ uggv´eny¨ unket: 1 MyString s t r ; 2 // . . . 3 s t r = GetMyString ( ) ;
Ekkor a k¨ ovetkez˝ o met´ odusok fognak lefutni: 1. GetMyString() 2. Ez l´etrehoz egy lok´ alis p´eld´anyt az oszt´alyunkb´ol: MyString(const char*) 3. Ehhez ´ at kell m´ asolnunk a sz¨oveget az oszt´alyunkba: strcpy(...) 4. Miut´ an visszat´ert¨ unk a GetMyString() f¨ uggv´enyb˝ol, ´atm´asoljuk a visszakapott oszt´ alyunkat: MyString::operator=(const MyString&) 5. MyString::operator=(const char*) ´ asoljuk az ideiglenes p´eld´anyunkb´ol a v´eglegesbe a sz¨oveget: 6. Atm´ strcpy(...) 7. V´eg¨ ul megsemmis¨ ul az ideiglenes p´eld´anyunk: MyString()
39
Mint azt ´eszrevehetj¨ uk, az ideiglenes MyString p´eld´anyunk tulajdonk´eppen teljesen feleslegesen j¨ ott l´etre, hiszen ha lenne r´a lehet˝os´eg¨ unk, akkor sokkal jobban j´ arn´ ank vele, hogyha r¨ogt¨on a c´elobjektumunkba tudn´ank m´asolni a sz¨ oveget, de legal´ abb ne kellene k´et teljes ´ert´ek˝ u p´eld´anyt l´etrehoznunk, megdupl´ azva ez´ altal a m´ asoland´o adat mennyis´eg´et. A p´eld´ankban ez 9 KB helyett 18 KB adatot jelent, ami tal´an nem t˝ unhet soknak, de ez a probl´ema rendk´ıv¨ ul sz´elesk¨ or˝ u, el˝ oj¨ on vektorok, list´ak ´es tov´abbi t´arol´ok eset´en is, amelyek komplexebb alkalmaz´asok eset´en rengeteg adatot tartalmazhatnak, amelyek j´ o es´ellyel szint´en k´etszer annyi m´asol´ast fognak eredm´enyezni. Erre a probl´em´ ara ny´ ujt megold´ast a C++11 egyik u ´jdons´aga, az u ´n. mozgat´ asi szemantika (move semantics). [3] Ez a bal-´ert´ekek (lvalue) ´es jobb-´ert´ekek (rvalue) megk¨ ul¨onb¨oztet´es´en alapszik. A bal-´ert´ekek olyan kifejez´esek, amelyeknek a programoz´o a forr´ask´od ´ır´asa sor´an tudja venni a c´ım´et az & (address-of) oper´ atorral. R´egebben a bal-´ert´ek defin´ıci´oja az volt, hogy egy olyan b´ armi, amelyhez hozz´a lehet rendelni, azonban ez a const kulcssz´ o megjelen´es´evel megv´altozott. Ezzel szemben a jobb-´ert´ekek olyan ´ert´ekek, amelyeknek a programoz´o nem tudja el˝ore venni a c´ım´et, azonban a ford´ıt´ o igen. Erre egy egyszer˝ u p´elda a (3 + 4) kifejez´es, amely el´e nem tehet¨ unk ki egy address-of oper´atort, azonban a program fut´asa sor´an m´egis lesz c´ıme, amelyhez a ford´ıt´o hozz´a is tud f´erni, ´es ez´altal k¨ ul¨onb¨oz˝o optimaliz´ aci´ os lehet˝ os´egek ny´ılnak meg el˝ott¨ unk. A jobb-´ert´ekekre gyakran ideiglenes ”v´ altoz´ okk´ent” szoktak tekinteni, ugyanis a legt¨obb esetben a jobb-´ert´ekek szinte azonnal megsemmis¨ ulnek. Erre p´elda az el˝obb bemutatott ideiglenes MyString p´eld´ anyunk, amelyre csak az´ert volt sz¨ uks´eg, hogy a megh´ıvott f¨ uggv´eny ´es a c´elobjektum k¨oz´e egyfajta k¨ozvet´ıt˝oi szerepet bet¨ olts¨ on. Ezekre a jobb-´ert´ekekre hivatkozni is lehet, m´eghozz´a a && haszn´alat´aval, amely tulajdonk´eppen egy referenci´at ny´ ujt nek¨ unk egy jobb-´ert´ekre. Emiatt a ”referencia” sz´ o k´et´ertelm˝ uv´e v´alik, ez´ert megk¨ ul¨onb¨oztet¨ unk bal-referenci´at ´es jobb-referenci´ at. A szeml´eltet´es kedv´e´ert tekints¨ uk az al´abbi p´eld´at, amely szeretn´e a π sz´ amjegyeit feldolgozni valamilyen form´aban: 1 void f ( const MyString& p i ) 2 { 3 // . . . 4 strcpy ( l o c a l b u f f e r , pi ) 5 // . . . 6 // Modify t h e s t r i n g 7 }
Ez a klasszikus v´ altozat. Ebben az esetben k´enytelenek vagyunk lem´asolni az eg´esz sz¨ oveget, annak ellen´ere, hogy lehet, hogy param´eterk´ent egy olyan v´ altoz´ ot kaptunk, ami csak ennek a f¨ uggv´enyh´ıv´asnak a kedv´e´ert j¨ott l´etre. 1 void f ( MyString&& p i ) 2 { 3 // Modify t h e s t r i n g t h r o u g h t h e parameter 4 }
Ha viszont meg´ırjuk ezt a f¨ uggv´enyt is, akkor biztosak lehet¨ unk benne, hogy ha ebben a f¨ uggv´enyben tart´ ozkodunk, akkor a megkapott param´eterre m´ar senkinek nem lesz sz¨ uks´ege, ez´ert an´elk¨ ul hogy azt lem´asoln´ank, nyugodt sz´ıvvel manipul´ alhatjuk a param´etert, hiszen a f¨ uggv´eny¨ unk lefut´asa ut´an u ´gyis meg
40
fog semmis¨ ulni. Az f f¨ uggv´eny megh´ıv´asakor teh´at a param´eter t´ıpusa sokkal nagyobb szerepet kaphat, mint eddig. Amennyiben egy lok´alis v´altoz´ot adunk at neki, akkor a r´egi, j´ ´ ol bev´ alt m´odszer marad, teh´at a fels˝o v´altozatot fogjuk megh´ıvni. Azonban ha p´eld´ aul f(GetMyString())-k´ent h´ıvjuk meg, akkor m´ar egy ideiglenes v´ altoz´ ot adunk ´at param´eterk´ent, ´ıgy az ir´any´ıt´as a m´asodik v´ altozatn´ al fog kik¨ otni. Ezt a m´ odszert a konstruktorok eset´eben is lehet haszn´alni, hiszen sokszor egy ideiglenes v´ altoz´ on kereszt¨ ul hozunk l´etre egy u ´jat, ami szint´en megdupl´azza a sz¨ uks´eges m´ asol´ ast. Mozgat´ o konstruktort a k¨ovetkez˝ok´eppen lehet l´etrehozni: 1 MyString ( MyString&& mystr ) : 2 { 3 mystr . s t r = n u l l p t r ; 4 }
s t r ( mystr . s t r )
Itt az l´ athat´ o, hogy el˝ osz¨ or egyszer˝ uen ´atvessz¨ uk a param´etert˝ol a char* v´ altoz´ oj´ at, amely az eddigi C++ vil´agban meglehet˝osen szokatlan gyakorlat lett volna. Ezek ut´ an r´ aad´ asul a param´eter str v´altoz´oj´at m´eg ki is null´azzuk. Ezt a m˝ uveletet, amikor a m´asik oszt´alyt´ol eg´esz egyszer˝ uen ”elvessz¨ uk” a v´ altoz´ oit, lop´ asnak h´ıvjuk. Azonban n´ezz¨ uk meg, mi´ert is j´o a lop´as. Mivel tudjuk, hogy a param´eterk´ent kapott oszt´aly egy jobb-´ert´ek, ez´ert tudjuk, hogy hamarosan meg fog semmis¨ ulni, senkinek sincs m´ar sz¨ uks´ege r´a. Ez´ert neki sincs sz¨ uks´ege a v´ altoz´ oira! Ha m´ar u ´gyis rendelkezik egy mutat´oval, ami m¨ og¨ ott ott van az eg´esz sz¨ oveg, mi´ert m´asoljuk azt ´at? Eg´esz egyszer˝ uen csak tegy¨ uk r´ a mi a kez¨ unket, hiszen pillanatokon bel¨ ul megh´ıv´asra ker¨ ul a param´eter destruktora ´es csak ”kidobn´ank” egy teljesen c´eljainknak megfelel˝o sz¨ oveget. Ez´ert teh´ at a konstruktor inicializ´al´o list´aban ´atm´asoljuk a mutat´o ´ert´ek´et, majd a konstruktor test´eben kinull´azzuk a m´asik oszt´aly v´altoz´oj´at. Erre az´ert van sz¨ uks´eg, mert ha ott meghagyn´ank az ´ert´eket, akkor a destruktor´ anak megh´ıv´ asakor arra a mutat´ora megh´ıvna egy delete-et, amely t¨ onkretenn´e a sz¨ oveg¨ unket. ´Igy viszont a delete egy nullpointerre lesz megh´ıvva, amely teljesen ´ artalmatlan. Az alap gondolat teh´ at az, hogy miel˝ott neki´alln´ank ´atm´asolni a dolgokat, el˝ osz¨ or pr´ ob´ aljunk meg minden haszn´alhat´o adatot ´atmozgatni a saj´at ir´ any´ıt´ asunk al´ a, majd az ideiglenes v´altoz´ot olyan ´allapotba kell hoznunk, hogy annak megsemmis¨ ul´ese senki sz´am´ara ne okozhasson gondot. Term´eszetesen ha van mozgat´o konstruktor, akkor lennie kell egy u ´j hozz´ arendel´esi oper´ atornak is. Azonban mivel ezt az elnevez´est m´ar haszn´ aljuk, ez´ert itt is bevezet¨ unk k´et u ´j elnevez´est: a m´asol´o-hozz´arendel˝o oper´ atort (copy-assignment operator) ´es a mozgat´o-hozz´arendel˝o oper´atort (move-assignment operator). Ennek kin´ezete a k¨ovetkez˝o: 1 MyString& operator=(MyString&& mystr ) 2 { 3 delete s t r ; 4 s t r = mystr . s t r ; 5 mystr . s t r = n u l l p t r ; 6 return ∗ t h i s ; 7 }
Az els˝ o dolog amit ´erdemes megjegyezni, hogy itt nincs sz¨ uks´eg megvizsg´ alni, hogy ¨ onmagunkhoz rendel¨ unk-e hozz´a. Ennek az a magyar´azata,
41
hogy a kapott param´eter egy jobb-´ert´ek, teh´at nem lehet hozz´arendelni semmit. Azonban mi egy mozgat´o-hozz´arendel˝o oper´atorban vagyunk, ez azt jelenti, hogy a *this egy bal-´ert´ek. Mivel term´eszetesen semmi nem lehet egyszerre bal-´ert´ek ´es jobb-´ert´ek is, ez´ert kiz´art, hogy saj´at magunkat kapjuk meg param´eterk´ent. Ezt lesz´ am´ıtva minden a m´ar megszokott m´odon megy. Felszabad´ıtjuk az eddig haszn´alt er˝oforr´asainkat, ellopjuk a param´eter haszn´ alhat´ o v´ altoz´ oit, biztons´ agosan megsemmis´ıthet˝ov´e tessz¨ uk a param´etert, majd visszat´er¨ unk. Mivel sokszor hasznos, ha a hozz´arendel˝o oper´atoraink ´es m´asol´o/mozgat´o konstruktoraink k¨ oz¨ ul csak a konstruktort ´ırjuk meg, az oper´atorhoz pedig egyszer˝ uen felhaszn´ aljuk a m´ar meg´ırt konstruktort, ez´ert ´erdemes megjegyezni a jobb-referenci´ak egy ´erdekes tulajdons´ag´at. Mivel egy jobb-referenci´ anak van neve, ´es f¨ uggv´enyparam´eterk´ent is megjelenik, ´ıgy term´eszetesen lehet hozz´ arendelni ´es a c´ım´et is venni. Ez azt jelenti, hogy a jobb-referencia ¨ onmag´ aban egy bal-´ert´ek. Emiatt a k¨ovetkez˝o naiv pr´ ob´ alkoz´ as nem fog m˝ uk¨ odni: 1 MyString& operator=(MyString&& mystr ) 2 { 3 delete s t r ; 4 ∗ t h i s = MyString ( mystr ) ; 5 return ∗ t h i s ; 6 }
Ebben az esetben ugyanis a mystr egy bal-´ert´ek, ez´ert a MyString(const MyString&), vagyis a m´ asol´ o konstruktorunk lesz megh´ıvva. Ez teljesen logikus is, hiszen nek¨ unk ebben a f¨ uggv´enyben m´eg sz¨ uks´eg¨ unk lehet a mystr v´altoz´o tartalm´ ara, azonban ha a mozgat´o konstruktorunknak jobb-referenciak´ent lenne atadva, akkor az megsemmis´ıten´e a v´altoz´onkat, amely megakad´alyoz minket a ´ tov´ abbi haszn´ alat´ aban. Emiatt mindig explicite jelezn¨ unk kell, hogy mi jobbreferenciak´ent szeretn´enk ´ atadni egy v´altoz´ot. Erre a legegyszer˝ ubb megold´as a castol´ as: 1 MyString& operator=(MyString&& mystr ) 2 { 3 delete s t r ; 4 ∗ t h i s = MyString ( s t a t i c c a s t <MyString&&>(mystr ) ) ; 5 return ∗ t h i s ; 6 }
Azonban a C++11 standard k¨onyvt´arait kieg´esz´ıtett´ek rengeteg u ´j f¨ uggv´ennyel, amelyek k¨ oz¨ ul az egyik pontosan a v´altoz´ok jobb-referenci´av´a t´etel´e´ert, m´ as n´even mozgat´ as´a´ert felel: 1 2 3 4 5 6 7 8
#include < u t i l i t y > MyString& operator=(MyString&& mystr ) { delete s t r ; ∗ t h i s = MyString ( s t d : : move ( mystr ) ) ; return ∗ t h i s ; }
Ezeket a f¨ uggv´enyeket hozz´aadva az oszt´alyunkhoz a k¨ovetkez˝ok´eppen v´ altozik a megh´ıvott f¨ uggv´enyek list´aja: 42
1. GetMyString() 2. Ez l´etrehoz egy lok´ alis p´eld´anyt az oszt´alyunkb´ol: MyString(const char*) 3. Ehhez ´ at kell m´ asolnunk a sz¨oveget az oszt´alyunkba: strcpy(...) 4. Visszat´ert¨ unk a GetMyString() f¨ uggv´enyb˝ol, azonban egy ideiglenes v´ altoz´ o van a kez¨ unkben, ez´ert a mozgat´o-hozz´arendel˝o oper´atort h´ıvjuk meg: MyString(MyString&&) 5. V´eg¨ ul megsemmis¨ ul az ideiglenes p´eld´anyunk: MyString() Teh´ at megsp´ oroljuk az adatm´asol´as fel´et, amely komplex alkalmaz´asok eset´en ´ ori´ asi el˝ onyt jelenthet. Amint az l´ athat´ o, a mozgat´o konstruktorok ´es mozgat´o-hozz´arendel´esi oper´ atorok meg´ır´ asa tov´ abb tart, mint egy ford´ıt´asi kapcsol´o ´at´all´ıt´asa, azonban a hat´ asai is j´ oval nagyobbak. A tesztjeink k¨oz¨ ul egyed¨ ul a StringCPP11 tartalmazott olyan k´odot, amelyre alkalmazhat´o volt ez a fajta optimaliz´ aci´ o, ennek az eredm´enye a k¨ovetkez˝o lett: Teszteset Id˝ o Id˝ o (C++11) Sebess´ egn¨ oveked´ es StringCPP11 1560 ms 589.70 ms 62.20% 9. t´ abl´ azat - A mozgat´o szemantika hat´asa A t¨ obb mint 60 sz´ azal´ekos teljes´ıtm´enyn¨oveked´es mag´a´ert besz´el, mindenf´elek´eppen ´erdemes implement´alni a mozgat´asi szemantik´at az alkalmaz´ asainkban. Amennyiben ezt nem tudjuk megtenni, de esetleg haszn´ alunk a standard k¨ onyvt´arakb´ol oszt´alyokat, m´ar akkor is ´erdemes lehet u ´jraford´ıtani az alkalmaz´ asainkat, ugyanis a legt¨obb ford´ıt´o m´ar a C++11-re aktualiz´ alt k¨ onyvt´ arakat tartalmazza. Teh´at ha legutolj´ara 2008-ban ford´ıtottuk le a rengeteg vektort haszn´al´o alkalmaz´asunkat, akkor m´ar egy egyszer˝ u u ´jraford´ıt´ ast´ ol is jelent˝osen n˝ohet a teljes´ıtm´enye a szoftver¨ unknek, amennyiben rendelkez¨ unk egy u ´jabb ford´ıt´oval, amely tartalmazza az u ´j k¨ onyvt´ arakat.
3.8
A GCC ford´ıt´ oval el´ ert eredm´ enyek
Noha a mi vizsg´ alatunk a Visual Studi´or´ol sz´olt, tett¨ unk egy r¨ovid o¨sszehasonl´ıt´ ast a GCC ford´ıt´oval is. Itt nem tett¨ unk pr´ob´at a k¨ ul¨onb¨oz˝o kapcsol´ ok egyenk´enti hat´ asainak megfigyel´es´ere, hanem csak ezt az egy, saj´at kez˝ uleg ¨ ossze´ all´ıtott kombin´ aci´ot ford´ıtottuk le: [6] -fabi-version=6 A C++ ABI (Application Binary Interface) verzi´oj´at hat´ arozza meg. A hatos ´ert´ek a leg´ ujabb, C++11-et is t´amogat´o v´ altozat. -fno-rtti Kikapcsolja az RTTI (RunTime Type Information) gener´al´as´at. -fstrict-enums Optimaliz´ aci´ os lehet˝os´eget biztos´ıt a ford´ıt´o sz´am´ara az´altal, hogy felt´etelezi, hogy az eg´eszb˝ol enumer´aci´oba val´o konverzi´o mindig ´erv´enyes (teh´ at mindig l´etez˝o ´ert´eket konvert´alunk). 43
-O3 Az optimaliz´ aci´ o m´ert´ek´et a lehet˝o legmagasabbra ´all´ıtja. -Ofast Enged´elyezi a ford´ıt´ onak, hogy elt´erjen a szabv´anyokt´ol amennyiben az gyorsabb k´ odot eredm´enyez. -funsafe-loop-optimizations Enged´elyezi a ford´ıt´onak, hogy m´eg jobban optimaliz´ alja a ciklusokat az´altal, hogy felteszi, hogy a ciklusfelt´etelek egy id˝ o ut´ an mindig hamisak lesznek (nincs v´egtelen ciklus), ´es hogy a ciklusv´ altoz´ ok sosem csordulnak t´ ul. -fipa-pta Hat´ as´ ara a ford´ıt´o sokkal nagyobb m´ert´ekben vizsg´alja ´at a programunkat optimaliz´ aci´os lehet˝os´eg ut´an kutatva. Ez nagy m´ert´ekben megn¨ ovelheti a ford´ıt´ as er˝oforr´asig´eny´et. -std=c++11 A haszn´ alt C++ szabv´anyt lehet fele be´all´ıtani. -march=core2 Specifik´ alhatjuk, hogy mely processzorcsal´adra szeretn´enk leford´ıtani a k´ odot, amely sz´amos egy´eb optimaliz´aci´os lehet˝os´eget nyit meg. A mi eset¨ unkben nem ´erz´ekelt¨ unk semmi hat´ast. Sajnos a ford´ıt´ as sor´ an nem volt lehet˝os´eg¨ unk 64-bites k´od gener´al´as´ara, ´ıgy a 32-bites v´ altozatokat hasonl´ıtjuk ¨ossze. Az el´ert eredm´enyeink a k¨ovetkez˝oek: Teszteset Id˝ o (Visual Studio) Id˝ o (GCC) Sebess´ egn¨ oveked´ es ArrayCopy 546.19 ms 515.60 ms 5.60% Parallel 995.56 ms 864.40 ms 13.17% Fibonacci 1273 ms 1185 ms 6.91% StringCPP11 1790 ms 1020 ms 43.02% 10. t´ abl´ azat - A GCC ford´ıt´oval el´ert eredm´enyeink
Mint az l´ athat´ o, a Visual Studio alapbe´all´ıt´asai minden esetben alul maradtak az ´ altalunk ¨ ossze´ all´ıtott GCC be´all´ıt´asokkal szemben. A GCC sokkal nagyobb szabads´ agot ad nek¨ unk az optimaliz´aci´os be´all´ıt´asok kezel´ese u ¨gy´eben. B´ ar t´enyleges sebess´egn¨oveked´esr˝ol besz´elhet¨ unk, hasonl´o eredm´enyeket a Visual Studi´oval is el tudtunk ´erni, miut´an saj´at kez˝ uleg beleny´ ultunk a ford´ıt´ asi be´ all´ıt´asokba. Az egyetlen jelent˝os elt´er´est a StringCPP11 eset´en l´ athatjuk, ahol a GCC sokkal gyorsabbnak bizonyult. Ennek h´ atter´eben feltehet˝ oleg az ´all, hogy a GCC RVO (Return Value Optimization) technik´ aja m´ ar alapb´ol is alkalmazta a C++11-es lehet˝os´egeket, m´ıg a Visual Studio ezt mag´ at´ol nem teszi meg.
44
4
¨ Osszefoglal´ as
A dolgozat c´elja az volt, hogy megvizsg´aljuk, lehetne-e m´eg fokozni a ford´ıt´ oink optimaliz´ al´ asi k´epess´egeit az´altal, hogy a manu´alisan be´all´ıthat´o ford´ıt´ asi felt´eteleket kielemezz¨ uk ´es megfelel˝oen alkalmazzuk. Ehhez ´ep´ıtett¨ unk egy tesztrendszert, amely a pontoss´ag kedv´e´ert kernel-m´odb´ol vez´erelve, hardveresen m´erte a programok fut´asi idej´et. B´ ar a tesztjeink csup´ an a val´os alkalmaz´asokban el˝oker¨ ul˝o sz˝ uk keresztmetszeteknek csak egy r´esz´et fedt´ek le, szinte minden esetben tudtunk pozit´ıv eredm´enyeket produk´ alni, m´eg ha azok csak p´ar sz´azal´ekkal is voltak ¨ jobbak az alapbe´ all´ıt´ asokn´ al. Osszegz´ esk´epp kijelenthetj¨ uk, hogy egy kis odafigyel´essel ´es minim´ alis extra munk´aval 5-10 sz´azal´ekos teljes´ıtm´enyn¨ oveked´est ´erhet¨ unk el a ford´ıt´onk k¨ ul¨onb¨oz˝o be´all´ıt´asainak all´ıtgat´ ´ as´ aval. Megvizsg´ altuk a C++ leg´ ujabb szabv´any´at is, amely jelent˝os u ´j´ıt´asokat hozott be a nyelvbe. Ezek egyike a mozgat´asi szemantika, amelyekkel t¨obb mint m´ asf´elszeres´ere siker¨ ult n¨oveln¨ unk az alkalmaz´asunk teljes´ıtm´eny´et. Noha ehhez m´ ar nem el´eg egy-k´et be´all´ıt´ast ´at´all´ıtani, ehhez m´ar u ´j dolgok tanul´ as´ ara ´es gyakorl´ as´ ara van sz¨ uks´eg, ami id˝o- ´es er˝oforr´as-ig´enyes lehet, azonban a hat´ asa szerint¨ unk mag´a´ert besz´el. Mivel a 21. sz´ azadi ´eletnek a sz´am´ıt´og´epek kritikus pontjai, nagyon fontos, hogy min´el hat´ekonyabban tudjunk vel¨ uk dolgozni. Ennek ellen´ere azonban a mai napig rengetegszer kell a sz´am´ıt´og´epre v´arni, ami sokszor blokkolja a ´ munk´ ankat. Ugy gondoljuk, hogy a vizsg´alatunk eredm´enye ezen tud jav´ıtani, amely komolyabb alkalmaz´ asok eset´en ak´ar sok m´asodperces megtakar´ıt´ast is jelenthet.
45
Irodalomjegyz´ ek [1] Walter Oney, Programming the Microsoft Windows Driver Model, 2nd Edition, 2003. [2] Real Time Devices USA, Inc., PCI4520/DM7520/DM7530 User’s Manual Version 2.0, 2003. [3] Scott Meyers, Presentation Materials: Overview of the New C++ (C++11), 2012. [4] Steam Hardware Survey http://store.steampowered.com/hwsurvey/, 2012 szeptember [5] Compiler Switches for Visual Studio 2012 (C++), http://msdn. microsoft.com/en-us/library/9s7c9wdw(v=vs.110).aspx, Microsoft Developer Network, 2012. [6] Compiler Switches for GCC, http://gcc.gnu.org/onlinedocs/gcc/ Option-Summary.html, 2012.
46