A Win32 nem más, mint egy, a 32-bites Windows platformok mindegyikén megtalálható API (Application Programming Interface - Alkalmazás-Programozási Interfész) neve. A Win32 pillanatnyilag a következő operációs rendszereken érhető el:
 

(lásd a keretes cikkeket az egyes platformokról)
 

A Win32 API-nak létezik egy rendkívüli módon leszűkített változata, a Win32s (Win32 subset), amely kiegészítőként telepíthető Win3.1 rendszerek alá. Ez a változat azonban a 32-bites memóriamenedzsmenten és az ehhez feltétlenül szükséges egyéb kapcsolódó rutinokon kívül szinte semmilyen egyéb, a Win32-ben fellelhető szolgáltatást nem tartalmaz, ezért azt itt most nem is tárgyaljuk.

A Win32 API egy komplex, rendkívül sokrétű szolgáltatásokat nyújtó interfészt definiál az alkalmazás-programozók számára. Célja egy olyan egységes programozói felület biztosítása a Windows platformok felett, ami lehetővé teszi az alkalmazások számára a mindenkori tényleges futtató környezet (operációs rendszer) funkcióinak és szolgáltatásainak azonos módon történő elérését, attól függetlenül, hogy adott esetben azok a különböző platformokon teljesen eltérő módon kerültek implementálásra.
Az általánosságban egységes működés ellenére számos API funkció nem, vagy csak korlátozott mértékben használható bizonyos platformokon. Ezek a korlátozások fakadhatnak magából az operációs rendszer architektúrájából, ill. adódhatnak a futtató környezet és/vagy a hardver korlátjaiból is.

Általánosságban elmondható, hogy a Win32 API-t legteljesebb mértékben a Windows NT családba tartozó operációs rendszerek támogatják, míg az egyéb változatok néhány vagy több funkciót, funkciócsoportot nem implementálnak. Ugyanakkor számos olyan Win32 API függvény is létezik, amelyikek mindegyike egy-egy "gyengébb testvér" speciális szolgáltatásait aknázza ki, és így pont NT alatt nem érhető el.
Például az ún. service funkciók kizárólag Windows NT és Windows 2000 platformokon érhetőek el, míg az érintőképernyők kezeléséhez használható API függvények csakis Windows CE platformon állnak az alkalmazások rendelkezésére. A GDI funkciók legnagyobb része mind Windows 9X, mind NT platformon elérhető, azonban előbbiek esetében az alkalmazható koordinátarendszerek kiterjedése, és a használható erőforrások száma erősen korlátozott - utóbbi platformokhoz képest.

Az Win32 API tárgyalásához feltétlenül szükséges a futtató operációs rendszerek alapvető architektúrájának, szolgáltatásainak és mechanizmusainak megértése. A következőkben ezekkel ismerkedünk meg közelebbről.
 

A multitaszking alapjai

Folyamatok és szálak

Minden Win32 alkalmazás egy vagy több folyamatból vagy más néven processzből áll. A Win32 terminológiában a processz egy futattás alatt álló programot takar, amely külön egyedi címterülettel (address space) rendelkezik, és ami kódból, adatból valamint egyéb - az operációs rendszer által biztosított és kezelt - erőforrásokból (fájlok, szinkronizációs és interprocessz-kommunikációs objektumok, stb.) áll. Egy program minden egyes futó példánya külön processzt alkot a saját szálaival, címterületével és erőforrásaival. Minden processz külön bázisprioritással rendelkezik.
Minden processz tartalmaz legalább egy szálat (thread) is. A szál a Win32-ben azt a legkisebb futtatási egységet jelenti, amely számára az operációs rendszer CPU időt biztosít. A szál a processz kódjának bármely részét futtathatja, beleértve az esetlegesen másik szál(ak) által már éppen futtatott területeket is. Új processz indításakor az operációs rendszer automatikusan létrehoz egy futtató szálat - ez az elsődleges szál (primary thread). (Ezt az automatikusan létrehozott első szálat szokás főszálnak is nevezni, amely azonban sokszor téves terminológiát takar. Ez a szál kizárólag létrejöttének módjában különül el a többitől, de önmagában - a benne futtatott kódból adódó funkciójától elvonatkoztatva - semmilyen kitüntetett szereppel nem bír.) Az elsődleges szálból kiindulva a folyamat tetszőlegesen sok további szálat hozhat létre futása során, melyek mindegyike a többitől függetlenül megállítható, újraindítható vagy megszakítható, és amelyek mindegyike akár újabb szálakat is indíthat. A processz minden szála azonos címterületen osztozik, valamint mindegyikük teljes hozzáféréssel bír a folyamat globális változóihoz és társított erőforrásaihoz. Ugyanakkor minden szál egyedi veremmel rendelkezik, ami biztosítja a párhuzamosan futó szálak számára az azonos kódrészek eltérő paraméterekkel történő futtatásának lehetőségét. Szintén szálanként külön-külön tartja nyilván az operációs rendszer a kivétel-kezelőket (exception handling), az ütemezési prioritást (scheduling priority) és a szál mindenkor aktuális állapotát leíró speciális struktúrákat, amelyek az ún. szálkontextust (thread context) alkotják. A gazda-processz bázisprioritása és a szál ütemezési prioritása együttesen határozzák meg a szál effektív ütemezési prioritását.
 

