Fóti Marcell
Magyar PC Maganzin, 2003. augusztus; 87.-89. oldal

A Windows NT, 2000 és XP memóriakezelése

Sokszor elhangzó kérdés: ha egy hálózatban x számú felhasználó van, y darab megosztott könyvtárban dolgoznak z darab file-lal, akkor mennyi memória kell a Windows NT/2000/2003 kiszolgálóba?

Sajnos a bevezetőben feltett kérdésre a Microsoft modern operációs rendszerei esetén nincs egyértelmű válasz, mert a virtuális memóriakezelés virtualizálja a fogalmakat is. Mit tekinthetünk foglalt memóriának? Mi a szabad memória? Mikor kell lapozni? Ezekre sincs egyértelmű válasz: tiszta Mátrix az egész. Amikor Neo megkérdezi, hogy a valóságot látja-e, Morpheus így válaszol: "What is real?" A valóság megismeréséhez kemény tanuláson kell átesni. Az alábbi cikk nem felületes olvasmány, akinek nem megy elsőre, olvassa át még egyszer!

Mi a virtuális memóriakezelés?

("How do you define real?") Különösen fájó ez a gumivalóság, ha hardverbővítésnél kell okosat mondani, bár napjaink memóriaárai mellett már nem okozhat gondot a szükséges mennyiség kifizetése. Ennek ellenére, úgy gondolom, érdemes megismerkedni a memóriafoglalás rejtelmeivel, mert teljesítmény-tuningoláskor nem árt a tisztánlátás!

calc1
1. Egy alkalmazás kilapozása memóriahiány esetén. A szürke négyzetek a fizikai memóriában vannak - a többi terület nincs ott!

Kezdjük a virtuális memóriakezeléssel (VM): áldás, vagy átok? Sokak számára ez a fogalom az állandóan zörgő merevlemezzel és a lassan vánszorgó rendszerrel azonos, legszívesebben kikapcsolnák - ha lehetne. Mindazok, akik a VM kikapcsolásával próbálkoznak, vasvillával hányják a diót a padlásra. A Windows XP-ben meg lehet szabadulni a lapozófile-tól - de ez nem egyenértékű a VM kikapcsolásával. Aki komolyan gondolja, hogy neki nem kell VM, az telepítsen egy Windows 3.1-et és a WIN.COM-ot mindig /r (real) kapcsolóval indítsa! Valójában a VM sokkal több előnnyel, mint nyűggel jár. Az sem igaz, hogy a VM egyet jelentene a lapozófile zörgetésével, bár ha kevés a RAM, bizony előfordul ilyesmi. A VM alapvetően az Intel (és más gyártók) processzorainak hardveres lehetősége arra, hogy az alkalmazások elől elrejthető legyen a párhuzamosan futó több száz végrehajtási szál egymástól elkülönített memóriaterületeinek borzalmasan bonyolult kezelése és védelme.

A VM a hardveres alaplehetőségre épít, és azt bonyolítja tovább a Windows, amikor kihasználja, hogy ha olyan memóriaterületre bökünk a címtérben, amely mögött nincs fizikai memória, a gép nem lefagy, hanem úgynevezett Page Fault (laphiba) megszakítást kezdeményez. A megszakításkezelő rutin pedig bepótolhatja a hiányzó memóriát, és utasíthatja a processzort, hogy térjen vissza a félbehagyott műveletre. A Windows esetében a memórialapok mérete egységesen 4 Kbyte, ami némi lapozási pazarlással jár, ha csak 3 Kbyte-ot kellene belapozni, de busásan megtérül abban, hogy a lapozófile kezelése sokkal egyszerűbb: nem kell változó hosszúságú blokkoknak helyet keresni, nem kell töredék helyeket egybenöveszteni - egyszóval nincs szükség szemétgyűjtésre (Garbage Collection).

calc1
2. A Task Manager lehetőségei

Először 1997-ben dühödtem be a VM működésének érthetetlenségén. A rendszer megfigyelésekor néhány olyan gondolat merült fel bennem, melyek végül elvezettek a memóriakezelés megismeréséhez. Ezt követően került kezembe az Inside Windows NT egyik kiadása, melyben meglepve olvastam korábbi natív spekulációim igazolását. Az i-re a pontot pedig David Solomon Tech.Ed előadása tette fel: most már tiszta a kép!

Az első apró jelek

