Once an area of address space has been reserved for persistent applications, we then need override the DPMI default event handler to ensure that we can swap segments to and from disk on demand.
To each selector, there corresponds an entry in a table of descriptors. The descriptors are data structures which specify the base address, size and other attributes of the segment. One of these attributes is a present flag which is checked each time a segment register is loaded. Segment not-present exceptions are triggered when an application loads a segment register with a selector corresponding to a segment that is marked as not present.
Figure 3.8: The configuration of the stack on entry to the segment not present exception handler.
When a not-present exception occurs, the CPU saves the context of the faulting code on the stack and pushes an error code which indicates which segment caused the fault, and under what circumstances. The exception is first handled by the operating system which then forwards it to the users exception handler. The resulting stack configuration is shown in figure 3.8. This configuration is unsuitable for a call to a high level language procedure, in that the CPU has only pushed the minimum state required to restart the application. The on entry to the handler most of the CPU registers contain the values that they had at the moment the fault occured, and there is every probability that these would be corrupted by a Pascal or C procedure. The first level fault handler has thus to be written in assembler to save the old registers, and to set up the data segment register correctly to ensure that the persistence manager will have access to its local variables.
The handler then checks to see if the segment which caused the fault is one for which it is responsible. If not control is passed back to the operating system to handle it. Otherwise, a call is made on the second level fault handler. This has to claim physical memory for the segment and load it from the disk file in which it is stored. The claiming of memory is done by the standard windows process of obtaining a handle to a global memory block of the appropriate size, fixing it, and obtaining the linear address of the block. The descriptor for the faulting selector is then set to point at this linear address, its limit is set to the size of the segment and the access rights set to present with read only access.
Any attempt to write to data in the segment causes a general protection fault (interrupt 13). By trapping this fault too, the object manager can selectively switch on write enable bits for segments that are modified. When segments are swapped back to disk, inspection of the write enable bit in the segment table allows one to determine if the segment has been modified and must be saved, or may simply be discarded.
Figure 3.9: The first level fault handler in assembler.