Általánosan elterjedt tévhit, hogy a DOS csak 640Kb memória kezelésére képes. Az igazság az, hogy maga a DOS akár 1MB összefüggő memória kezelésére is képes lenne és a 640Kbájtos felső korlátot igazából a PC-k memóriakiosztása határozza meg azzal, hogy az A0000h fizikai címtől a grafikus videó-memória helyét definiálja. Mivel a DOS nem rendelkezik igazi memória-menedzsmenttel és így csak egyetlen egybefüggő memóriaterületet tud kezelni, nem képes az amúgy nagyrészt kihasználatlanul maradó, a bővítő ROM-ok számára fenntartott felső memória blokkok (UMB) kihasználására sem. Ez a terület RAM-lemezként ill. DOS 5.0-tól a HIMEM.SYS használatával mintegy a felső memória területhez hozzácsapva hasznosítható.

A DOS a felhasznált és a még rendelkezésre álló (szabad) memóriaterületeket az ún. MCB-k (Memory Control Block - memória-kontroll blokk) segítségével tartja nyilván, melyeket láncolt listába szervez. Az MCB-k felépítése a következő:
 

MCB
Offset
Méret
Leírás
 
00h
1
típus ('Z' - a lista utolsó blokkja ; 'M' - közbenső v. első blokk)
 
01h
2
a memóriablokk tulajdonosának PSP-címe (paragrafusban)
 
03h
2
a memóriablokk mérete paragrafusokban (n)
 
05h
3
fenntartva
 
08h
8
DOS 4.0-tól tulajdonos neve (ASCIIZ), egyébként fenntartva
 
10h
n*16
a memória blokk maga

*a DOS MCB-je hivatalosan nem dokumentált (undocumented) így használata csak szigorú verzió-szám vizsgálat mellett ajánlott, hiszen későbbi verziókban változhat (megjegyzem: én Win95 alatt, de még WinNT 3.51 DOS-boxban is kipróbáltam és még ott is ment...)

A DOS a teljes (640Kb) memóriát ilyen, maximálisan egy szegmensnyi, azaz 64Kbájtos blokkokra osztja fel. A memóriablokkok mindig teljes paragrafusokat foglalnak el, így méretük mindig 16-tal osztható. Memória-foglaláskor keres egy, a kért memóriaterületnél nem kisebb (gy.k. nagyobb vagy ugyanakkora) szabad blokkot. Ha a blokk pont a kért méretű, akkor egyszerűen csak beállítja a 01h offszeten kezdődő mutatót, hogy a programunkra mutasson, egyébként pedig a kérdéses - eddig szabad - blokkot két másik blokkra osztja melyek közül az egyiket a rendelkezésünkre bocsátja, míg a másikat szabad blokként veszi fel a láncolt listába. Amennyiben nem talál a kérésnek megfelelő méretű összefüggő szabad memóriablokkot úgy hibakóddal, egyébként pedig a lefoglal terület (tehát nem az azt megelőző MCB) első bájtjának szegmenscímével tér vissza. A memóriablokkok felszabadításakor az esetlegesen - újra - egybefüggővé vált területeket egyetlen memóriablokkba kapcsolja össze. Bár a memóriablokkok inkonzekvens felszabadításával (tehát pl. A és B, ABC sorrendben lefoglalt blokkok közül B-t felszabadítjuk, de A-t nem) a memória erősen fragmentálható, valójában igazából csak futás közben újabb memóriát foglaló TSR-ek okozhatnak töredezést. Ekkor előállhat az, az egyébként nehezen felismerhető helyzet, amikor - bár a szabad memória mérete jóval meghaladja a foglalni kívánt blokk méretét - az operációs rendszer (DOS) nem képes memóriát az alkalmazás részére bocsátani, mert a szabad terület nem egybefüggően, hanem olyan darabokban helyezkedik el, melyek közül egyik sem elegendően nagy méretű ahhoz, hogy a kérést teljesíteni lehessen. (Erre a problémára igazából az lenne a megoldás, ha fizikai cím helyett a blokkokat közvetve, handle-kkel azonosíthatnánk, ekkor ugyanis a fizikai memóriában szabadon mozgathatók, átrendezhetők lehetnének az operációs rendszer számára, így újra folytonossá lehetne tenni őket még az előbbi eset fellépése esetén is. Sajnos a DOS-t nem igazán ezen irányvonalak szerint tervezték.)

