home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / C / MCB.ZIP / MCB.C < prev    next >
Encoding:
C/C++ Source or Header  |  1988-11-23  |  13.5 KB  |  311 lines

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