Ütemezés

A Win32 platformok mindegyike pre-emptív ütemezési rendszerrel rendelkezik, amelyben az operációs rendszer a rendelkezésre álló processzoridőt felosztja az azért versengő szálak között. Az ütemező minden egyes futtatási ciklus után újraütemezi az aktív szálakat. Az újraütemezés során az ütemező sorrendet állít fel a versengő szálak között azok effektív prioritása, illetve az utolsó futtatás óta eltelt idő alapján. A mindenkori, a sorrendben a legelső szál kapja meg a vezérlést a következő időszelet tartamáig. Amennyiben a szál az adott idő lejártáig nem adja vissza a vezérlést az operációs rendszernek, úgy ez megszakítja annak futását, mely után ismét újraütemezi a szálakat. A várakozó állapotban lévő szálaknak sosem oszt ki időszeletet az ütemező.
Az időszeletek mérete rendszerenként és processzoronként különbözik, de mivel alapvetően viszonylag kicsik (kb. 10-20ms), ezért a rendszer működése során a felhasználóban azt az érzetet kelti, mintha egyszerre több szál párhuzamosan dolgozna. Valójában egyprocesszoros rendszerekben minden időpillanatban csakis egyetlen szál áll futtatás alatt. Többprocesszoros rendszereken az NT családba tartozó platformok valódi párhuzamos futtatásra is képesek, amelyben minden egyes processzor - egymástól teljesen függetlenül - egy-egy külön szálat futtat.

Érdemes megjegyezni, hogy a többfeladatos futtatásnak létezik egy másik formája, az ún. nem-preemptív vagy kooperatív multitaszking is. Ennek elsődleges ismertetője, hogy egy adott feladat számára rendelkezésre álló CPU időt nem az operációs rendszer, hanem maga a feladat határozza meg. Ez a tulajdonság annak köszönhető, hogy a feladatok futását az operációs rendszer nem felügyeli, hanem a futtatás során szó szerint átadja a vezérlést azok számára. Így a feladatok újraütemezésére csakis akkor kerülhet újból sor, amikor az éppen futtatás alatt álló feladat visszaadja a vezérlést az operációs rendszernek. Ez a működési mechanizmus alapvetően kevéssé reszponzív alkalmazásokhoz és alapjaiban instabil rendszerhez vezet. (Kooperatív multitaszkingot alkalmaz például a Windows 3.1 operációs rendszer.)
 

A memória szervezése

A Win32 API számos a memória-menedzsmenthez kapcsolódó interfészt és funkcióit is tartalmaz, amik alapvetően függetlenné teszik az alkalmazásokat a konkrét architektúrától, és ennek megfelelően nem követelik meg az alkalmazásprogramozó részéről annak részletes ismeretét. Ugyanakkor a rendszer belső működésének, lehetőségeinek és korlátjainak megértéséhez feltétlenül szükséges az alkalmazott memóriamodell ismerete is.