("Like a splinter in my mind that drives me mad.") Először gyakorló rendszergazdaként vettem észre, hogy valami nem stimmel a világgal. Volt egy hálózatom több száz olyan munkaállomással, melyeknek merevlemezére az Office egyszerűen nem fért fel. Mit tesz ilyenkor a rendszergazda? Elolvassa a dokumentációt, és felfedezi, a setup.exe/A módszert, mellyel az Office hálózatos telepítésére is lehetőség nyílik. És fut a winword.exe a hálózati megosztásról, mint a kisangyal - de meddig? A kiszolgálók néha-néha lepihennek, ha nagyon fáradtak. Mit gondolnak, milyen közvetlen hatással volt ez a több száz felhasználóra? Döbbenetes módon semmilyen hatással sem! Píri és Rozi zavartalanul csépelte tovább a Wordöt. Ez nem csoda - gondoltam -, hisz a gépek leszedték a hálózati megosztásról az egész winword.exe-t a lapozófile-ba (pagefile.sys). Pár perc múlva azonban szóltam Pirinek, hogy jobb lesz, ha mégis elmenti a művét, mert a kiszolgáló öt perce leállt. És abban a pillanatban, hogy a mentésre kattintott, a Word úgy elszállt, hogy öröm volt nézni. Piri is örült, és ennek egy cifra káromkodással adott hangot. Bár nem értettem a jelenséget, a zavartalanul dolgozó Rozihoz új stratégiát agyaltam ki: neki azt mondtam, tegye vágólapra a művét. És csodák csodája, az akció sikerült, a Word még elbillegett egy darabig, majd ugyanúgy kinyiffant, mint Pirinél, de a doksi megvárt minket a vágólapon. Mi a két eset között a különbség? Egyáltalán miért esett el a Word, ha egyszer bekerült a helyi Windows lapozófile-jába? Hát mert nem került bele!

calc1
3. Mit mond a Performance Monitor a Winword.exe-ről?

Sok-sok kísérletezés után rájöttem arra, amit kapásból tudnom kellett volna: nem az egész winword.exe lapozódik ki, hanem csak az adatszegmensei! A kódszegmenseket felesleges lapozófile-ba tenni, hisz tökéletesen visszaállítható állapotban megtalálhatók valahol máshol: a megosztott könyvtárban a kiszolgálón! A Word addig tud futni, amíg képes pótolni a hiányzó EXE-falatkákat az eredeti file-ból. Tehát a lapozás és a lapozófile csörgése nem sok összefüggést mutat a programok kódszegmenseinek használatával. Ha kevés a memória, a Windows igen hatékonyan "lapozza ki" a kódszegmensek tartalmát: egyszerűen eldobja!

Az 1. kép ízelítőt ad a valóságról, és leolvasható róla a futó winword.exe nemóriadarabkáinak kilapozási útvonala. A kódszegmensek elpárolognak, míg az adatszegmensek és a heap memóriablokkok (fontos.doc) valóban a lapozófile-ba kerülnek.

Egy pár szó a Task Managerről

("All I'm offering is the truth. Nothing more.") Próbáljuk megállapítani az előbb emlegetett alkalmazás, a winword.exe összesített memóriafoglalását! Elsődleges memóriaméricskélő eszközünk a Task Manager (Feladatkezelő), a szokásos, alapszámlálókon kívül további memóriamennyiségek mérésére is képes. A második kép bemutatja, milyen sokféle mennyiséget jeleníthetünk meg akár ezzel az egyszerű eszközzel is. Ha megjelenítjük mind a Memory Usage (Memóriahasználat), mind pedig a Virtual Memory Size (Virtuális memória mérete) oszlopokat, érdekes felfedezést tehetünk: a két számoszlop között nem sok összefüggés mutatkozik, hol az egyik a nagyobb, hol a másik. Az alapnézetben is látható Mem Usage az alkalmazásnak juttatott fizikai memória, míg a VM Size az alkalmazás pagefile.sys-be kilapozott része. Ha pontosan tudni akarjuk az alkalmazás memóriaéhségét, fejben gyorsan összeadjuk a kettőt, és megkapjuk azt a számot - melynek semmi köze semmihez...

calc1
4. Az XP "megtanulja" a programok memóriaigényét

Ez azért van így, mert a korábbi, kilapozós ábra tanúsága szerint a végrehajtható kódrészek (a kódszegmensek) nem kerülnek ki a pagefile.sys-be, hanem "elpárolognak"! Falba ütköztünk: nem vagyunk képesek megmondani egy alkalmazás összesített memóriaigényét - legalábbis a Task Manager nem segít ebben.

Térjünk át a sokkal kifinomultabb Performance Monitorra (Windows 2000-től System Monitor a neve, magyarul pedig Teljesítmény)!

A Performance Monitor segítsége

("You mean I can dodge bullets?") Első lépésként egyeztessük óráinkat, azaz állapítsuk meg, hogy az alábbi, Report nézetben használt Performance Monitor milyen számlálókat mutat a kiválasztott Process objektumon, s ezek vajon megegyeznek-e a Task Manager valamelyik számlálójával (3. kép).

