aboutsummaryrefslogblamecommitdiffstats
path: root/docs/nt-architecture-mapping.md
blob: 3510f8262a04f6e62a45e08e9820f21e1b647710 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489








































































































































































































































































































































































































































































































                                                                                                                                                                                                         
# NT Kernel Architecture — Holistic Mapping to a Database Kernel

A full-system view of the ReactOS NT kernel (`ntoskrnl/`) mapped onto the
architecture of a database kernel. The object manager comparison is covered
separately in `reactos-ob-comparison.md`; this document covers the rest of the
kernel and treats the project as a whole.

The central thesis: **NT is already a database kernel in disguise**. It manages
named, typed, reference-counted objects; enforces capability-based access control;
schedules work across a pool of threads; routes requests through a layered
dispatch stack; and maintains a write-buffered, lazily-flushed cache in front of
physical storage. Every one of those responsibilities has a direct counterpart in
a relational database engine.

---

## Subsystem Map

| NT subsystem | DB kernel counterpart |
|---|---|
| **KE** — kernel scheduler, IRQL, APC/DPC, synchronization primitives | Query scheduler, latch manager, deferred execution |
| **PS** — process/thread lifecycle, job objects, quotas | Session manager, worker thread pool, resource groups |
| **IO** — driver model, IRP dispatch stack, completion ports | Storage engine interface, query operator pipeline, async execution |
| **MM** — virtual memory, page faults, working set, section objects | Buffer pool, page frame allocator, eviction policy |
| **CC** — cache manager, lazy writer, VACB, prefetcher | WAL buffer / dirty page flusher, sequential prefetch |
| **SE** — tokens, ACLs, DACL/SACL, privilege evaluation | User principal, role/privilege set, access control, audit |
| **EX** — events, work queues, lookaside lists, rundown refs | Synchronization utilities, worker dispatch, buffer allocation |
| **LPC** — port objects, request/reply messaging | Wire protocol layer, client connection, inter-session RPC |
| **FSRTL** — fast I/O bypass, MCB extent map, file byte-range locks | Index hot-path, extent/page mapping, record-level locks |
| **OB** — object manager | Object manager (covered in reactos-ob-comparison.md) |
| **HAL** — CPU, interrupt, DMA, timer abstraction | Storage media abstraction, CPU/NUMA topology |
| **KD** — kernel debugger transport | EXPLAIN / trace / diagnostic interface |
| **CONFIG** — registry hive manager | System catalog, configuration store |

---

## 1. Subsystems as Client Protocols

NT supports multiple *environment subsystems*: Win32 and POSIX both run on top of
the same kernel. Each subsystem is a user-mode personality layered above a common
native API (`ntdll.dll`). The kernel itself has no awareness of Win32 or POSIX;
it exposes only native NT objects (files, processes, events). The subsystem DLLs
translate environment-specific calls into native system calls.

```
Win32 application            POSIX application
      ↓                            ↓
  kernel32.dll                  libc (POSIX layer)
      ↓                            ↓
         ntdll.dll (native NT API)
                    ↓
           ntoskrnl (kernel)
```

**Database analogue: wire protocols as subsystems.**

The database kernel (RelationalNT) is the `ntoskrnl`. Each supported wire
protocol — PostgreSQL, MySQL, HTTP/JSON — is an environment subsystem. A thin
native client library is the `ntdll` equivalent: it translates wire-protocol
messages into kernel-level handle opens, cursor operations, and VM plan
evaluations. The kernel itself never sees SQL syntax or protocol framing.

```
psql client         MySQL client        HTTP client
      ↓                  ↓                   ↓
  PG wire adapter   MySQL wire adapter   REST adapter
      ↓                  ↓                   ↓
         native client library (ntdll equivalent)
                         ↓
              RelationalNT kernel
```

The subsystem boundary is where SQL parsing, result serialization, and
protocol-specific error codes live. The kernel boundary is where object paths,
handle allocation, cursor iteration, and capability checks live. Keeping these
boundaries clean means a new wire protocol never touches the kernel; it only
implements the native-to-protocol translation layer.

`CSRSS` (the client-server runtime) is the service process that hosts
session-specific subsystem state on the NT side. The DB equivalent is a per-
protocol server daemon that owns wire connections and dispatches them to the
native API.

---

## 2. IO Manager → Query Operator Pipeline

The NT I/O manager's driver stack model is the most directly applicable pattern
for query execution.

### NT Driver Stack