Az alkalmazott memóriamodell platformonként meglehetősen eltérő is lehet, így általánosságban nem tárgyalható. Például magának a Windows NT-nek az Alpha ill. x86 processzorokon futó változatai is teljesen más memóriaszervezéssel dolgoznak, de az Intel processzorokon futó Windows CE változat is jelentős architektúrális eltéréseket mutat az azonos gyártótól származó processzorokhoz készült Windows9X családban alkalmazott modellhez képest. A következőkben mi kizárólag a Windows 9X és az NT család, az Intel processzorokon futó változatainak memória-szervezését tárgyaljuk.

Mint arról már korábban szó volt, Win32 alatt minden processz saját, védett, 32-bites címterülettel rendelkezik, melynek következtében lehetetlenné válik számára egy másik folyamathoz tartozó adatterület tartalmának közvetlen módon történő olvasása, véletlen vagy szándékos módosítása. A teljes 4GB-os címterület alapvetően két részre van osztva: az alsó 2GB-os területen helyezkedik el maga az alkalmazás kódja és adatai, míg a felső 2GB az operációs rendszer számára van fenntartva. Míg az alsó ún. felhasználói területhez a processz teljes hozzáféréssel bír, addig a felső ún. rendszerterületet (system space v. kernel space) kizárólag maga az operációs rendszer magja (kernel) érheti el.

Természetesen egy tipikus alkalmazás általában nem használja ki a teljes, rendelkezésére álló címterületet adatainak és kódjának tárolására, hanem mindig csak annak egy töredékét hasznosítja. Ugyanakkor viszont a rendszerben futtatott alkalmazások összessége általában jóval több memóriát igényel, mint amennyi a mai tipikus konfigurációkban megtalálható. A problémát a rendszer a virtuális memória alkalmazásával oldja meg, melynek lényege, hogy az operációs rendszer a lemezes egységek egy részét is memóriaként hasznosítja. A lemezes egységeken e célra felhasznált területet szokás lapozófájlnak (paging file) hívni.

A processz szálai a memóriához történő hozzáférés során mindig ún. virtális címekkel dolgoznak, amelyek nem az adott objektum a fizikai memóriában elfoglalt helyét, hanem csak a processz címterületén belüli pozícióját írják le. A virtuális-fizikai címek konverzióját a hardver (a CPU) az operációs rendszer által biztosított ún. laptáblázatok segítségével végzi el. Mivel a processzek által kezelt virtuális címek függetlenek az adatok tényleges, fizikai memóriában elfoglalt helyétől, így az operációs rendszer az alkalmazások számára észrevétlenül mozgathatja az adatokat az operatív memórián belül (pl. a memória defragmentálásához), vagy éppen az operatív memória és lapozófájl között, mindössze a megfelelő laptáblázat-bejegyzések karbantartásával.

A processzor és az I/O eszközök kizárólag az operatív (fizikai) memóriában elhelyezkedő adatokkal tudnak dolgozni. Ha a rendelkezésre álló fizikai memória már betelt, de az operációs rendszernek újabb területre van szüksége, akkor egyszerűen megkeresi a fizikai memóriában elhelyezkedő blokkok közül a legrégebben használtakat és azok tartalmát a lapozófájlba írja ki, helyüket pedig felszabadítja az új memóriablokk számára. Amennyiben egy processz vagy eszköz az előzőleg lapozófájlba mentett memóriablokkok tartalmához újból hozzá szeretne férni, az operációs rendszer az előbb ismertetett módon "helyet csinál" a blokknak, majd tartalmát visszatölti a lapozófájlból az operatív memóriába. Ilyen módon az operációs rendszer a memóriaszegmensek "ügyes" cserélgetésével el tudja érni, hogy a mindenkori, éppen szükséges memóriatartalom mindig rendelkezésére álljon, míg a pillanatnyilag nem használt adatok - átmenetileg - a lemezes egységen kerüljenek tárolásra.
 

Szinkronizáció

