?? mmarch.doc
字號:
This initial task actually becomes the MMURTL monitor program which is always queued to run back there somewhere. It serves several purposes which are out of the scope of memory management. It's discussed a little later in this section.
-------------------------------------------------
OS Initialization
Initial OS Structures
When the OS is first loaded, it is placed in physical memory at address 0. The data segment for the OS is located at low physical memory (address 00000000h). At this point, physical memory is the same as linear memory (we're not in paged mode yet). In fact, your initial code and data address MUST be the same when transitioning in and out of paged memory or the Great Pumpkin falls on you (yes, a very messy crash). The OS starts off with the following initial structures (these may change slightly between versions, but the concept will not change):
Important tables:
Interrupt Descriptor Table (IDT) - 2Kb at 00000000h Physical
Global Descriptor Table (GDT) - 6Kb at 00000800h Physical
Initial Page Directory (PD) - 4Kb at 02000h Physical
Initial Page Table (PT) - 4Kb at 03000h Physical
Other important structures:
Static Task State Segments for Monitor and Debugger (TSSs)
Static Job Control Blocks for Monitor and Debugger (JCBs)
Array of Link Blocks (LBs)
Array of Service Descriptors (SVCs)
Initial Static Exchanges (EXCHs)
Note that several structures are defined as static. This is because they are used to get the OS up and running. The bulk of these structures are allocated dynamically in the OS address space after memory management is initilized.
Other junk:
pRunTSS -- Pointer to the Running TSS
RdyQ -- Ready Queue for Tasks ready to Run
pFreeJCB -- Ptr to pool of free Job Control Blocks
pCrntJCB -- Ptr to current Job Control Block
nRegistered -- Number of Registered Services
Page Allocation Map (PAM) -- 2 Kb to manage 64Mb of memory
Still more junk:
oMemMax Last byte of addressable physical memory
GDTLimit Global Descriptor Table Limit
GDTBase GDT base address
IDTLimit Interrupt Descriptor Table Limit
IDTBase IDT base
TimerTick A DWord counting the ticks of the clock
nTmrBlksUsed Number of timer blocks in use
Array of Timer Blocks (rgTmrBlks)
There's still more junk, but we'll stop because you get the idea. Look at the code to see what's really there.
The OS initializes most of those arrays, and sets up it's initial task, then it initializes it's memory space by setting bits in the PAM and making entries in it's page table (it's only got one PT at this point). Then it goes into PAGED MEMORY MODE. It never comes back into physical address mode. If we do it wrong, it never comes back at all (reset buttons are really useful when developing an operating system).
Once memory management functions are working we allocate several pages for dynamic structures (more TSSs and JCBs etc.)
The Page Directory (PD) that was initially allocated to the OS will become the PD for the OS Monitor program. It will hold all the PTs for OS shared code.
---------------------------------------------------------
The MMURTL Monitor Program
The OS Monitor program does the following things:
1) Initializes the Keyboard Service, floppy & hard disk device drivers, default comms device drivers, and the file system.
2) Allows the user to execute the command line interpreter (CLI). This creates a new job with its own virtual video.
3) Displays system errors (such as why your program was shut down and thrown out)
4) Provides performance monitoring.
Loading Things Into Memory
Applications, System Services, Device drivers, and DLLs, must all be loaded into memory somewhere.
Each application (job) gets it own PD. This is allocated along with the new Job Control Block (JCB). It also get as many pages as it needs for it's code, intial stack, and data. It's loaded into these initial pages. Message based system services are exactly like applications from a memory standpoint. They are simply new jobs.
Device Drivers have code and data, but no stack. They become part of the OS and are reached through the standard entry points (in the call gates). They are actually loaded into the operating system's memory space with freshly allocated pages. They become part of the OS in the OS address space (accessable to everyone).
Dynamic Link Libraries are the weird ones. They have only code (no data, and no stack). They are also loaded into OS address space, but the pages they occupy are marked executable by user level code. This means they can be accessed from the user's code with near calls. This also means that the loader must keep track of the PUBLIC names of each call in a DLL and resolve references to them after we load the applications that call them. But this gets away from memory management.
OSs page tables are aliased as the first tables in each job's PD and marked as supervisor. PDs and PTs are always resident (no page swapper yet, how could they be swapped?). This is 8Kb of initial memory management overhead for each job. It will still only be 8Kb for a 4Mb application.
Messaging and Aliases
An "alias" address is actually just a shared PTE. If two jobs need to share memory (as they do when using the Request/Respond messaging), the kernel copies a PTE from one job's PT to another Jobs PT. Instantly the second job has access to other jobs data. They "share" physical memory. Of course they probably won't be at the same linear address which means we have to fixup a pointer or two in the request block, but that's trivial (two or three instructions).
There is no new allocation of physical memory, and the service doesn't even know that the pages don't actually belong to him as they "magically" appear inside his linear address space. Of course, If he tries to deallocate them an error will occur. Paging makes messaging faster and easier. A PTE that is aliased is marked as such and can't be deallocated or swapped until the alias is dissolved. Aliasing will only occur for certain OS structures and messaging (other than the aliased page tables in the users Page Directory for the OS code and data).
Memory and Pointer Management for Messaging
If you haven't read the chapter on messaging, you should do it before you read this section. This will make more sense if you do.
When an application "Requests" a service, the kernel allocates a Request Block from the OS and returns a handle identifying this request block to the caller. This request block is allocated in OS memory space, but at the user's protection level so the service can access it.
The user's two pointers (pData1 and pData2) are aliased into the services memory area and are placed in the request block.
The memory aspects of the Request/Respond messaging process work like this:
1) The caller makes a request
2) The Request primitive (on the caller's side):
- Allocates a request block
- Returns a request handle to the caller
- Places the following into the RqBlk:
linear address of pData1 (if not 0)
linear address of pData2 (if not 0)
sizes of the pData1 and 2
pointer to caller's Job Control Block
Service Code
Response Exchange
dData0 and dData1
- Places a message on the Service's exchange with the
Request handle in it
- Schedules the service for execution
- Reevalutes the Ready Queue (switching tasks if
needed)
3) The Wait primitive (on the service's side):
- Adds aliases to the service's Page Table(s) for
pData1 and 2
- Places aliased linear addresses into RqBlk
- Returns the message to the service
4) The service does it's thing using the aliased pointers (reading & writing data to the caller's memory areas). When it's done it work, it Responds.
5) The Respond primitive (on the service's side):
- Removes the aliased memory from the service's PTs
- Places the message on the caller's response exchange
- Reevaluates the Ready Queue (switch tasks if needed)
6) The wait primitive (back on the caller's side):
- Simply passes the response message to the caller
Enough on memory management. You know as know as much as I do at this point.
--------------- END OF ARCHITECTURE CHAPTER ---------------
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -