home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
200-299
/
ff236.lzh
/
Proc
/
proc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-08-09
|
9KB
|
283 lines
/* :ts=8 bk=0
*
* proc.c: Illustration of how to create a full-fledged DOS process
* without needing to LoadSeg() it first. Based on an idea
* presented at BADGE (don't remember the name of the guy
* who thought it up).
*
* Leo L. Schwab 8903.01
*/
#include <exec/types.h>
#include <libraries/dosextens.h>
extern void *AllocMem(), *CreatePort(), *GetMsg(), *FindTask();
extern LONG Open(), Output(), CreateProc();
/*
* Cursor manipulation strings.
*/
char fwdcursor[] = "\033[C";
char backcursor = '\b';
/*
* Buried deep in the AmigaDOS Technical Reference Manual, the structure of
* of memory lists and SegLists are described. A SegList consists of a BPTR
* to the next element in the list (NULL-terminated), followed by code.
* The pointers point to the NextSeg field. The size of the node in bytes
* is stored in the longword just before NextSeg field. However, the
* size is only of real interest to UnLoadSeg(), which you won't be calling
* in this case. So we don't need it.
*
* So the PhonySegList structure consists merely of a NextSeg field, which
* is initialized to NULL (CreateProc() ignores this field, anyway), followed
* by code to execute, which CreateProc() will jump to. The code we give it
* to execute is an absolute jump to the real entry point, which you
* initialize appropriately.
*/
struct PhonySegList {
BPTR psl_NextSeg; /* BPTR to next element in list */
UWORD psl_JMP; /* A 68000 JMP abs.l instruction */
LONG (*psl_EntryPoint)(); /* The address of the function */
};
/*
* This is NOT the structure that will actually get passed to CreateProc().
* AmigaDOS demands that everything, including SegLists, be on longword
* boundaries. Short of compiler-dependent switches, the only way to
* guarantee correct alignment is to AllocMem() a PhonySegList structure,
* and copy this template into it. You should also remember to initialize
* the psl_EntryPoint field (the argument to the JMP instruction) in the
* allocated structure, for obvious reasons.
*/
struct PhonySegList template = {
NULL, /* No next element. */
0x4EF9, /* JMP abs.l */
NULL /* Argument for JMP instruction */
};
/*
* This is the routine that will be CreateProc()ed. Due to the global nature
* of library base variables, any library routines this routine calls will
* be using library base variables that, strictly speaking, belong to the
* "parent" process. Despite the fact that it will work, this is
* nevertheless a dangerous practice. In an ideal world, you would want to
* convince the linker to resolve all library base pointer references to
* your co-process's own copies. Lattice, on the other hand, due to its
* #pragmas, may not have this problem (can someone confirm this?).
*/
LONG
coprocess ()
{
register int i, n;
struct Process *me;
struct Message *startupmsg;
LONG doswin;
char buf[256];
#ifdef AZTEC_C
#ifndef _LARGE_DATA
/*
* This gets to be a bloody nuisance.
*/
geta4 ();
#endif
#endif
/*
* Startup messages are important. Not only can they provide
* configuration information for the newly-created process (what
* directory you're in, what size to make your window, etc.), but
* they also serve to inform the program that started you that
* you have finished executing. This is important, because the
* parent program will want to know when it's okay to free the
* resources it allocated to start you. In this example, we
* grab the message so that we can reply it. The act of replying
* the startup message will inform the parent that we're done.
*/
me = FindTask (NULL);
WaitPort (&me -> pr_MsgPort);
startupmsg = GetMsg (&me -> pr_MsgPort);
/*
* Recall that, because a global DOSBase pointer has been initialized
* by the parent, and because the stubs will reference it at the link
* stage, we don't need to open dos.library here.
*/
if (!(doswin = Open ("CON:0/0/320/100/Sub-Process", MODE_NEWFILE)))
goto xit;
/*
* Say hello.
*/
Write (doswin, "This is the child speaking.\n", 28L);
/*
* Print the value of FindTask(NULL) to prove we're a separate
* task, and print some values in the Process structure to prove
* we're a real process.
*
* (Another caveat: The stdio functions in this example (like
* sprintf()) are being shared by both processes. Some stdio
* routines utilize a set of global variables, access to which is
* not arbitrated. Therefore, it is possible for the processes to
* collide when calling stdio functions, causing Bad Things to
* happen. Be aware of this when doing your own multiprogramming.
* (I'm pretty sure sprintf() is safe.))
*/
sprintf (buf, "I'm at 0x%lx\n", me);
Write (doswin, buf, (LONG) strlen (buf));
sprintf (buf, "My stack size appears to be\n%ld bytes.\n",
me -> pr_StackSize);
Write (doswin, buf, (LONG) strlen (buf));
sprintf (buf, "pr_StackBase is 0x%lx\n", me -> pr_StackBase);
Write (doswin, buf, (LONG) strlen (buf));
/*
* Make the cursor in the window zot back and forth, which will
* happen concurrently with the same action in the CLI window,
* proving beyond a shadow of a doubt that there really are two
* separate programs running.
*/
for (n = 20; n--; ) {
for (i = 35; i--; )
Write (doswin, fwdcursor, sizeof (fwdcursor) - 1L);
for (i = 35; i--; )
Write (doswin, &backcursor, 1L);
}
/*
* We've proved our existence. We now reply our startup message,
* and exit. Note the use of Forbid() without a corresponding
* Permit(). This prevents this process from being switched out
* before exiting completely. When this process is totally gone,
* Exec will notice, and do the equivalent of a Permit() internally.
*/
Close (doswin); /* Get ridda da window. */
xit:
Forbid ();
ReplyMsg (startupmsg);
return (0);
}
/*
* Here is the main program. Its job will be to create the support
* structures to fire off the sub-process, print out some relevant
* information, then while away the time until the child exits.
*/
main ()
{
register int i;
struct Process *me;
struct MsgPort *replyport = NULL;
struct Message startupmsg;
struct PhonySegList *fakelist = NULL;
LONG child,
priority,
term;
/*
* Get a handle on the current output stream so that we can Write()
* to it. Note that this implies this program really ought to be
* run from a CLI.
*/
if (!(term = Output ()))
goto xit;
/*
* Create a message port for the startup message to be replied to.
*/
if (!(replyport = CreatePort (NULL, NULL)))
goto xit;
/*
* Allocate a PhonySegList structure.
*/
if (!(fakelist = AllocMem ((LONG) sizeof (*fakelist), NULL)))
goto xit;
/*
* Copy the template into the allocated memory, and set the entry
* point to the sub-process.
*/
CopyMem (&template, fakelist, (LONG) sizeof (template));
fakelist -> psl_EntryPoint = coprocess;
/*
* Initialize the startup message. There's nothing really amazing
* happening here. Its sole purpose in life is to be replied.
*/
startupmsg.mn_Node.ln_Type = NT_MESSAGE;
startupmsg.mn_Node.ln_Pri = 0;
startupmsg.mn_ReplyPort = replyport;
/*
* Find ourselves. Discover what priority we're running at, so that
* we can start the sub-process at the same priority.
*/
me = FindTask (NULL);
priority = me -> pr_Task.tc_Node.ln_Pri;
/*
* Print some information about ourselves.
*/
puts ("This is the parent speaking.");
printf ("I'm at 0x%lx\n", me);
printf ("My stack size appears to be\n%ld bytes.\n",
me -> pr_StackSize);
printf ("pr_StackBase is 0x%lx\n", me -> pr_StackBase);
/*
* Now, create and start the sub-process. The current release of
* AmigaDOS CreateProc() does nothing special with SegLists.
* All it uses it for is to find the first bit of code to execute.
* By passing it 'fakelist', we're essentially feeding it a JMP
* instruction which jumps to the real start of our sub-process.
* (Note that we have to convert 'fakelist' into a BPTR.)
* Thus, we get a full-fledged DOS process, which we can do things
* with, and we didn't have to LoadSeg() it.
*/
if (!(child = CreateProc ("Sub-Process",
priority,
(LONG) fakelist >> 2,
2048L)))
goto xit;
/*
* Send the startup message. This will get the sub-process doing
* its thing.
* (Note that CreateProc() returns a pointer, not to the process
* structure, but to the pr_MsgPort structure *within* the process
* structure.)
*/
PutMsg (child, &startupmsg);
/*
* Make our cursor fly back and forth, which hopefully will happen
* concurrently with the sub-process's activity. We continue to
* do this until the sub-process replies its message, making
* GetMsg() return a non-NULL value. Since we "know" what's arriving
* at the replyport, we can safely throw the result from GetMsg()
* away.
*/
while (!GetMsg (replyport)) {
for (i = 64; i--; )
Write (term, fwdcursor, sizeof (fwdcursor) - 1L);
for (i = 64; i--; )
Write (term, &backcursor, 1L);
}
/*
* At this point, the sub-process has completely exited. We may
* now safely deallocate all the support structures, and exit.
*/
puts ("Child terminated.");
xit:
if (fakelist) FreeMem (fakelist, (LONG) sizeof (*fakelist));
if (replyport) DeletePort (replyport);
}