```
NtReadFile
  ↓
I/O Manager: allocates IRP, fills stack location [0]
  ↓  IoCallDriver()
Filesystem driver (NTFS):  interprets path, maps to block range
  ↓  IoCallDriver()
Disk class driver: translates logical → physical sectors
  ↓  IoCallDriver()
Disk port driver: issues SCSI/ATA commands
  ↓  IoCallDriver()
Miniport: hardware-specific DMA
```

Each driver layer processes its stack location and either completes the IRP
inline (sync fast path) or pends it and calls the next driver. Completion fires
back up through registered completion routines — bottom to top — until the
original caller wakes.

### Database Analogue: PlanNode as IRP

The VM's `PlanNode` is an IRP. Each operator in the query plan is a driver layer
with a dispatch routine (`Next()`). The Volcano pull model is completion-routine
dispatch in reverse: instead of pushing results down-to-up on completion, the
caller pulls up-to-down via `Next()`.

```
VM::Next(root_plan_node)
  ↓  Next() dispatch
JOIN operator:    requests rows from left and right children
  ↓  Next()
FILTER operator:  requests rows, discards those failing predicate
  ↓  Next()
PROJECT operator: strips unwanted attributes
  ↓  Next()
SCAN operator:    pulls raw tuples from CursorManager
  ↓
CursorManager::Next() → physical storage
```

NT's `IRP_STACK_LOCATION` (one per driver layer) maps to each `PlanNode` holding
its own operator-local state (join buffer, filter expression, projection map).
NT's `IRP_MJ_READ` / `IRP_MJ_WRITE` major function codes map to `Op::SCAN`,
`Op::JOIN`, `Op::FILTER`, `Op::PROJECT`.

**Filter drivers.** NT allows filter drivers to insert between any two layers
without modifying either. The DB equivalent is transparent middleware: a
compression or encryption operator inserted between SCAN and PROJECT without
touching either. An audit operator inserted at the top of every plan without
touching the plan itself.

**Async IRP / completion ports.** NT issues an IRP and gets a completion
notification later. For the DB, a query issued by a session does not block the
session thread if the operator tree must wait on I/O. A completion-port analogue
(a result queue per session) allows the session thread to yield and be notified
when the cursor has rows ready. This is relevant when the physical storage backend
is remote (etcd, S3) and latency is non-trivial.

---

## 3. Drivers → Storage Backends

A NT device driver is a self-contained module that satisfies read/write/control
requests for a physical or logical device. The kernel does not know whether the
device is a disk, a network socket, or a USB key; it only knows the driver
dispatch table.

`CursorManager` is the equivalent of the I/O manager's dispatch layer. The mock
store (`unordered_map`) is the miniport. Replacing it with LMDB, RocksDB, etcd,
or an in-memory append log is equivalent to swapping a miniport driver without
changing the I/O manager or the filesystems above it.

```
CursorManager::Open / Next / Close
         ↓
Storage backend interface (the "driver dispatch table")
         ↓
   ┌─────────────┬──────────────┬─────────────┐
   │ LMDB backend│ etcd backend │ memory backend│
   └─────────────┴──────────────┴─────────────┘
```

The driver model implies that the storage backend interface should be stable and
narrow: `Open(path, snapshot_id)`, `Next()`, `Close()`. All backend-specific
concerns (transaction semantics, key encoding, compression) stay inside the
backend module. `CursorManager` holds snapshot pinning and buffering logic that
applies regardless of backend, exactly as the disk class driver holds caching
logic regardless of whether the miniport is SATA or NVMe.

**Filter driver analogue.** A replication backend can be a filter: it intercepts
`Next()` calls after the underlying backend returns a row and ships a copy to a
replica. A row-level encryption backend decrypts inline before returning to the
caller. These compose without the operator pipeline or the caller knowing.

---

## 4. MM → Buffer Pool

NT's Memory Manager manages physical page frames and virtual address mappings. The
three-tier hierarchy is:

```
Process working set (recently used pages, trimmed under pressure)
         ↓
Cache Manager VACBs (256 KB mapped views of files in kernel VA space)
         ↓
Physical storage (disk, via filesystem IRP)
```

Page faults are demand-load: a page is only brought from disk when first accessed.
The working set trimmer evicts least-used pages under memory pressure. Section
objects allow multiple processes to map the same physical pages (shared memory).

**Database buffer pool.** The same three tiers apply:

```
Query working set (pages referenced by active cursors, pinned)
         ↓
Buffer pool frames (fixed-size pages cached in memory)
         ↓
Physical storage backend
```

The NT page fault handler (`MmNotPresentFault`) maps to a buffer pool miss:
when a cursor requests a page not in the buffer pool, load it from the backend.
The NT working set trimmer maps to LRU eviction: under memory pressure, unpin
pages not referenced by any active cursor. NT section objects (shared physical
pages across processes) map to shared buffer pool pages accessible from multiple
sessions.

Because this project uses immutable snapshot storage, the eviction policy is
simplified: a page belonging to a snapshot pinned by at least one cursor
(`reference_count > 0` on the snapshot registry entry) cannot be evicted. Pages
belonging to snapshots with no remaining cursors are candidates for eviction
regardless of recency. This is cleaner than NT's working-set model because there
is no dirty-page complexity — writes always produce new pages, never mutate
existing ones.

---

## 5. CC → WAL and Dirty Page Flush

NT's Cache Manager sits between the filesystem and MM. Its `VACB` (Virtual Address
Control Block) is a 256 KB view of a file in kernel virtual address space. The
lazy writer (`CcWorkerThread`) periodically scans `DirtyVacbListHead` and writes
modified pages back to disk. The prefetcher traces access patterns and pre-loads
pages before they are requested.

**Database analogue: write-ahead log buffer and sequential prefetch.**

Because storage is immutable here, the cache manager's dirty-write concern does
not apply directly. New snapshot pages are always appended; they are never
modifications of existing cached pages. However, the structural lessons hold:

- **Append buffer.** New snapshot pages accumulate in an in-memory append buffer
  before being flushed to the physical backend. The lazy writer pattern (flush
  when the buffer exceeds a threshold, or periodically) applies directly.
- **Read-ahead prefetch.** Sequential scans access pages in order. A prefetch
  mechanism that issues backend reads ahead of the cursor's current position
  reduces latency. NT's prefetcher learns per-scenario patterns; a database
  prefetcher can use the query plan to predict access order exactly.
- **VACBs as buffer frames.** Each VACB is a fixed-size view; the database
  equivalent is a fixed-size buffer frame. Fixed-size frames simplify allocation
  and eviction arithmetic.

---

## 6. PS → Sessions and Worker Threads

NT process = database session. NT thread = query worker thread.

```
PEPROCESS                    Session
  ├─ Handle table              ├─ Handle table (open cursors, relations)
  ├─ Token (identity)          ├─ Connection context (AUTH_CLAIM set)
  ├─ Working set               ├─ Buffer pool pinned pages
  ├─ Quota (memory, CPU, I/O)  ├─ Resource quota (memory, CPU, I/O)
  └─ ThreadListHead            └─ Worker thread set

PETHREAD                     Query worker
  ├─ APC queue                 ├─ Deferred plan evaluation queue
  ├─ IRP queue                 ├─ Pending cursor operations
  └─ Impersonation context     └─ SECURITY DEFINER / SET ROLE context
```

NT job objects group processes with shared limits: NT's `NtAssignProcessToJobObject`
enforces memory, CPU time, and I/O quotas across a group. The DB equivalent is a
*resource group* or *tenant*: a set of sessions sharing a memory limit and a CPU
share. Multi-tenant databases need this to prevent one tenant's runaway query from
starving others. Job objects are the right model: assign sessions to resource
groups at connection time and enforce limits at the job-object level rather than
per-session.

**Impersonation.** NT allows a thread to temporarily assume a different security
context (`SeImpersonateClient`). The DB equivalent is `SET ROLE` or `SECURITY
DEFINER` in stored procedures: the query worker temporarily adopts a different
access context without requiring the connection to re-authenticate.

---

## 7. SE → Authorization Beyond the Object Manager

NT's security subsystem extends what the object manager does. Where OB attaches a
security descriptor to an object and checks access at handle-open time, SE
provides the richer infrastructure underneath:

- **Token structure.** A token carries the caller's SID (identity), group
  memberships, privilege set, and mandatory integrity level. The DB equivalent
  is the connection context: user identity, granted AUTH_CLAIMs, session-level
  role, and a trust level (e.g., a connection from localhost vs. from the public
  internet might carry different trust).
- **DACL / SACL split.** The DACL controls access; the SACL controls audit logging
  (which accesses are recorded, on success or failure). For a database these are
  different concerns: the permission check decides whether to allow the operation;
  the audit policy decides whether to log it. Conflating them in a single
  security descriptor is correct design; it keeps both concerns co-located with
  the object rather than scattered.