Rövid számológép-sanyargatás után rájövünk, hogy ami a Performance Monitornál Page File Bytes és Working Set, az a Task Managerben WM Size, illetve MEM Usage. Így csak öt, eddig nem említett számláló maradt. Ezek közül három megjeleníthető a Task Managerben, de nem sok segítséget nyújtanak:

Ismeretlenként tehát itt maradt a Private Bytes és a Virtual Bytes. Hátha ezek választ adnak a kérdésre: mennyit fogyaszt a winword.exe?

Hopp! Úgy tűnik, helyben vagyunk! A Virtual Bytes megválaszolja a kérdésünket! Lássuk csak: 140951 552 byte, ami annyi mint 134,42 Mbyte. Uramisten! Csak nem kap ennyit egy nyavalyás Word?

Konklúzió

(" Welcome to the real world!") Az élet szép, de a helyzet szomorú. A Virtual Bytes szépen megmutatja, hogy az adott alkalmazás mennyi memóriát kért, de azt sajnos nem, hogy mennyit kapott. (Ez utóbbi lenne a Committed Bytes vagy Előjegyzett memória - lásd később.) De legalább tetten érhető a virtuális memóriakezelés egyik igen jelentős előnye: az alkalmazások memóriaigénye akkor kerül kiszolgálásra, ha az általuk "lefoglalt" területre valóban szükségük lesz, oda írni vagy onnan olvasni akarnak (Demand Paged Virtual Memory). llyenkor Page Fault (laphiány) megszakítás keletkezik, és az operációs rendszer gyorsan odadob egy adag memóriát, hadd higgye azt a Word, hogy megkapott mindent, amit kért!

calc1
5. Available Bytes és Cache Bytes a Performance Monitorban

Valójában minden processz 0 (nulla) méretű Working Settel (Munkakészlet) indul. Az alkalmazás mintegy "befaultolja", "behibázza" magát a memóriába, mert kezdetben nem kap egy fikarcnyi RAM-ot sem (kivéve az EXE legelejét), és ahogy futni kezd, és csapkod ide-oda, mindannyiszor üres lapot talál, amit a VM kezelő bepótol neki!

Ez a Windows 2000-nél még elég buta módon zajlik, mert - bár minden egyes alkalmazás mindig ugyanúgy indul - az operációs rendszertől nem kapja meg egyből az indulólapokat, hanem az EXE lassan "befaultolódik". Ez magyarázza, hogy például egy vacak kis calc.exe közvetlenül indítás után miért mutat 334 Page Faultot, pedig senki sem bántotta! A Windows XP-n gyorsabban indulnak az alkalmazások, mert a rendszer egy .PF (PreFetch) file-ba feljegyzi az induláskor szükséges lapokat, és innentől kezdve már nem nulláról kell bemásznia szegény programnak. (4. kép)

A lapozásról

("How deep the rabbit hole is?") A Performance Monitor Process objektumának számlálóival tehát nem jutottunk a dolog végére. Nem tudjuk, hogy egy adott pillanatban mennyit fogyaszt egy alkalmazás, sőt azt sem tudjuk pontosan, miért nem tudjuk, amit nem tudunk. Térjünk át most a Memory objektum számlálóira, hátha így közelebb jutunk a működés lényegéhez. Itt van mindjárt az Available Bytes (Rendelkezésre álló memória) és a Cache Bytes (Gyorsítótár jelenlegi mérete) számláló. Ha egy gépet terhelésnek teszünk ki, ez a két számláló gyönyörű szimmetrikus ábrákat rajzol a képernyőre (5. kép).

Látszólag minden lefoglalt byte a cache területre kerül, mintha soha semmi nem kerülne át például a programok hatáskörébe. (Persze átkerül, ha indítgatunk és leállítgatunk programocskákat, de ha csak a meglévőkkel manipulálunk, ez ritkán látszik.) Mi valójában az "Available Bytes"? És mi a "Cache Bytes"?

Kövesd a fehér nyulat!

Az operációs rendszer futása közben állandóan zajlik a lapozás. Ennek oka, hogy minden processznek korlátozott a Working Set mérete, és még rengeteg fizikai memória esetén is előbb-utóbb utoléri a végzet: bizonyos lapokról le kell mondania. Ettől azonban még nem fog zörögni a merevlemez. Ugyanis a Working Setből LRU (Least Recently Used) algoritmussal kilapozott blokkok általában először nem a merevlemezre, hanem előbb az úgynevezett Standby (rendelkezésre állási) memórialistára kerülnek. A Standby területen a memóriablokkok változtatás nélkül kerülnek tárolásra, hátha az LRU algoritmus tévedett, és hamarosan ismét szükség lesz rájuk, így innen a Working Setből kilapozott blokkok mindenféle merevlemez-tekerés nélkül visszalapozhatók. A memóriából memóriába történő lapozást Soft Page Faultnak hívjuk, ellentétben a valóban lapozófile-művelettel járó Hard Page Faulttal.

