home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / pascal / rehack / text / memory.txt < prev    next >
Encoding:
Text File  |  1993-06-29  |  14.6 KB  |  367 lines

  1. File:            MEMORY.TXT
  2. Path:            ...\REHACK\TEXT\MEMORY.TXT
  3. Version:        0.01
  4. Author:            Pat Reilly
  5. CIS Id:            71333,2764
  6. Created On:        6/29/93
  7. Modified On:
  8. Description:     Info on MEMORY classes for REHACK.
  9. Tabs:            4
  10.  
  11.  
  12. Class Hierarchy
  13. ===============
  14.  
  15. VMemManager
  16.  
  17. MemoryObject --+-- ConvMemory
  18.                |
  19.                +-- EmsMemory
  20.                |
  21.                +-- XmsMemory
  22.                |
  23.                +-- DiskMemory
  24.                |
  25.                +-- UmbMemory (used internally by XmsMemory)
  26.  
  27. Description
  28. ===========
  29.  
  30.     The classes in MEMORY.HPP form the basis for a virtual memory management
  31. system for REHACK. Because memory is virtual, it has to be loaded into
  32. conventional (0..1MB) space for your program to manipulate it. Because of
  33. this, you need to "lock" an object to get its address, and "unlock" it when
  34. you're done - the unlocking is so that you don't run out of places in
  35. conventional memory for buffers.
  36.  
  37. Enum MemType
  38. ============
  39.  
  40.     This enumerates the memory types; its returned by an object's overriden
  41. virtual type() method, and is used in the VMemManager class (see below).
  42. MemTypes currently are:
  43.  
  44.     MemUnknown
  45.         The "unknown" type - currently, UMB memory objects return this type,
  46.         since they are designed to only be used internally by the XmsMemory
  47.         class.
  48.  
  49.     MemConv
  50.         ConvMemory object type - uses the program's heap space.
  51.  
  52.     MemEMS
  53.         EmsMemory object type - uses EMS memory (v3.0 and above).
  54.  
  55.     MemXMS
  56.         XmsMemory object type - uses XMS memory (v2.0 and above).
  57.  
  58.     MemDisk
  59.         DiskMemory object type - uses a temporary disk file.
  60.  
  61. Class VMemManager
  62. =================
  63.  
  64.     This class is extrememly simple - it just allows you to set a 'strategy'
  65. for the order in which to try to allocate memory (use EMS first? etc) and
  66. then just walks through the strategy returning the first MemoryObject type
  67. that successfully allocted your memory request. So you can use a "fastest
  68. first" strategy that would try allocaing in the following order: Conventional,
  69. EMS, XMS, Disk. Or you might want a strategy that is non-disk; in this case,
  70. the list wouldn't have MemDisk in it.
  71.     An example of using the first strategy is:
  72.  
  73. MemType fastAllocStrategy[] =
  74.     { MemConv, MemEMS, MemXMS, MemDisk, MemConv, MemUnknown };
  75.  
  76. void setFastestStrategy()
  77. {
  78.     int n = 0;
  79.     do    VMemManager::allocScheme[n] = fastAllocStrategy[n];
  80.     while(fastAllocStrategy[n++] != MemUnknown);
  81.     // Alternatively:
  82.     //    memcpy(VMemManager::allocScheme, fastAllocStrategy, 5*sizeof(MemType));
  83. }
  84.  
  85. int main()
  86. {
  87.     setFastestStrategy();
  88.     MemoryObject* mem = VMemManager::allocate(sizeof(dword)*1000);
  89.     dword FAR* ptr;
  90.     if(mem != 0 && (ptr = (dword*) mem->lock()) != 0)
  91.         {
  92.         // can now access the memory as a 1000-element dword array.
  93.         ...
  94.         mem->unlock();
  95.         }
  96.     ...
  97.     delete mem;
  98. }
  99.  
  100.     For fastest order without using disk memory, you could do the identical
  101. code, but change the fastAllocStrategy[] array to not include MemDisk.
  102.  
  103.  
  104. Class MemoryObject
  105. ==================
  106.  
  107.     This is the base class for memory object classes. Almost all of its
  108. methods are pure, so you cannot create an instance of MemoryObject. The
  109. following definitions describe what a derived class must perform for each
  110. method in MemoryObject:
  111.  
  112. Methods
  113. -------
  114.  
  115.     MemType type()            virtual
  116.         This is a type identifier method. If a new memory class is derived
  117.         from MemoryObject, then the MemType enum needs to have a new entry
  118.         for this memory type, and VMemManager needs to have its allocScheme
  119.         array increased in size, as well as code modification.
  120.  
  121.     bool allocate(word sz)    pure virtual
  122.         A MemoryObject must only attempt allocating memory when it is not
  123.         currently in use (previous successful call to allocate()); if the
  124.         object already has allocated some memory, this method must return
  125.         false. Otherwise, the class must try to allocate <sz> bytes and
  126.         return true on success, or false on failure. Note that the object
  127.         should NOT lock the memory - just allocate it.
  128.  
  129.     void free()                pure virtual
  130.         Object should do nothing if it currently has locks (see lockFlag);
  131.         otherwise, it should free any memory previously allocated by a call
  132.         to allocate().
  133.  
  134.     void FAR* lock()        pure virtual
  135.         If the object has no memory allocated (see allocate()), then must
  136.         return 0. If the object already has locks in place, must return the
  137.         SAME address that previous calls to lock() returned (IE don't go
  138.         locking the data in different buffers). Otherwise, must attempt to
  139.         place the data in conventional memory (0..1MB) in such a manner that
  140.         either the various MemoryObject classes, or the compiler's heap
  141.         manager, know about it, and return a pointer to it. If MemoryObject
  142.         classes are used as the buffer, then note that only ConvMemory is
  143.         guaranteed to be able to lock() a successful allocation (and the
  144.         internal UmbMemory class of XmsMemory) - all others (including EMS)
  145.         might be able to allocate, but then unable to lock(), a buffer.
  146.         If a buffer is successfully created, this function must copy its
  147.         data into the buffer and return the buffer address - also, it MUST
  148.         increment the lockFlag member. On failure, return 0.
  149.  
  150.     void unlock()            pure virtual
  151.         If the data is not locked, perform nothing. Otherwise (lockFlag != 0)
  152.         then you must decrement the lock flag: lockFlag--. If it still has
  153.         locks on it (lockFlag != 0), then just return. Otherwise you must
  154.         copy (if necessary) the data from the buffer to its normal location
  155.         and free the buffer.
  156.  
  157.     long memAvail()            pure virtual
  158.         Should return the total amount of unused memory for that class. Note
  159.         that for a class like DiskMemory, it can be misleading to just
  160.         include all the remaining disk space, since some memory is required
  161.         by the operating system, and the program might be writing/deleting
  162.         other files on that disk.
  163.  
  164.     long maxAvail()            pure virtual
  165.         Should return the largest single block that is available for that
  166.         memory type. Since allocate() uses a word (unsigned 16-bit) for
  167.         allocation sizes, this is NOT the maximum block that can be allocated;
  168.         its the maximum contiguous area free for that data type. Again, the
  169.         same caveats apply as for memAvail().
  170.  
  171.     word memSize()
  172.         Returns the size of the memory allocated by this object, or 0 if
  173.         none has been allocated.
  174.  
  175. Members
  176. -------
  177.  
  178.     word memsize
  179.         Size of this object's allocated memory. Must be maintained by the
  180.         derived class.
  181.  
  182.     word lockFlag
  183.         Number of locks on the object's data. Must be maintained by the
  184.         derived class' overridden lock() and unlock() methods.
  185.  
  186.  
  187. Class ConvMemory
  188. ================
  189.     Derived from MemoryObject
  190.     Source: CONV.CPP
  191.  
  192.     ConvMemory is a MemoryObject whose data is stored in the program's heap
  193. space (allocated through a call to global new() operator). It has the benefit
  194. of ALWAYS being able to be locked() (since its own data is used as a buffer)
  195. but is not overly useful.
  196.     This class adds a "safety pool" to its heap usage. For instance, if you
  197. need to make sure that 10K of the heap is never touched by allocating
  198. MemoryObjects (perhaps to ensure that other classes, such as XMS or Disk, can
  199. always be locked by creating ConvMemory buffers) then you can just set the
  200. static memPoolSize member to 10K, and ConvMemory will fail any calls to
  201. allocate() that leave less than 10K as the maxAvail() conventional memory.
  202. For example: the following code will (try) and allocate 800K of any type
  203. memory (using VMemManager's current allocation strategy) as 80 blocks of
  204. 10K each - it will ALSO ensure that, as long as you only lock() one block at
  205. a time, it will always pass:
  206.  
  207.     ...
  208.     MemoryObject* mem[80];
  209.     int n;
  210.     ConvMemory cmem; // just for using its maxAvail() function.
  211.  
  212.     if(cmem.maxAvail() < 10016)    // extra 16 is for heap manager
  213.         ... fail operation - can't guarantee 10000 buffer can be made.
  214.  
  215.     ConvMemory::memPoolSize = 10016;
  216.     for(n = 0; n < 80; n++)
  217.         if((mem[n] = VMemManager::allocate(10000)) == 0)
  218.             ... fail.
  219.     ConvMemory::memPoolSize = 0;
  220.  
  221. Now mem[0..80] can be lock()'d with success (as long as you don't allocate
  222. any more memory from the heap or with ConvMemory) because we've guaranteed
  223. that there's enough heap space to create a ConvMemory of 10K, and all the
  224. Memory classes that don't have access to conv memory (XMS, Disk) will try
  225. using a ConvMemory as their buffer on a call to lock().
  226.  
  227. Class EmsMemory
  228. ===============
  229.     Derived from MemoryObject.
  230.     Source EMS.CPP
  231.  
  232.     EmsMemory attempts to use the systems EMS memory for allocation. By
  233. default, EmsMemory will attempt to use ALL available EMS memory. You can
  234. force EmsMemory to use less by modifying the static variable MaxEmsPages
  235. in the file EMS.CPP. If this value is less than 4 (need 4*16K as a minimum)
  236. then EmsMemory tries to allocate ALL EMS. If this value is >= 4, then it
  237. will only allocate MaxEmsPages pages from EMS; each page is EmsPageSize in
  238. size (16K - defined in MEMORY.HPP).
  239.     Many EMS memory systems just use EMS' built-in 16K page manager; this
  240. means that the minimum allocation is 16384 bytes. Instead, EmsMemory
  241. allocates MaxEmsPages (or all available pages) and considers this as a
  242. contiguous block of memory. So if it allocated 100 pages, then EmsMemory
  243. considers that it has one long block of 16384*100 = 1,638,400 bytes. This
  244. eliminates a lot of wasted memory.
  245.     When allocating into this space, EmsMemory uses a first-fit strategy. So
  246. if the EMS memory looked like:
  247.  
  248.     +--------------+-------------+--------+------
  249.     |   In use     |  Available  | In use | ...
  250.     +--------------+-------------+--------+----
  251.     Page 0
  252.  
  253. and "Available" was 14K in size, and you allocate(300), then the memory
  254. would look like:
  255.  
  256.     +--------------+---+---------+--------+------
  257.     |   In use     |   |Available| In use | ...
  258.     +--------------+---+---------+--------+----
  259.     Page 0           ^
  260.                   Block just allocated.
  261.  
  262.   This scheme makes the most use of EMS memory, but had side effects. Since
  263. we're storing the linear offset of the block, we have to use a long instead
  264. of a page number (word) - this is 2 extra bytes per EmsMemory instance. Also,
  265. the EMS manager only works on page numbers (every 16K block), so we have to
  266. convert this linear offset into a page number + (word) offset.
  267.  
  268.     EMS memory is not directly accessible by the computer. In order to access
  269. that 300-byte block you just allocated, that area has to be moved so that the
  270. CPU can address it. What EMS does is to have a 64KB region of conventional
  271. memory in which is 'maps' its pages into one of 4 "frame" pages which the
  272. computer can access. So somewhere above 640K, the EMS manager has 64K set
  273. aside that is its frame buffer, which is can be accessed as 64K contiguous
  274. conv memory, but which actually modifies the page which you mapped into it.
  275. This is actually kinda goofy <g>; for instance, you could map EMS page 0 into
  276. frame page 0, EMS page 2 into frame page 1, EMS page 4 into frame page 2, and
  277. EMS page 47 into frame page 3. To your program, they all just look like a
  278. 64K block of memory; but if you then swapped frame page 1 and 0, it would
  279. be as if you 'swapped' the second 16K with the first 16K!
  280.     EmsMemory uses this fact to its advantage. For instance, if you have one
  281. allocation block which fits into 2 16K pages, one which fits into one page,
  282. and another that fits into one page, then all 4 blocks can be lock()'d at
  283. the same time. Note that I said "fit into x pages"; this does NOT mean that
  284. their SIZE is <= 16K. For example, the above 300 byte allocation might have
  285. occurred such that, page-wise, EMS memory looks like:
  286.  
  287.  
  288.     |    16K   |    16K   |    16K   |    16K   |    16K   |
  289.     +--------------+---+---------+--------+------
  290.     |   In use     |   |Available| In use | ...
  291.     +--------------+---+---------+--------+----
  292.     Page 0           ^
  293.                   Block just allocated.
  294.  
  295. If I "blow up" the area around the allocation block itself, it looks like:
  296.  
  297.     ------+-------------+-------------
  298.           |  300 bytes  | Available...
  299.     ------+---------+---+------------
  300.                     |
  301.        ...16K PageN |  16K Page(N+1) ...
  302.  
  303. Now to load the full 300-byte block into the frame buffer, I have to load
  304. both PageN AND Page(N+1):
  305.  
  306.     +---------------+---------------+---------------+---------------+
  307.     |   Page0       |   Page1       |   Page2       |   Page3       |
  308.     +---------------+---------------+---------------+---------------+
  309.     |  EMS PageN    | EMS Page(N+1) |
  310.  
  311.              | 300 bytes |
  312.  
  313.              ^
  314.     Pointer returned by lock().
  315.  
  316.   For searching purposes, EmsMemory objects form a sorted linked list; the
  317. class has a static pointer to the head of the list. Whenever a new EmsMemory
  318. allocates memory, its added to the list in the proper location; when it
  319. successfully free()s its memory, its removed from the list. The sort order
  320. for the list is by the linear "address" of its memory block. This is somewhat
  321. similar to the tagged boundary heap management method, but the tag info is
  322. contained in the object, instead of the EMS memory block.
  323.  
  324.  
  325. Class XmsMemory
  326. ===============
  327.  
  328.     This class operates very much like the EmsMemory class, except that XMS
  329. memory (driver v2.0 and above) is used as the data storage location, not
  330. EMS memory. One disadvantage with XmsMemory is that, unlike EMS, XMS memory
  331. does not have a "frame buffer" in conventional memory space. This means that
  332. to lock() an XMS memory block, memory has to come from somewhere in the
  333. conventional memory space. On 386 machines, where UMBs are available, the
  334. XmsMemory class will try to lock() by allocting a UmbMemory object (internal
  335. to XMS.CPP) - if successful, then the XMS memory is copied here and you
  336. don't have to use heap space for the buffer. If no more UMBs are available,
  337. them XMS tries ConvMemory and EmsMemory to allocate and lock() a buffer.
  338.  
  339. Class DiskMemory
  340. ================
  341.  
  342.     This class also is much like the EmsMemory class, but it uses a temporary
  343. disk file as its storage location. You can change the drive/path used for
  344. the temp file (default is current drive/directory) by strcpy() to the
  345. external string DefaultDiskMemDir. For example, if you have a ramdisk set up
  346. as F drive, then you would:
  347.  
  348.     extern char DefaultDiskMemDir[];
  349.     ...
  350.     strcpy(DefaultDiskMemDir, "F:");
  351.  
  352. This MUST be done before ANY DiskMemory objects are instantiated (their ctor
  353. run), since this file is opened the first time any DiskMemory::DiskMemory()
  354. runs. Like XmsMemory, DiskMemory has no default "frame buffer" so a buffer
  355. has to be allocated to lock() an object. By default DiskMemory::lock() will
  356. first try to allocate and lock() a ConvMemory; on failure, it will then try
  357. to allocate and lock a EmsMemory. You can swap the order of this (try EMS
  358. first) by changing the external int DiskFrameOrder to non-zero:
  359.  
  360.     extern int DiskFrameOrder;
  361.     ...
  362.     DiskFrameOrder = 1;    // Now will try EMS before Conventional.
  363.     ...
  364.     DiskFrameOrder = 0;    // Now will try Conventional before EMS (default).
  365.     ...
  366.  
  367.