home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1988 / 11 / moore.asc < prev    next >
Text File  |  1988-10-21  |  14KB  |  319 lines

  1. MAPPING DOS MEMORY ALLOCATION_
  2. by
  3. Robert J. Moore
  4.  
  5.  
  6. [LISTING ONE]
  7.  
  8. /*
  9.   ====================  MCB.C  =======================================
  10.     This program chains through all the DOS memory control
  11.     blocks and computes and prints out information related to
  12.     each one.
  13.     R.J. Moore (C) 06 June 1988 Version 1.2. May be used freely
  14.     for non-commercial purposes only.
  15.     Compiled under Turbo C, Version 1.5, using the small memory
  16.     model, which itated the explicit declaration of some huge and
  17.     far pointers.  Tested on IBM PC, IBM XT, IBM AT, TP/286 AT
  18.     clone, under PC-DOS 2.1, 3.1, 3.2, and 3.3.  Also run under
  19.     MS-DOS 2.11 on a DEC Rainbow.  PC-DOS 3.3 required some
  20.     adjustments as discussed in later comments.
  21.     ==================================================================
  22. */
  23.  
  24. #include <stdio.h>
  25. #include <dos.h>
  26.  
  27. /*-------Global declarations----------------------------------------*/
  28. struct MCB             /*template for a one paragraph MS-DOS MCB    */
  29. {
  30.      char chain;       /* 'Z' for last MCB, 'M' for all others      */
  31.      unsigned  pid;    /* PSP segment for process owning the MCB    */
  32.      unsigned  psize;  /* Paragraphs of memory in the MB following  */
  33.      char unused [11]; /* Last 11 bytes of MCB (currently unused)   */
  34. };
  35.  
  36. typedef struct MCB huge *PTRMCB;    /*PTRMCB is a type declared
  37.                                       to be a huge pointer to MCB   */
  38.  
  39. /*-------Function prototypes----------------------------------------*/
  40. void main (void);
  41. void far *ffmcb (void);   /* Returns far pointer to first MCB.      */
  42. void prn_header (void);   /* Prints output table header.            */
  43. void prn_mcb (PTRMCB pm); /* Prints out MCB and related information.*/
  44.                           /* Prints out owner name string for pid.  */
  45. void prn_pid_own (unsigned pid,unsigned parent);
  46.  
  47. /*------main()------------------------------------------------------*/
  48. /* Executive to control finding a pointer to each MCB and directing
  49.    the printing out of information for each until the end of the
  50.    MCB chain is reached.                                            */
  51.  
  52. void main()
  53. {
  54.   PTRMCB ptrmcb;             /* ptrmcb is a huge pointer to an MCB  */
  55.  
  56.    /* Get pointer to first MCB. Note that ffmcb() returns a far
  57.       pointer, which is then cast to a huge pointer. A far pointer
  58.       is good enough to find the first MCB since a far pointer can
  59.       start at any memory location.  However, the use of this pointer
  60.       in ptrmcb must be huge because MCBs range over more than 64K,
  61.       which is all that a far pointer can handle since the segment
  62.       portion of a far pointer never changes. I, of course, found
  63.       this out the hard way.  Such special declarations can be
  64.       avoided if this program is compiled under the huge memory
  65.       model, but I think the method I used is more instructive.     */
  66.  
  67.   ptrmcb = (PTRMCB) ffmcb(); /* Get far pointer to first MCB and
  68.                                 cast to huge pointer via (PTRMCB).  */
  69.   prn_header ();             /* Print out table header to stdout.   */
  70.   prn_mcb(ptrmcb);           /* Print out information for first MCB.*/
  71.  
  72.     /* Print out MCB information for each of the remaining MCBs    */
  73.   do
  74.   {
  75.     ptrmcb += ptrmcb->psize + 1;   /* Get pointer to next MCB       */
  76.  
  77.     /* Each unit increment of ptrmcb corresponds to one paragraph;
  78.        adding ptrmcb->psize thus increments through entire allocated
  79.        memory block following the MCB. Since this doesn't include
  80.        space occupied by the MCB itself, must increment through one
  81.        more paragraph (+ 1) to point to the next MCB.               */
  82.  
  83.     prn_mcb(ptrmcb);               /* Print out information for MCB */
  84.   } while (ptrmcb->chain == 'M');  /* as long as not at end of chain*/
  85.                                    /*Print out final decoration.    */
  86.   printf ("========================================================");
  87.   puts   ("===========");
  88. }
  89.  
  90. /*------ffmcb()-----------------------------------------------------*/
  91. /* Returns a far pointer to the first MCB in memory.  Explict
  92.    declaration of far needed since small model was used to compile,
  93.    as noted in a comment in main.                                   */
  94.  
  95. void far *ffmcb(void)
  96. {
  97.   union REGS regs;        /* REGS and SREGS defined in dos.h.       */
  98.   struct SREGS sregs;
  99.   unsigned far *segmptr;  /*  Far pointer to segment address of MCB.*/
  100.   regs.h.ah=0x52;         /*  Undocumented MS-DOS function 52H.     */
  101.   intdosx(®s, ®s, &sregs);  /* ES:BX-2 points to segment
  102.                                      address of first MCB on return
  103.                                      and is copied to segmptr below.*/
  104.   segmptr=MK_FP(sregs.es,regs.x.bx-2);
  105.   return MK_FP(*segmptr,0);       /* Return pointer to MCB itself.  */
  106. }                                 /* Segment pointed to by *segmptr.*/
  107.                                   /* Offset is zero (on paragraph). */
  108.  
  109. /*-----------prn_header()-------------------------------------------*/
  110. /* Prints out header for the output variables describing the
  111.    information for each MCB which will be subsequently printed
  112.    out by the function prn_mcb().
  113. */
  114. void prn_header (void)
  115. {
  116.   printf ("===================================================");
  117.   puts   ("================");
  118.   puts   ("MCB MCB  ID PID      MB PAR- ENV  OWNER");
  119.   puts   ("NO. SEG            SIZE ENT  BLK?");
  120.   printf ("===================================================");
  121.   puts   ("================");
  122.  
  123. /*    MCB NO. = ordinal number of MCB being processed (1,2,...).
  124.       MCB SEG = segment address (hex) of memory control block.
  125.       ID      = chain id, 'Z' if last MCB, 'M' otherwise.
  126.       PID     = process id, the PSP segment address (hex) of owner of
  127.                 the MCB. (PSP always starts on paragraph boundary.)
  128.       MB SIZE = size of the allocated memory block controlled by
  129.                 the MCB (the MB immediately follows its associated
  130.                 MCB at the next paragraph in memory (decimal bytes).
  131.       PARENT  = segment address (hex) of parent process's PID.
  132.       ENV BLK?= 'Y' if the MCB controls an environment block,
  133.                 'N' otherwise.
  134.       OWNER   = string that prints out program associated with
  135.                 the PID.
  136. */
  137. }
  138.  
  139. /*------prn_mcb()---------------------------------------------------*/
  140. /* Prints out the information associated with the MCB pointer passed
  141.    to it in its argument list
  142. */
  143.  
  144. void prn_mcb (PTRMCB pm)
  145. {
  146.   static cnt = 0;          /* Count of number of times parent has
  147.                               been equal to the pid.                */
  148.   static mcbnum = 1;       /* Ordinal # of MCB being printed out.   */
  149.   unsigned parid;          /* Parent id (segment address of parent
  150.                                process).                            */
  151.   unsigned mcbseg;         /* Segment address of MCB (offset is
  152.                                always zero since paragraph aligned).*/
  153.   char envf;               /*Set to 'Y'/'N' if MB is/is not an
  154.                                environment block.                   */
  155.   unsigned envseg;         /*Segment address of pid's environment
  156.                                block.                               */
  157.  
  158.    /* Get parent id located at pid:16H                              */
  159.   parid = * (unsigned far *) MK_FP (pm->pid,0x16);
  160.  
  161.   mcbseg = FP_SEG (pm);    /* Segment address of the MCB            */
  162.  
  163.   envseg = * (unsigned far *) MK_FP(pm->pid,0x2C);  /* segment      */
  164.                            /* Address of pid's environment          */
  165.                            /* located at pid:2CH.                   */
  166.  
  167.     /* If the MCB segment value plus one equals the environment
  168.        segment address, then the MCB controls the environment
  169.        block (set envf = 'Y'); otherwise set envf = 'N'             */
  170.  
  171.   envf = mcbseg+1 ==  envseg ? 'Y' : 'N';
  172.  
  173.     /* Count the number of times parent and pid have been equal
  174.        (when this is true, memory blocks are owned by COMMAND.COM   */
  175.  
  176.   if (parid == pm->pid) cnt++;
  177.  
  178.     /* The above determination of whether an MB is an environment
  179.        block isn't complete for DOS versions 2.0 thru 3.2.  The
  180.        above logic will not identify the master environment block
  181.        owned by the master copy of COMMAND.COM since the value at
  182.        pid:2CH contains zero, not the segment address of the master
  183.        environment.  The logic below uses the fact that the master
  184.        environment follows the master COMMAND.COM in memory. (The
  185.        environment copies for other programs are in memory BEFORE
  186.        the pid they are associated with.) Starting with DOS 3.3
  187.        pid:2CH always points to the environment, even for the
  188.        master COMMAND.COM, so the following is not needed (but it
  189.        doesn't do any harm).                                         */
  190.  
  191.   if (!envseg && cnt == 2) envf = 'Y';
  192.  
  193.     /* Print out MCB information except for owner name in the
  194.        following call to printf().                                  */
  195.  
  196.   printf("%2.2u%06.4X%2.1c%06.4X%7lu%5.4X %-5.1c",
  197.    mcbnum++,mcbseg,pm->chain,pm->pid,(long) pm->psize*16,parid,envf);
  198.  
  199. /* Call prn_pid_own() to find and print out owner string            */
  200.  
  201.   prn_pid_own(pm->pid,parid);
  202. }
  203.  
  204. /*------prn_pid_own()-----------------------------------------------*/
  205. /* Prints out owner name string associated with the pid, which is an
  206.    input parameter.  Also needs the parent address as an input to
  207.    identify cases where COMMAND.COM is the owner (true when
  208.    pid=parent). This function also uses the fact that the following
  209.    pid values are special:
  210.  
  211.    pid = 0 means that MCB is a free block of memory
  212.    pid = 8 means that the MCB is owned by IBMDOS.COM/MSDOS.SYS
  213.    pid = parent means that the MCB is owned by COMMAND.COM (the only
  214.          program that is its own parent.)
  215.  
  216.    In these cases I assign appropriate owner string names instead of
  217.    getting them from the environment since they are not available
  218.    there.  Owner names consisting of a string with the drive, path,
  219.    and file name of the program that owns the memory are only
  220.    available in DOS 3.x. Note that DOS 3.3 does not provide an owner
  221.    string for the master copy of COMMAND.COM for some reason.  This
  222.    is of no consequence in the method used here.
  223. */
  224.  
  225. void prn_pid_own (unsigned pid,unsigned parent)
  226. {
  227.   unsigned far *envsegptr;  /* Pointer to seg address of environment*/
  228.   char far *envptr;         /* Pointer to pid's environment         */
  229.   unsigned far *envsizeptr; /* Pointer to envsize word below        */
  230.   unsigned envsize;         /* Size of pid's environment            */
  231.  
  232.   /* Ordinal # of copy of COMMAND.COM in memory (ccnum=1 for master
  233.      copy, 2 for first secondary copy (if any), etc.                */
  234.  
  235.   static unsigned char ccnum = 0;
  236.  
  237.   /* Pid value saved from previous call to this function.
  238.   Initialized to an impossible value (no PSP could start at FFFF:0) */
  239.  
  240.   static prev_pid = 0xFFFF;
  241.  
  242.   switch (pid)
  243.   {
  244.        /* Assign owner names for two special cases                  */
  245.      case 0 : puts ("FREE MEMORY CONTROL BLOCK");return;
  246.      case 8 : puts ("IBMDOS.COM/MSDOS.SYS");return;
  247.   }
  248.  
  249.   /* pid:2CH contains ptr to segment address of pid's environment   */
  250.   envsegptr = (unsigned far *) MK_FP (pid,0x2C);
  251.  
  252.   /* Get pointer to the environment block itself                    */
  253.   envptr = (char far *) MK_FP (*envsegptr,0);
  254.  
  255.   /* Define a pointer that contains the size of the environment
  256.      block. Must point back one paragraph (where the environment's
  257.      MCB resides) plus three bytes forward (where the MCB block
  258.      size field is).                                                */
  259.   envsizeptr = (unsigned  far *) MK_FP(*envsegptr-1,0x3);
  260.  
  261.   /* Get the size of the environment using the above pointer in
  262.      units of bytes (1 paragraph = 16 decimal bytes).               */
  263.   envsize = *envsizeptr*16;
  264.  
  265.     /* If next stmt is satisfied, owner is a copy of COMMAND.COM    */
  266.  
  267.   if (pid == parent)
  268.   {
  269.     /* If previous pid is different from current pid, have found a
  270.        new secondary copy of COMMAND - ccnum keeps track records the
  271.        copy number.                                                 */
  272.  
  273.     if (prev_pid != pid) ccnum++;
  274.     printf ("COMMAND.COM COPY #%-2u\n",ccnum);
  275.  
  276.     prev_pid = pid;        /* Save current pid - will be previous   */
  277.     return;                /*  in the next call to this function    */
  278.   }
  279.  
  280.   /* Loop at most until the end of the environment                  */
  281.  
  282.   while (envsize)
  283.   {
  284.       /* Decrement counter (envsize) indicating # of bytes left in
  285.          environment and advance pointer thru environment block until
  286.          either end of environment or a NULL is located
  287.       */
  288.     while (--envsize && *envptr++);
  289.  
  290.       /* The next stmt will be true if another NULL immediately
  291.          follows the first one located and a word count of 0001 then
  292.          follows that.                                              */
  293.  
  294.     if (!*envptr && *(unsigned far *) (envptr+1) == 0x1)
  295.     {
  296.        envptr +=3;           /* Correct pattern found (00 00 01 00) */
  297.        break;                /* so point envptr to owner string     */
  298.     }
  299.   }
  300.  
  301.   if (envsize)
  302.   {
  303.      /* If an owner string was found before the end of the
  304.          environment so print out the owner name.  Note that can't
  305.          use puts() or printf() to print out the results since I
  306.          used the small memory model.
  307.       */
  308.     while(*envptr) putchar(*envptr++);
  309.     putchar('\n');
  310.   }
  311.   else
  312.      /* If reached the end of the environment without finding
  313.         an owner string (should only occur for DOS 2.x)             */
  314.     puts ("UNKNOWN OWNER");
  315. }
  316.  
  317.  
  318.  
  319.