A Task Manager és a Performance Monitor -> Process objektum nem képes különbséget tenni a Hard Page Fault és a Soft Page Fault között, így azok a számlálók gyakorlatilag használhatatlanok a lapozófile-használat felbecsülésére! Egyedül a Performance Monitor Memory objektuma ad valós képet a Pages/sec számlálóval, mert az csak a Hard Page Faultot méri. A Standby listán kívül további memórialistái is vannak az operációs rendszernek (7. kép).

calc1
6. A Task Manager buta arca. A dupla kurzor a sziámi számlálókat mutatja

A Working Setből a 4 Kbyte-os lapok annak megfelelően szorulnak ki vagy a Standby, vagy a Modified Page (Módosított lapok) listára, hogy tartalmuk módosult-e - azaz kód- vagy adatszegmensről van-e szó. Ha egy alkalmazásból kilépünk, annak összes memórialapja a Free Page (Szabad lapok) listára kerül, felszabadul. Biztonsági okokból erről a listáról kizárólag olyan processz kaphat lapot, aminek amúgy joga lenne az adott lap olvasásához. Mivel azonban a kilapozott blokkban jelszavak, RSA-kulcsok és egyéb érzékeny adatok lehetnek, a lapok még törlésen is átesnek, mielőtt akármelyik processz kaphatna belőlük. Így kerülnek át lenullázva a Zeroed Page (Nullázott lapok) listára. Ha a szabad memória mennyiségére vagyunk kíváncsiak, nehéz helyzetben vagyunk, mert míg a Zeroed Page lista nyilván szabad memória, addig a Free és a Standby így is, úgy is értelmezhető: ha visszalapozzuk eredeti helyére, akkor inkább "cache", ha viszont újra kiadjuk, akkor szabad...

A Task Manager és a Performance Monitor által mutatott Available Bytes (az 5. képen az alsó vonal) valójában a Free, a Standby és a Zeroed listákon lévő memóriablokkok összes területe! Már csak egyetlen dolgot kell megválaszolnunk: mi a Cache Bytes?

Cache Bytes

("There is no spoonl") A Performance Monitor szerint: "A gyorsítótár jelenlegi mérete a rezidens gyorsítótár, a rezidens rendszerillesztő memória, a rezidens rendszerkód és a rezidens lapozható készlet méretének összege." Vagyis mindenféle System vicik-vacak által elfoglalt memóriaterületek összessége! Ennek magyarázata a következő: nincs is olyan memóriatípus a Windowsban, hogy cache, mert a fenti listák gyakorlatilag elvégzik a gyorstárazást. Cache mechanizmus persze van: a Read Ahead (előre beolvasás], Lazy Write ("ráérős kiírás") és a többi jól ismert algoritmus itt is megvan, de nem egy különálló cahce managerben, hanem a VM-memóriakezelés részeként.

A trükk a következő: ha egy alkalmazás beolvas egy nagy file-t, a Read Ahead nem a hívó processz memóriaterébe dobálja az előrefutó olvasás eredményét, hanem az operációs rendszer saját Working Setjébe, hogy ha esetleg rosszul "gondolkodott", és a Read Ahead eredménye mégsem kell az alkalmazásnak, ne kelljen külön eltávolítania az alkalmazás memóriateréből a kéretlen cuccot. E tény ismeretében érthető, hogy a Task Manager által mutatott Cache Bytes mást tartalmaz NT 4 és Windows 2000 esetén - hisz egyiknek sincs semmi köze a valósághoz.

A File Cache tehát: NT 4 esetén valójában a System Working Set mérete (Paged Pool + ntoskrnl.exe, az eszközmeghajtók kód- és adatszegmensei stb.). Semmi köze semmihez! Windows 2000 esetén az NT 4-es zagyvaság plusz a Standby List mérete. A fura az, hogy a Standby List mérete beleszámítódik az Available Bytesba is! Az Available és a Cache tehát sziámi ikrek, melyek a Standby Listnél fogva össze vannak nőve (6. kép).

A Commit Charge (Lefoglalt memória) pedig a lapozófile méretéről ad közelítőleges információt: A Performance Monitor Memory objektumának Committed Bytes számlálója ugyanis azt a mennyiséget mutatja, amennyit a Windows a kért memóriából valóban kiosztott, és melynek számára a lapozófile-ban fészket is rakott, hogy ha majd kilapozódik, ne kelljen helykeresgéléssel bajlódnia. Milyen kár, hogy a Committed Bytes csak a Memory objektumon mérhető, és a processzeken nem!

calc1
7. A Windows memórialistái (Forrás: David Solomon)