A Win32 API számos különböző megoldást kínál a szálak szinkronizációjához. Az erőforrásokhoz történő hozzáférés szinkronizálása alapvetően két dologra épül: a szinkronizációs objektumokra és a várakozó funkciókra.
Minden szinkronizációs objektumnak alapvetően két állapota van: jelzett és nem-jelzett. A várakozó funkciók lehetővé teszik szál számára a futás felfüggesztését addig, amíg egy vagy több szinkronizációs objektum egyike vagy mindegyike jelzett állapotba nem kerül, vagy amíg a várakozásban megadott időtartam el nem telik. A feltételek teljesüléséig az adott szál várakozó állapotba kerül, ami azt jelenti, hogy nem kerül ütemezésre, tehát nem használ CPU időt a futáshoz. A Ez a Win32 aszinkron ki-/beviteli funkcióival kombinálva rendkívül effektív adatátvitelt tesz lehetővé. A különböző szinkronizációs objektumok abban térnek el egymástól, hogy milyen események következtében kerülnek jelzett állapotba.

Az esemény (event) objektumok állapota explicit módon, közvetlenül állítható. Működési mechanizmus alapján két eltérő típusú esemény objekumot különböztethetünk meg. A kézi törlésű események (manual-reset events) addig maradnak jelzett állapotban, míg explicit módon törlésre nem kerülnek egy szál által. Ennek megfelelően egy esemény jelzett állapotba kerülése akár több szál feloldását is maga után vonhatja, amennyiben azok mindegyik ezt az objekumot (is) jelölte meg várakozó funkciójában. Ezzel szemben az automatikusan törlődő események (auto-reset events) az első, az objektumra várakozó szál feloldása után nem-jelzett állapotba kerülnek és így egy állapotváltás alkalmával kizárólag egyetlen szál feldoldására alkalmasak. Amennyiben a jelzett állapotba kerüléskor nem volt erre az objektumra várakozó szál, úgy az a következő rá vonatkozó várakozó funkcióig jelzett állapotban marad.

Az eseményekkel szemben a mutex objektumok automatikusan váltanak állapotot. A mutex objektum állapota akkor jelzett, amikor egyetlen szál sem birtokolja azt és akkor nem-jelzett, amikor ez a feltétel nem teljesül. Egy mutexet egyszerre mindig csakis egyetlen szár birtokolhat, ami kiválóan alkalmassá teszi az erőforrásokhoz történő, egymást kizáró hozzáférések koordinálására. Egy mutex objektumra várakozó szál a feloldás után automatikusan a mutex birtokosává válik, ami egyben nem-jelzett állapotúra váltja az objektumot. A feldolgozás befejezése után a mutex elengedésével a birtokló szál lehetővé teszi más, az objektumra esetlegesen várakozó szálak számára a mutex tulajdonlásának megszerzését. Az objektumra várakozó szálak FIFO-sorban kerülnek elhelyezésre (majd kiszolgálásra) ami biztosítja, hogy mindegyikük kiszolgálásra kerüljön, függetlenül prioritásuktól.

A szemafor (semaphore) objektumok egy 0 és egy előre meghatározott maximum közti egész érték tárolására alkalmasak. A szemafor inicializálásakor a maximum értéket veszi fel, amelyet a rendszer minden alkalommal csökkent egyel, amikor egy szál feloldásra kerül az objektumra történő várakozásból, és minden alkalommal növel, amikor - az előzőleg az objektumra sikeresen várakozott - szál elengedi a szemafort. A szemafor jelzett állapotban van, ha kapcsolódó értéke nagyobb, mint 0 és nem-jelzett állapotban, ha ez a feltétel nem teljesül. Ez azt jelenti, hogy ha a szemaforhoz kapcsolódó érték elérte a 0-t, akkor az összes további az objektumra történő várakozás blokkolja az azt alkalmazó szál futását egészén addig, amíg egy, a szemafor jelzett állapotára már sikeresen várakozott szál el nem engedi azt (és így a szemafor értéke a növelés következtében 0 fölé megy). Ennek megfelelően a szemafor objektumok - a mutexekhez hasonlóan - egy adott erőforráshoz történő párhuzamos hozzáférések számának limitálására használhatók, azonban előbbiekkel szemben a párhuzamos hozzáférések száma esetükben szabadon meghatározható. Egy szál egymás után akár többször is sikeresen várakozhat ugyanarra a szemafor objektumra - annak értékétől függően -, de ez esetben természetesen minden egyes sikeres várakozáshoz társulnia kell egy szemafor-elengedésnek is
A kicsit furcsa nevű várható időzítők (waitable timers) olyan objektumok, melyek állapota nem-jelzettről jelzettre vált egy adott idő eltelte után. Az időzítőknek alapvetően két típusuk létezik: a kézi és az automatikus törlésűek. Ezek működésében jelentkező különbség az eseményeknél már megismertekkel teljesen egyezik. Mindkét altípus további tulajdonsága lehet periodikussága. A nem-periodikus időzítők mindig csakis egyszer kerülnek jelzett állapotba, ami után automatikusan deaktiválódnak.