A láncolt lista első (DOS 5.0+ esetén első két) blokkja speciális jelentőséggel bír, ugyanis ez(ek) tartalmazzá(k) a DOS belső munkaterületeit (és bővítő kódszegmenseit). Ez a memóriablokk újabb speciális ún. adatszegmens-blokkokra van osztva, melyekben a DOS a nyitott fájlok adatait (FILES), vermeket (STACKS), FCB-ket (FCBS) vagy éppen az IFS-eket (Installable File System - installálható fájl-rendszer, pl. CDFS - a CD-k lemezformátumát a DOS számára olvashatóvá tevő FS) tárolja. Ezen munkaterületek egy részének méretét magunk határozhatjuk a CONFIG.SYS-ben (lásd az adatterületek utáni zárójelben a változóneveket, pl. FILES=30).

Minden memóriablokk első bájtjának 'M'-nek vagy 'Z'-nek kell (!) lennie. Ha DOS - egy "eltévedt" programnak köszönhetően (lásd még védett mód előnyei) - más bájtot talál itt a lista vizsgálata közben, akkor 'Memory control blocks destroyed' üzenettel leáll (Ž RESET). A blokkok a memóriában a listában elfoglalt helyüknek megfelelően helyezkednek el és mindig kitöltik a teljes (max. 640Kb) rendelkezésre álló memóriát. A láncolt listában mindig csak egy 'Z' jelű blokk van, ugyanis a DOS evvel jelzi, hogy ez az utolsó blokk. Ha a tulajdonos PSP-cime (lásd később) 0000h, akkor a memóriablokk szabad, ha 0008h akkor a DOS egyébként pedig a megadott program birtokolja.

Az lánc első elemének címét egy szintén dokumentálatlan DOS funkcióhívás (AH=52h) segítségével tudhatjuk meg. A listában a következő memóriablokk szegmenscímét mindig úgy kapjuk meg, ha az aktuális kezdőcímhez hozzáadjuk az MCB-ben megadott blokk-méretet plusz 1-et (maga az MCB méretét) (figyelem paragrafusokról és nem bájtokról van szó!). 

A DOS futtatható állományai

A DOS alapvetően két fajta futtatható állományt ismer: a .COM és az .EXE fájlokat. (A batch fájlok nem igazi futtatható fájlok ; ezek inkább a forráskódokhoz hasonlíthatóak, amelyeket egy interpreter - itt a COMMAND.COM - dolgoz fel, azaz "futtat".) Bár a memóriába kerülve már mindkét fájl azonos módon jelenik meg, jelentős eltérés mutatkozik fizikai szerkezetükben és lehetőségeikben.

A .COM fájlok alapvetően egy-szegmensű (max. 64Kb-os) állományok, melyek belső rutinjaikat tekintve kizárólagosan 16-bites (szegmensen belüli) címzést alkalmaznak. Esetükben a kód-, valamint verem- és adatszegmens - ha csak nem foglalnak plusz memóriát a DOS rutinajai segítségével - ugyanabban a fizikai szegmensben helyezkedik el. Ennek köszönhetően meglehetősen rugalmasak, hiszen bármelyik fizikai szegmensbe betöltve közvetlenül futtathatóak. Ugyanakkor ez a cimzési megszorítás okozza hátrányukat: a maximum 64Kbájtos méretbeli megszorítást is. Gondoljunk csak bele, ha 32-bites címeket is alkalmaz(hat)nának, akkor pontosan tudniuk kellene melyik szegmensben is vannak (ha csak nem lineárisan, azaz az első utasítástól az utolsóig fizikailag egymást szorosan követő címeken futnak ; ilyen program gyakorlatilag nincs) pl. egy másik (nem az éppen aktuális) kódszegmensben lévő rutin meghívásához. Ezt a szegmenscímet azonban már fordítási időben tudni kellene, ami a program csak egyetlen - a fordításkor meghatározott - szegmensbe való betölthetőségét tenné lehetővé. (Itt kell megjegyeznem, hogy ugyan léteznek ún. multi-szegmens .COM fájlok, de ezek valójában az overlay technika kicsit átalakított változatát alkalmazzák és nem ténylegesen több fizikai kód-szegmenssel dolgoznak.)