- **Privilege evaluation.** NT privileges (SE_BACKUP_PRIVILEGE bypasses ACLs;
  SE_DEBUG_PRIVILEGE allows opening any process) have a DB analogue in superuser
  privileges: a DBA role that bypasses row-level security, or a replication user
  that bypasses normal write contention checks. These are orthogonal to the
  capability tree and should be modelled as privileges on the connection token,
  not as explicit capabilities on every object.
- **Mandatory access control (integrity levels).** NT's integrity levels
  (UNTRUSTED < LOW < MEDIUM < HIGH < SYSTEM) prevent low-integrity processes
  from writing to high-integrity objects even if the DACL permits it. The DB
  equivalent is a trust tier on connections: an externally-facing API connection
  at LOW tier cannot write to audit or system catalog tables regardless of the
  DACL, because those tables require HIGH tier.

---

## 8. LPC → Wire Protocol and Inter-Session Messaging

NT's Local Procedure Call provides synchronous request-reply messaging between
processes via named port objects. A server process creates a named connection port;
clients connect and receive private communication ports. This is the mechanism
through which Win32 applications communicate with CSRSS (console, shutdown) and
through which subsystem DLLs synchronize session state with the subsystem server.

**Database analogue: wire protocol connection.**

Each client connection to the database is an LPC communication port. The session
server daemon listens on a named port (the TCP socket); accepted connections
become private communication ports (per-client sessions). The session daemon is
CSRSS.

LPC's fixed message size limit (`LPCP_MAX_MESSAGE_SIZE`) prevents a misbehaving
client from forcing unbounded allocation. The DB wire protocol layer should apply
the same discipline: a maximum query length, a maximum result frame size, and a
maximum number of concurrent open cursors per session.

LPC ports are objects in the NT namespace. Database connection endpoints (the
listening socket, or a named pipe for local connections) should similarly be
registered as objects in the RelationalNT namespace, making them subject to the
same capability checks: a client must have explicit permission to connect to a
particular endpoint.

---

## 9. FSRTL → Index Extent Management and Record Locks

The File System Runtime Library is a set of utilities that filesystems use. The
most relevant pieces for a database are:

- **MCB (Map Control Block).** Tracks the logical-to-physical block mapping for a
  file that may be stored in non-contiguous extents on disk. The DB equivalent is
  an index page map: a B-tree or extent map translating a logical page number in
  an index to a physical storage location. The MCB API (`FsRtlAddLargeMcbEntry`,
  `FsRtlLookupLargeMcbEntry`) is a model for an extent manager that handles
  fragmentation without surfacing it to callers.
- **Fast I/O bypass.** `FAST_IO_DISPATCH` provides function pointers for
  synchronous I/O that skip IRP allocation entirely when the requested data is
  already in the cache. The DB equivalent is a buffer pool hot-path: if the
  requested snapshot page is already pinned in the buffer pool, return it without
  going through the full CursorManager dispatch chain.
- **Byte-range file locking.** `FILE_LOCK` tracks exclusive/shared locks on
  sub-file ranges. The DB equivalent is record-level locking within a relation.
  Since storage here is immutable, this concern is narrowed to locking mutable
  references (branch HEADs) rather than individual records — but the MCB-style
  range-tracking structure is still the right data structure for knowing which
  parts of a namespace are currently locked by which writers.

---

## 10. HAL → Storage Media Abstraction

The Hardware Abstraction Layer isolates the kernel from CPU, interrupt controller,
and timer differences between x86 and ARM machines. It provides a stable set of
primitives (`HalAllocateAdapterChannel`, `HalReadDmaCounter`, `KeAcquireSpinLock`)
that the rest of the kernel calls without knowing the underlying hardware.

**Database analogue: storage media and topology abstraction.**

The equivalent boundary sits between `CursorManager` (the "kernel") and the
physical storage backend (the "hardware"). The HAL lessons:

- **Platform-neutral primitives.** `CursorManager::Next()` and `Close()` should
  never contain NVMe-specific or LMDB-specific code. Those specifics live in the
  backend module, exactly as NVMe DMA specifics live in the miniport, not the HAL.