(folytatjuk)


Windows CE Windows NT
A Windows CE egy, elsősorban beágyazott rendszerek (embedded systems) számára kialakított, teljes értékű 32-bites operációs rendszer. A közhiedelemmel ellentétben a CE nem egy már létező operációs rendszer (pl. Win95) "lebutított" változata, hanem egy, az alapoktól újraírt, bizonyos aspektusaiban teljesen egyedi architektúrával rendelkező platform. Bár a rendszer egészét tekintve rendkívül sok hasonlóságot mutat a többi Windows platformmal, azonban speciális célterületének megfelelően bizonyos pontokon teljesen szakít a "hagyományokkal". A többi Windows operációs rendszerhez hasonlóan az alkalmazások futását egy teljesen pre-emptív környezetben, szeparált memóriaterületeken biztosítja. Az NT családba tartozó platformokhoz hasonló módon a karakterláncokat ún. Unicode formátumban tárolja, ami biztosítja az alkalmazások legkülönbözőbb lokalizált (honosított) verzióinak létrehozását. Ugyanakkor az egész operációs rendszer hihetetlen kompakt és rendkívül nagyfokú modularitással bír, ami lehetővé teszi az adott feladathoz szükségtelen komponensek teljes körű eliminálását a rendszerből. A fejlesztők az adott futtató (hardver-)környezet kezeléséhez és csak a konkrét feladat ellátásához feltétlenül szükséges modulok összeválogatásával teljesen egyedi Windows CE változatokat alakíthatnak ki, amelynek köszönhetően a komplett futtató környezet mérete mindössze 200 KB (!) körüli méretre is visszaszorítható.

Windows CE számára kialakított alkészlet a teljes Win32 API funkcióinak mindössze felét tartalmazza, amely azonban az opcionális modulok kiiktatásával tovább szűkíthető. Ugyanakkor a Win32 for Windows CE API számos, más Windows platformokon nem található szolgáltatást is biztosít. Ezek között található például a rendszerszintű esemény-kezelés lehetősége, valamint az érintőképernyők (touch screen) illesztése is.

A Windows NT család tagjai a Microsoft stratégiai platformjai, melyek mindegyikét skálázhatóság, megbízhatóság (stabilitás), nagyfokú biztonság és hibatűrő működés jellemzi.
Az NT technológia lényegét az ún. módosított mikrokernel és a kliens-szerver architektúra kiterjedt alkalmazása szolgáltatja. Ebben a felállásban az operációs rendszer magja (a kernel) csakis a legszükségesebb és legalapvetőbb funkciókat (objektumok kezelése, hozzáférési jogosultságok ellenőrzése, memória-menedzsment) látja el, és a feldolgozás legnagyobb részét a felhasználói módban futó kliens modulokra bízza. Így kernel módban csak viszonylag kis mennyiségű ám rendkívüli alaposan tesztelt kód fut. Ennek több előnye is van. Az egyik, hogy a módok közti váltások száma minimalizálódik, ami önmagában jelentős sebességbeli többletet jelent a más megoldásokat alkalmazó architektúrákkal szemben. Ráadásul a felhasználói módban bekövetkező kivételek vagy esetleges rosszszándékú "akciók" a rendszer egyéb részeinek integritását nem veszélyeztetik, ami rendkívül stabil működéshez vezet.
Mivel az e családba tartozó platformokon a biztonsági ellenőrzés magába az operációs rendszer magjába van beépítve, így - egyéb biztonsági rés hiányában - lehetetlen azt megkerülni, ami tovább növeli a rendszer megbízhatóságát. Ráadásul az NT rendszerek szerves részeit képezik a különböző hibatűrő technológiák (mint pl. az NTFS fájlrendszer vagy a különböző szintű RAID támogatás), ami ideális operációs rendszerekké teszi őket szerverek és egyéb, folyamatos működés szempontjából kritikus gépek számára.