A fenti probléma áthidalására nyújtanak megoldást az .EXE fájlok, melyek tetszőleges számú szegmensből állhatnak. Ezek olyan speciális fájlok, melyek elején egy ún. relokációs (azaz áthelyezési) táblázat található. A táblázat minden elem egy 32-bites cím, ami a tényleges (a relokációs táblát követő) futtaható fájlban határoz meg egy offszetcímet. Ezeket a címeket a futtatható állományt létrehozó fordító program generálja, mégpedig úgy, hogy azok mindegyike a programban található egy-egy belső szegmens-hivatkozásra (adatelérés v. távoli ugrások) mutasson. A fordító az .EXE fájlba nem a valódi szegmenscímeket fordítja bele (hiszen azok fordítási időben még nem meghatározhatóak, mert a program a memóriában bárhová kerülhet), hanem csak egy relatív (0-bázisú) ál-szegmens-címet amely valójában csak azt határozza meg, hogy a hivatkozás a program hányadik (!) szegmensére történik. A program betöltésekor a DOS a relokációs tábla elemeit egyenként kiolvasva a szegmens-hivatkozásokat a programnak a memóriában kijelölt hely első szegmensének fizikai címével megnöveli, így generálva a valódi, a futtatás pillanatában érvényes fizikai szegmens-címeket. Így, amikor a program a betöltése után megkapja a vezérlést a memóriában már a tényleges fizikai elhelyezkedéséhez igazított szegmens-címek találhatóak.

Az egyszerűbb érthetőség kedvéért lássunk egy példát. Tegyük fel, hogy programunk két kódszegmenst és egy adat-, valamint veremszegmenst tartalmaz, mégpedig fizikailag ebben a sorrendben. Az 1. kódszegmens a fájlban a 0. offszeten kezdődik és 4190 bájtos. Mivel a szegmenseknek mindig 16-tal osztható címen (paragrafushatáron) kell kezdődniük, így a következő, 2. kódszegmens a fájlban a 4192. offszeten kezdődik, hiszen ez a legközelebbi szegmens-határ az 1. kódszegmens után ; mérete 1019 bájt. A 16381 bájtos adatszegmens ennek megfelelően az 5120. (4096+1024, 1. ill. 2. kódszegmens paragrafus-határra kerekített mérete) offszeten kezdődik. A 8188 bájtos veremszegmens pedig a 21504. (5120+16384) offszettől kezdődik. A teljes fájl így - a fejlécet és a relokációs táblázatot nem számítva 29692 bájtos. A fájl szerkezetét az alábbi táblázat mutatja.
 

Szegmens
Kezdő offszet
Kezdő szegmens*
Méret
Méret** paragrafusokban
1. kódszegmens
0
0
4190
262
2. kódszegmens
4192
262
1019
64
adatszegmens
5120
320
16381
1024
veremszegmens
21504
1344
8188
512

*a kezdő szegmenst az offszet 16-tal való osztásával kaphatjuk meg

**a méret 16-tal való osztásával kapjuk meg

Tehát a fordító minden az 1. kódszegmensből a 2. kódszegmensbe irányuló 32-bites ugró utasításba a 262 szegmens és a rutin a szegmensen belül elfoglalt helyének megfelelő ofszet-címből álló fizikai címet helyez el. Az adatszegment elérő utasítások esetén pedig 320 lesz a szegmens-cím. A program betöltésekor mindegyik 32-bites referenciához tartozó (amiknek relatív címeit ugye a fordító a relokációs táblázatban helyezte el és innen tudja a DOS, hogy azokat módosítani kell) bejegyzést a fent leírtaknak megfelelően módosít. Így, ha - tegyük fel - a programunkat a 22ECh fizikai szegmenstől kezdődve töltötte be a memóriába a DOS, akkor (a relokációs táblázat segítségével) minden a 2. kódszegmensbe irányuló 32-bites hivatkozás szegmens-címéhez (amiknek eddig ugye mind 262 volt az értéke) hozzáadja a 22ECh értéket, így azokat 23F2h-ra (22ECh + 262) módosítva. Ugyanígy az adatszegmens-hivatkozások eredeti 320-as szegmens-része 242Ch-ra módosul a relokációt követően.