- **CPU/NUMA topology.** HAL exposes processor and memory node topology so the
  scheduler can make NUMA-aware placement decisions. A database on a NUMA machine
  benefits from binding buffer pool regions and worker threads to the same NUMA
  node. This is a future concern but the right interface is at the HAL boundary:
  a `platform_topology` structure that the scheduler queries without knowing
  whether it is running on x86, ARM, or a cloud VM.
- **Timer abstraction.** HAL provides a system tick and a high-resolution
  performance counter. The DB needs both: a coarse timer for transaction
  timestamps and TTL-based expiry, and a high-resolution counter for query
  latency measurement.

---

## 11. KD → Diagnostic Interface

NT's kernel debugger (`kd/`) provides a serial transport over which WinDBG can
inspect kernel state, set breakpoints, and walk data structures live. The
`!object`, `!handle`, and `!poolused` debugger extensions expose the object
manager, handle tables, and pool allocator without stopping the machine.

**Database analogue: EXPLAIN and live diagnostic.**

The equivalent is a diagnostic interface that can walk live runtime state: the
object registry, open handle table, active cursors, and lifecycle counters.
Implemented as a reserved namespace path (`/system/diagnostics/`), it exposes
read-only virtual objects whose `Next()` returns runtime state rather than stored
data. Queries against `/system/diagnostics/cursors` return a row per open cursor;
queries against `/system/diagnostics/objects` return a row per registry entry with
its handle and reference counts. This is `EXPLAIN ANALYZE` and
`pg_stat_activity` unified under the same object model, for free.

---

## 12. Config Manager → System Catalog

NT's configuration manager (`config/`) manages registry hives — persistent
key-value stores loaded at boot and kept consistent via transaction logs. The
registry is itself stored as a structured binary file that the kernel reads before
any filesystem driver is loaded.

**Database analogue: system catalog.**

The system catalog is a set of relations stored in a reserved multigroup
(`/system/catalog/`) that describes all other objects: their types, paths,
security descriptors, and capabilities. It is the registry of the database. It
must be readable before any user-defined relations are accessible, exactly as the
registry is readable before any device driver is loaded.

Because storage is immutable here, the catalog is itself versioned: each schema
change produces a new catalog snapshot, and the kernel always reads the latest
committed catalog version on startup. Old catalog versions remain accessible for
historical schema reconstruction (e.g., reading data written under a previous
schema). This is more powerful than the NT registry, which has no built-in
history.

---

## Architectural Summary

The full NT kernel, mapped to a database, produces this layer diagram:

```
┌─────────────────────────────────────────────────────────────────┐
│              Wire Protocol Subsystems (LPC / Env. Subsystems)   │
│    PostgreSQL wire   MySQL wire   HTTP/JSON   native client lib  │
├─────────────────────────────────────────────────────────────────┤
│                   Session Manager  (PS)                         │
│    Sessions · Worker threads · Resource groups · Impersonation  │
├─────────────────────────────────────────────────────────────────┤
│              Authorization Engine  (SE + OB)                    │
│    Tokens · Capabilities · DACL/SACL · Privilege evaluation     │
├─────────────────────────────────────────────────────────────────┤
│                Query Operator Pipeline  (IO / VM)               │
│    SCAN · FILTER · PROJECT · JOIN · SORT · AGGREGATE            │
│    Async execution · Filter-driver middleware · Plan nodes       │
├─────────────────────────────────────────────────────────────────┤
│     Object Manager  (OB)          Namespace  (NamespaceRefMgr)  │
│     Registry · Handles · Types    Branch refs · View reparse    │
├─────────────────────────────────────────────────────────────────┤
│            Buffer Pool  (MM + CC)                               │
│    Page frames · Working set · Prefetch · Append flusher        │
├─────────────────────────────────────────────────────────────────┤
│              Storage Backend Interface  (IO driver model)       │
│    CursorManager · Backend dispatch table · Filter backends     │
├─────────────────────────────────────────────────────────────────┤
│         Physical Backends  (HAL / miniport drivers)             │
│         LMDB · RocksDB · etcd · in-memory · S3                 │
└─────────────────────────────────────────────────────────────────┘
```

The current RelationalNT codebase covers the Object Manager, parts of the
Operator Pipeline (SCAN only), and the authorization skeleton. The layers above
(session manager, wire protocols) and below (real buffer pool, real storage
backends) are the next frontier. The NT architecture shows that each layer has a
well-understood responsibility and a narrow interface to its neighbours — that
discipline is the reason the NT kernel scales from embedded devices to 1024-core
servers without architectural changes.