Az összes Win32 platform közül az NT család tagjai tartalmazzák a legteljesebb Win32 API implementációt. Ez nem véletlen, hiszen a Win32-t eredetileg valójában a Windows NT 3.1 natív interfészeként fejlesztették ki és az csak később került implementálásra más Windows változatokban (pl. Windows CE/95/98) is.

Windows 95/98
A Windows 9X család tagjai pillanatnyilag egyértelműen a legelterjedtebb Win32 platformok. Elsődleges céljuk a DOS és az NT technológia közti kapocs, a problémamentes átmenet biztosítása. Tervezésük során a biztonság és a megbízhatóság mint szempontok egyértelműen a kompatibilitás mögé szorultak.
A Windows 2000 megjelenéséig egyértelműen ezek a platformok voltak a legcsábítóbbak az átlagfelhasználók számára, hiszen ezeken kerültek a legújabb technológiák először bemutatásra ill. néhány speciális, ám annál nagyobb jelentőségű újítás kizárólag ezeken volt elérhető (FAT32 fájlrendszer, teljes DirectX támogatás, stb.).
A rendszerek magját a VMM (Virtual Machine Manager) szolgáltatja, ami önmagában is egy teljes, 32-bites operációs rendszer, és amelynek "tetején" fut a Win32 API-t implementáló alrendszer. A VMM feladata a memória, a processzek, a megszakítások és a kivételek kezelése ill. a többszálú, preemptív multitaszking biztosítása. A VMM a hardverrel 32-bites eszközmeghajtókon keresztül kommunikál, de ugyanakkor lehetőséget biztosít 16-bites és valós módú kód futtatására is. A VMM önmaga nem reentráns kódból áll, ami nehézkessé teszi az eszközmeghajtók írását és ront a rendszer összteljesítményén.
Belső architektúrájukat tekintve a család tagjainak további közös jellemzője, hogy lényeges részeik még mindig 16-bites komponenseket használnak, vagy esetenként azokra nyúlnak vissza. Ilyen 16-bites komponens például a rendszer grafikus motorját szolgáltató GDI, illetve a felhasználó interfész menedzselését végző USER modul. Mivel ezek a 16-bites modulok nem reentránsak, így futtatásuk során az operációs rendszer "kikapcsolja" a preemptív multitaszkot, ami növekvő válaszidőkhöz és a rendszer általános instabilitásához vezet.

Az ezeken a platformokon elérhető Win32 implementációk számos korlátozással rendelkeznek. A biztonsági ill. az eseménynapló funkciókat nem támogatják, de ezek helyén ún. "buta" (stub) funkciókat tartalmaznak, amik lehetővé teszik a legtöbb, a teljes Win32 API-re épülő alkalmazás számára a futását ezeken a platformokon is. A szolgáltatások (service) futtatását egyáltalán nem támogatják, illetve teljesen egyedi mechanizmust alkalmaznak hasonló jellegű funkcionalitás megvalósításához.
A többi Windows platformmal ellentétben e család tagjai, bár számos Unicode paraméterekkel dolgozó API hívást támogatnak, de valójában a karakterfüzéreket belsőleg nem Unicode formátumban tárolják.
A rendszer 16-bites komponensei kihatással vannak az API funkciók képességeire is. Ennek megfelelően a számos struktúra belső ábrázolási formájában nem haladhatja meg a 64KB-os méretet, valamint a grafikus megjelenítésben alkalmazott különböző koordinátarendszerek sem léphetik túl a 32,784x32,784-es kiterjedést. A rendszerben számos kulcsfontosságú komponens nem re-entráns (pl. a grafikus motor, a GDI is ilyen), ami erősen rontja a rendszer válaszidejét és az aszinkron folyamatok effektivitását. A rendszer ugyancsak viszonylag szűk számú erőforrással képes dolgozni (ált. 16,384 darabot tud kezelni fajtánként), ami sokszor jelentős problémát jelenthet komolyabb alkalmazások fejlesztése esetén.