[comp.sys.ibm.pc] File handles in TSR's: A tip and a question

stuart@bms-at.UUCP (Stuart Gathman) (01/05/89)

First: A tip for writing TSR's in Turbo C 1.5 & 2.0.  The setjmp() and
longjmp() functions save ss:sp.  This allows easy context switching.
Non-trivial interrupt handlers usually need to switch to an internal stack
to avoid overflowing a caller's stack of unknown size.  (And so that
pointers to stack variables work as expected for a small model TSR.)
A handler plus main for small model looks something like this:
--------------------------
#include <setjmp.h>
#include <dos.h>

#define	VECT	0x7f
#define	DSPARAS	0x1000		/* 64K data segment */

static jmp_buf u_state, state;

void interrupt handler()
  unsigned ...,ax;
{
  _AX = setjmp(u_state);		/* save user stack */
  if (_AX == 0) longjmp(state,ax);	/* switch to internal stack */
}

main()
{
  void interrupt (*saveint)();
  int op;
  op = setjmp(state);
  if (op == 0) {
    /* should check if already installed, etc. . . . */
    saveint = getvect(VECT);
    setvect(VECT,handler);
    keep(_DS - _psp + DSPARAS);		/* TSR */
  }

  if (op != UNINSTALL) {
    doit(op);				/* do stuff */
    longjmp(u_state);			/* return to user */
  }

  /* uninstall */
  setvect(VECT,saveint);		/* restore vector */
  freemem(*(unsigned far *)MK_FP(_psp,0x2c));	/* free environment */
  freemem(_psp);				/* free program */
  longjmp(u_state);			/* return to user */
}
---------------
Nice & high level, huh?  The handler doesn't need to be interrupt for
huge model (assuming the caller doesn't need to save AX..DX).  The
interrupt form ensures that _DS is loaded with the local data segment.
This could also be accomplished with inline assembler as could pushing
only those registers not saved by setjmp().

NOW - my question is this:  is there any way to keep files open in a TSR?
DOS likes to close them whenever the last process loaded before they were
opened exits.  (Got that?)  Dos does not simply close all files whenever
any program exits.  It carefully keeps track of which files were opened
by which psp (apparently).  Unfortunately, TSR's don't count.  Is there any
way to fool DOS into saving TSR files also?  The alternative of keeping
an internal file descriptor table with qualified path names and replacements
for open/read/write/lseek/close doesn't appeal to me.

Strangely enough, file desciptors 0,1,2,3,4 are not closed even for TSR's.

Please reply by E-mail (in addition to posting if desired).  I read this
group, but can't keep up with the thousands of articles.
-- 
Stuart D. Gathman	<stuart@bms-at.uucp>
			<..!{vrdxhq|daitc}!bms-at!stuart>

dixon@sagittarius.steinmetz (walt dixon) (01/05/89)

File handles are an index into a process data structure called the Job
File Table (JFT for short).  The address of the JFT and usually the JFT
itself are located in the Program Segment Prefix (PSP).  Each JFT entry
contains either a System File Number (SFN) or 0xff indicating the entry
is unused.  The SFN is an index into a DOS maintained data structure
called the System File Table (SFT).  There is one SFT no matter how many
programs/TSR's are resident. (Actually DOS maintains a separate SFT for
FCB access).

The SFT entry records the file name (no path),  status information (EOF,
raw,  etc.),  file position (characters processed),  and a path to the
device driver.  Other information recorded here include the entry owner
(a PSP address) and a reference count.

When you perform a handle i/o operation DOS uses the handle as an index
into the JFT of the current PSP.  DOS maintains an internal global variable
which records the PSP address of the current process.  DOS int 21H AH=50h or
AH=62H return the current PSP address;  DOS int 21H AH=51h sets the current
PSP to the value contained in BX.

The main difference between the close call (int 21H ah=4ch) and the terminate
and stay resident call is that the former closes all files and releases
all Memory Control Blocks (MCBs) owned by the terminating process.  When
DOS processes the a close request,  it decrements the reference count in
the SFT.  When this count goes to 0,  the SFT entry is dealocated and the
file/device is finally closed.

The devices/files referenced by handles 0-4 are initially opened by command.com
and are inherited by each process.  So long as they are only closed once
and you don't mess with the JFT,  the SFT reference count will never go to
0 and the devices/files will never close.  (Only the SFT owner can cause
entry deallocation anyway).

TSRs should set the current PSP to their own PSP before trying to reactivate.
Before attempting any I/O the TSR must be sure that it is safe to do so.
Significant portions of DOS are not reentrant.  Making an int 21h request
at the wrong time can be disasterous.  You should also set up critical error
and break handlers before initiating an int 21h request from a reactivating
TSR.  Never try to use another programs JFT to perform I/O;  that is an
invitation for trouble.

TSRs are a very complicated subject.  Check out chapter 4 of the MS DOS
Developer's Guide (2nd edition) (H. Sams, 1988).  This chapter covers
TSRs in great detail.  NB I am the author of that chapter.  I get no
royalties;  I'm merely citing a good reference.


Walt Dixon		{ARPA:		dixon@ge-crd.arpa	}
			{US Mail:	ge-crd			}
			{		po box 8		}
			{		schenectady,  ny 12345	}
			{VOICE:		518-387-5798		}

Standard disclaimers apply