Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| en:docs:win16:modules:local_heap [2026/02/25 09:10] – prokushev | en:docs:win16:modules:local_heap [2026/02/26 01:33] (current) – prokushev | ||
|---|---|---|---|
| Line 4: | Line 4: | ||
| ===== Overview ===== | ===== Overview ===== | ||
| - | In 16-bit versions of Windows (1.x, 2.x, 3.x), each module (application or DLL) typically has its own data segment (DGROUP) limited to 64 KB. This segment contains the stack, the local heap, and the atom table. Local heap functions manage memory within this segment using near pointers (offsets). They are exported by the kernel (KRNL386.EXE). Internally, the local heap is managed through a set of data structures that reside inside the segment itself. | + | In 16-bit versions of Windows (1.x, 2.x, 3.x), each module (application or DLL) typically has its own data segment (DGROUP) limited to 64 KB. This segment contains the stack, the local heap, and the atom table. Local heap functions manage memory within this segment using near pointers (offsets). They are exported by the KERNEL module. Internally, the local heap is managed through a set of data structures that reside inside the segment itself. |
| - | A key part of this management is the Instance Data (also called the NULL segment) located at the very beginning of DGROUP, which holds pointers to the heap, atom table, and stack information. The field at offset 6 (pLocalHeap) contains a near pointer to the HeapInfo structure that heads the local heap. This pointer is set by [[en: | + | A key part of this management is the Instance Data (also called the NULL segment) located at the very beginning of DGROUP, which holds pointers to the heap, atom table, and stack information. The field at offset 6 (pLocalHeap) contains a near pointer to the HeapInfo structure that heads the local heap. This pointer is set by [[en: |
| The local heap itself is organized as a series of arenas (headers) that precede each block. The two low bits of the la_prev field in an arena are used as flags: bit 0 indicates whether the block is in use (1) or free (0); bit 1 indicates whether the block is MOVEABLE (1) or FIXED (0). Free blocks are linked via la_free_prev and la_free_next. For MOVEABLE blocks, a separate handle table (pointed to by hi_htable in HeapInfo) stores the actual address, lock count, and flags; each handle is an offset into this table. | The local heap itself is organized as a series of arenas (headers) that precede each block. The two low bits of the la_prev field in an arena are used as flags: bit 0 indicates whether the block is in use (1) or free (0); bit 1 indicates whether the block is MOVEABLE (1) or FIXED (0). Free blocks are linked via la_free_prev and la_free_next. For MOVEABLE blocks, a separate handle table (pointed to by hi_htable in HeapInfo) stores the actual address, lock count, and flags; each handle is an offset into this table. | ||
| Line 19: | Line 19: | ||
| | 00h | WORD | wMustBeZero | Must be zero for the NULL segment structure to be considered present. | | | 00h | WORD | wMustBeZero | Must be zero for the NULL segment structure to be considered present. | | ||
| | 02h | DWORD | dwOldSSSP | When [[en: | | 02h | DWORD | dwOldSSSP | When [[en: | ||
| - | | 06h | WORD | pLocalHeap | Near pointer to the Local Heap information structure (i.e., the HeapInfo structure). This field is set by [[en: | + | | 06h | WORD | pLocalHeap | Near pointer to the Local Heap information structure (i.e., the HeapInfo structure). This field is set by [[en: |
| | 08h | WORD | pAtomTable | Near pointer to the atom table structure. Set by [[en: | | 08h | WORD | pAtomTable | Near pointer to the atom table structure. Set by [[en: | ||
| | 0Ah | WORD | pStackTop | Near pointer to the end (top) of the stack. For DLLs, this is zero. | | | 0Ah | WORD | pStackTop | Near pointer to the end (top) of the stack. For DLLs, this is zero. | | ||
| Line 35: | Line 35: | ||
| Every local heap begins with an instance of the HeapInfo structure, which is identical to the one used by the global heap and is defined in WINKERN.INC. Its location is given by the pLocalHeap field at offset 6 of the Instance Data. Immediately following the HeapInfo structure are additional fields that, together with HeapInfo, form the LocalInfo structure. | Every local heap begins with an instance of the HeapInfo structure, which is identical to the one used by the global heap and is defined in WINKERN.INC. Its location is given by the pLocalHeap field at offset 6 of the Instance Data. Immediately following the HeapInfo structure are additional fields that, together with HeapInfo, form the LocalInfo structure. | ||
| - | ===== HeapInfo Structure (386) ===== | + | ===== HeapInfo Structure (KRNL386) ===== |
| - | In the enhanced mode (krnl386), the HeapInfo structure occupies **1Eh** bytes and has the following format (according to " | + | Under KRNL386, the HeapInfo structure occupies **1Eh** bytes and has the following format (according to " |
| ^ Offset ^ Type ^ Field ^ Description ^ | ^ Offset ^ Type ^ Field ^ Description ^ | ||
| | 00h | WORD | hi_check | If this value is nonzero, the debug version of KERNEL verifies the heap. This field appears to be used only for the local heap, not for the global heap. | | | 00h | WORD | hi_check | If this value is nonzero, the debug version of KERNEL verifies the heap. This field appears to be used only for the local heap, not for the global heap. | | ||
| - | | 02h | WORD | hi_freeze | If this is nonzero, KERNEL should not compact the heap. For the global heap, this value appears to be set only while inside the INT 24h handler. The local heap is frozen during [[en: | + | | 02h | WORD | hi_freeze | If this is nonzero, KERNEL should not compact the heap. For the global heap, this value appears to be set only while inside the INT 24h handler. The local heap is frozen during [[en: |
| | 04h | WORD | hi_count | The total number of blocks in the heap. | | | 04h | WORD | hi_count | The total number of blocks in the heap. | | ||
| | 06h | DWORD | hi_first | A far pointer to the arena header for the first block in the heap. The first block is always a sentinel and points to itself. | | | 06h | DWORD | hi_first | A far pointer to the arena header for the first block in the heap. The first block is always a sentinel and points to itself. | | ||
| Line 54: | Line 54: | ||
| | 1Ch | WORD | hi_pstats | A near pointer to a LocalStats structure which the local heap uses in the debug KERNEL. As the local heap does various things, such as search for free blocks, it increments fields in the structure. The structure is defined in WINKERN.INC. | | | 1Ch | WORD | hi_pstats | A near pointer to a LocalStats structure which the local heap uses in the debug KERNEL. As the local heap does various things, such as search for free blocks, it increments fields in the structure. The structure is defined in WINKERN.INC. | | ||
| - | ===== HeapInfo Structure (286) ===== | + | ===== HeapInfo Structure (KRNL286) ===== |
| - | In standard mode (krnl286), the HeapInfo structure occupies 18h bytes. Due to the 16‑bit segmented architecture, | + | Under KRNL286, the HeapInfo structure occupies 18h bytes. The layout is as follows: |
| ^ Offset ^ Type ^ Field ^ Description ^ | ^ Offset ^ Type ^ Field ^ Description ^ | ||
| Line 65: | Line 65: | ||
| | 0Ah | BYTE | hi_ncompact | Number of compactions performed (or bitfield). | | | 0Ah | BYTE | hi_ncompact | Number of compactions performed (or bitfield). | | ||
| | 0Bh | BYTE | hi_dislevel | Current discard level. | | | 0Bh | BYTE | hi_dislevel | Current discard level. | | ||
| - | | 0Ch | WORD | hi_distotal | When discarding begins, this field contains the number of bytes that need to be discarded. As discarding proceeds, the sizes of discarded blocks are subtracted until the value reaches zero or below. | + | | 0Ch | WORD | hi_distotal | When discarding begins, this field contains the number of bytes that need to be discarded. As discarding proceeds, the sizes of discarded blocks are subtracted until the value reaches zero or below. | |
| | 0Eh | WORD | hi_htable | Near pointer to the handle table for moveable blocks. Used only by the local heap. | | | 0Eh | WORD | hi_htable | Near pointer to the handle table for moveable blocks. Used only by the local heap. | | ||
| | 10h | WORD | hi_hfree | Near pointer to the free handle table entry list. | | | 10h | WORD | hi_hfree | Near pointer to the free handle table entry list. | | ||
| Line 72: | Line 72: | ||
| | 16h | WORD | hi_pstats | Near pointer to a LocalStats structure (used in debug kernel). | | | 16h | WORD | hi_pstats | Near pointer to a LocalStats structure (used in debug kernel). | | ||
| - | Note: In krnl286, all pointer fields are near (16‑bit offsets) because the local heap resides in a single 64‑KB segment. | + | ===== LocalInfo Structure ===== |
| - | ===== LocalInfo Structure (386) ===== | + | For both **KRNL386** and **KRNL286**, the LocalInfo structure immediately follows the HeapInfo structure and has the following layout: |
| - | + | ||
| - | For both **krnl386** and **krnl286**, the LocalInfo structure immediately follows the HeapInfo structure and has the following layout: | + | |
| ^ Offset ^ Type ^ Field ^ Description ^ | ^ Offset ^ Type ^ Field ^ Description ^ | ||
| Line 87: | Line 85: | ||
| Important: | Important: | ||
| - | * In krnl286, the li_sig field is located at offset 22h from the beginning of the combined HeapInfo + LocalInfo structure. | + | * In KRNL286, the li_sig field is located at offset 22h from the beginning of the combined HeapInfo + LocalInfo structure. |
| - | * In krnl386, the li_sig field is located at offset 28h from the beginning of the combined HeapInfo + LocalInfo structure. | + | * In KRNL386, the li_sig field is located at offset 28h from the beginning of the combined HeapInfo + LocalInfo structure. |
| ==== Arena Formats ==== | ==== Arena Formats ==== | ||
| Line 140: | Line 138: | ||
| ^ Offset ^ Type ^ Field ^ Description ^ | ^ Offset ^ Type ^ Field ^ Description ^ | ||
| | 00h | WORD | lhe_address | Address of the memory block referenced by the handle. | | | 00h | WORD | lhe_address | Address of the memory block referenced by the handle. | | ||
| - | | 02h | BYTE | lhe_flags | Flags: 0Fh = LHE_DISCARDABLE (discard level), 1Fh = LHE_USERFLAGS (reserved for programmer), | + | | 02h | BYTE | lhe_flags | Flags: 0Fh = LHE_DISCARDABLE (discard level), 1Fh = LHE_USERFLAGS (reserved for programmer?), 40h = LHE_DISCARDED (block has been discarded). | |
| | 03h | BYTE | lhe_count | Lock count of the block. Non‑zero prevents moving or discarding. | | | 03h | BYTE | lhe_count | Lock count of the block. Non‑zero prevents moving or discarding. | | ||
| Line 150: | Line 148: | ||
| ===== Heap Operations ===== | ===== Heap Operations ===== | ||
| - | * **Allocation (LocalAlloc)** walks the free list, splitting blocks if necessary, and sets up the appropriate arena. For MOVEABLE blocks, it also allocates a handle table entry. | + | * **Allocation ([[en: |
| - | * **Compaction (LocalCompact)** coalesces adjacent free blocks and may move or discard unlocked MOVEABLE blocks. When a block is moved, its lhe_address is updated. | + | * **Compaction ([[en: |
| - | * **Locking (LocalLock/ | + | * **Locking ([[en: |
| - | * **Discarding (LocalDiscard)** frees the memory of a MOVEABLE block but keeps the handle entry alive with the LHE_DISCARDED flag set. | + | * **Discarding ([[en: |
| ===== Atom Tables ===== | ===== Atom Tables ===== | ||
| Line 163: | Line 161: | ||
| ==== Relationship with the Local Heap ==== | ==== Relationship with the Local Heap ==== | ||
| - | Physically, an atom table resides **inside** the local heap of some data segment. Therefore, before creating an atom table, the segment must be initialized as a local heap by calling | + | Physically, an atom table resides **inside** the local heap of some data segment. Therefore, before creating an atom table, the segment must be initialized as a local heap by calling |
| ==== ATOMENTRY Structure ==== | ==== ATOMENTRY Structure ==== | ||
| Line 181: | Line 179: | ||
| ===== String Atoms ===== | ===== String Atoms ===== | ||
| - | String atoms are created by passing an ordinary string to `AddAtom` or `GlobalAddAtom`. They are stored in the atom table as `ATOMENTRY` structures. | + | String atoms are created by passing an ordinary string to [[en: |
| * **Range**: `0xC000` to `0xFFFF` (encoded pointer). | * **Range**: `0xC000` to `0xFFFF` (encoded pointer). | ||
| Line 222: | Line 220: | ||
| **Important: | **Important: | ||
| - | |||
| - | ==== Local vs. Global Atom Tables ==== | ||
| - | |||
| - | * **Local atom tables**: Bound to a specific data segment (e.g., an application' | ||
| - | * **Global atom table**: A system-wide table accessible to all applications via `GlobalAddAtom`, | ||
| ==== Creating Custom Atom Tables (outside DGROUP) ==== | ==== Creating Custom Atom Tables (outside DGROUP) ==== | ||
| Line 232: | Line 225: | ||
| Since all atom operations work with the current segment pointed to by DS, you can create and use an atom table in any arbitrary data segment by following three steps: | Since all atom operations work with the current segment pointed to by DS, you can create and use an atom table in any arbitrary data segment by following three steps: | ||
| - | - **Create a local heap** in the target segment using `LocalInit(Selector, | + | - **Create a local heap** in the target segment using `[[en: |
| - **Switch the DS register** to that segment. | - **Switch the DS register** to that segment. | ||
| - | - Call `InitAtomTable(size)` to initialize the atom table in the newly created heap. | + | - Call `[[en: |
| - | After that, any subsequent call to `AddAtom`, `FindAtom`, etc., will operate on the custom table if DS is temporarily set to the correct segment. | + | After that, any subsequent call to `[[en: |
| ==== Summary of Atom Type Differences ==== | ==== Summary of Atom Type Differences ==== | ||
| Line 304: | Line 297: | ||
| </ | </ | ||
| - | After this call, the global block can be used with local heap functions (`LocalAlloc`, | + | After this call, the global block can be used with local heap functions (`LocalAlloc`, |
| **Important Considerations: | **Important Considerations: | ||




