home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fish 'n' More 1
/
FishNMoreVol1.bin
/
more
/
text_files
/
aboutexec
< prev
next >
Wrap
Text File
|
1990-01-03
|
13KB
|
246 lines
A Tutorial on exec, i/o, and Multi-tasking
(c) By John T. Draper
A product of the Programmers Network, A Software Developers Guild and
Information source for Independent programmers. Permission to distribute
this is granted as long as the title and header is included. For more info
on how to join the Programmers Network, Contact John Draper at the following
net address:
USNail: 310 WestLine Dr. B210
Alameda, Calif. 94501
(415) 769-1268
UUCP: ...ihnp4!ptsfa!well!crunch
BIX: crunch
WELL: crunch
DELPHI:CRUNCH
THE BASICS OF EXEC
------------------
All programs running on the Amiga are under control of the EXEC. You can
think of the EXEC as a very fast program that controls all other programs
running on the Amiga. Programs come in 3 flavors. Processes, Tasks,
and Interrupts. In this tutorial, the term "Task" will mean a separate
"Program" or set of instructions. The EXEC has the responsibility of deciding
which TASK gets control of the CPU. Tasks can have a specific PRIORITY. The
range of prioritys can be from -128 to +127. Normally, user programs run
at priority 0. Intuition runs at priority 50, and programs can change their
priority if necessary. The PRIORITY of a program determines which place in
line it has in relation to other programs waiting for their turn to run. It's
like "Waiting in line" to get your chance to use the CPU.
THE EXEC CONTROLS TASKS (among other things)
--------------------------------------------
Tasks can be in 3 states: Running, Ready, and Waiting. A "Running" task
actually has control over the CPU, It now "Owns" the CPU for a specified
period of time called a QUANTUM. A QUANTUM is a set period of time, that
can be changed, and is somewhere around 50 Milliseconds. After its time is
up, the EXEC saves the state of the program on its own stack, and switches
to the NEXT program in line. The program that just gave up the CPU gets put
into another line. This line is the "Ready" line. If that task has a higher
priority than the highest task in that "Ready" list, it gets shoved in the
front of that list. When the program thats running, executes a "Wait" or
a "WaitPort" instruction, the task in the front of the "Ready" list gets
a chance to run again, until either its QUANTUM is up, or it executes
a "Wait" instruction.
LISTS OF "Things" THAT EXEC CONTROLS
------------------------------------
Speaking of "Lists", the Amiga has a standard way of dealing with lists.
A good description is in the first chapter of the VOL 1 RKM. A list is
a collection of "Nodes". Just about everything in the Amiga has a Node.
Tasks, Messages, Ports, I/O Blocks all have Nodes. This gives the
Exec a consistant way of handling each of these. This means that Messages
can be put into a "List" of messages, Ports can be put into "Lists" of Ports
and so on. This permits a very flexable way for an Amiga programmer to
control the various resources of the system. The RKM doesn't do a very
good job in explaining what lists are used for, it just does a good job
in explaining what they are, and how to manipulate them.
A "List" structure is shown below, and how it connect up a few "Nodes".
-----------> struct Node {
| struct Node *ln_Succ; (ZERO)
struct List { | struct Node *ln_Pred; <------
struct Node *lh_Head; ----- UBYTE ln_Type; |
struct Node *lh_Tail; (Always Zero) BYTE ln_Pri; |
struct Node *lh_TailPred; ----- char *ln_Name; |
UBYTE lh_Type; | }; |
UBYTE lh_pad; (Not used) | |
}; | struct Node { |
| struct Node *ln_Succ; <------
| struct Node *ln_Pred; <------
| !! !! |
| |
------> struct Node { |
struct Node *ln_Succ; <------
struct Node *ln_Pred; (Zero)
JUST ABOUT EVERYTHING IS A NODE
-------------------------------
A Node is sort of a "Connection point" where the various structures can
be connected. Usually, Nodes are connected or arranged in some sort of
a "List". Nodes can be move from list to list. Nodes can be Tasks,
Processes,
Messages, Message ports, I/O Blocks, and just about anything else in the
Amiga. Not everything is a node. For instance: Gadgets, Menus, and
Windows are NOT nodes. I don't know why!! Perhaps RJ can comment on that.
As shown above, Three Nodes are connected in a "List". If these nodes
are Tasks, and the list happens to be a "Ready" list, these Nodes are inserted
in such a way that the Highest priority is at the "Head" of the list.
the Exec function Enqueue() is probably used.
The "ln_Pri" field is the nodes Priority field. Because these Nodes are
Tasks, the ln-Pri field then determines that Tasks priority. Other entities
have Nodes, like Messages, and Ports. These also have "Priorities", or
ln_Pri fields. If you are using the sound drivers, the ln_Pri field is used
as a "Precidance". Sometimes, this field isn't really used by Exec. You can
use it in your OWN Custom nodes should you decide to create your own. So,
we now have these Lists and Nodes, what can we really do with them?
DIDDLING WITH NODES
-------------------
We can create a New List, by calling "NewList". Unless you are an advanced
programmer, you probably won't ever have to use this function. It is probably
called when you Create a Port, or in some of the lower level Exec functions.
INSERTING A NODE
----------------
After you create this "List", now you can "Attach" nodes to it. Use the
Insert() function. For example: Insert(Mylist, MyNode, Pred); Where:
struct List *MyList; /* Is a pointer to my List structure */
struct Node *MyNode; /* Pointer to the Node that I want to Add to the list */
struct Node *Pred; /* Pointer to the Node in the list, where "MyNode" gets
inserted AFTER */
If Pred = 0, the node will be added at the Head of the list. If the Pred
node points to the list lh_Tail field, it will be inserted at the "Tail" of
the list. You can "AddHead" and "AddTail", also "Remove" a node.
PRIORITIZED INSERTION
---------------------
The Priority field of the Node can also be used to determine the position in
the list where a node can be inserted. When your task finishes its turn
using the CPU, this function is called. This is called PRIORITIZED INSERTION.
The "Enqueue" function inserts a node into a list AFTER the last node of same
or higher priority. For example: Enqueue(MyList, ANode); Inserts
"ANode" into "MyList" where they are declared as:
struct List *MyList; /* A pointer to the list we want to insert in */
struct Node *ANode; /* The Node that I want to insert */
Naturally, before "Enqueue" is called, the Nodes ln_Pri field is set
as follows: ANode->ln_Pri = <some priority value>, For instance, just
before you add a Task, you must set the tasks Priority field. If "MyTask"
is a pointer to a task, then: MyTask->tc_Node.ln_Pri = PRIORITY; Will set
that tasks Priority. This is usually done before your task becomes activated.
FINDING NODE BY NAME
--------------------
Finally, each node can have a "Name" pointer "ln_Name". Using Names is
useful for "Debugging" purposes and otherwise identification of them. A
Function called FindName will return a pointer to the node, given a pointer
to a null terminated string, and a list. For example:
FoundNode = FindName(MyList, "MyNode"); Where MyList is a pointer to a List,
and "MyNode" is the Name for that node. "FoundNode" is a pointer to the
node with that name. If No name exists, function returns a NULL. To find
multiple entrys, call function again.
MOST EXEC STRUCTURES HAVE NODES IN THEM
---------------------------------------
As mentioned earlier, just about every C structure contains a "Node"
structure within it. Notice that the FIRST entry in a Task structure is
a "Node". So, a Task node can be referenced by: tc_Node. Notice that
this field is an "INSTANCE" of a node. This means that the Task structure
has a Node contained within it. This ISN'T a "Pointer", it is the actual
structure. If a field within a structure is a "POINTER", it contains an "*"
in front of it. Part of a Task structure is listed below, Refer to Pg 1-15
Vol 1 of RKM for entire structure.
struct Task {
struct Node tc_Node; /* Tasks Node structure */
UBYTE tc_Flags
!! !!
!! !!
Each NODE has a Type. Each List also has Types. There are MANY DIFFERENT
Types. For instance, a Task Node has a type of NT_TASK. If a node is
attached to a "Message", its type is NT_MESSAGE. For instance, we might
have a "List" of Messages attached together for some purpose, for instance,
messages queued up in a message port are lists of message nodes.
So the "List" lh_Type field in the list would have NT_MESSAGE as its
Type. Then each Message in that list would have to have its ln_Type field
also set to NT_MESSAGE. In the "exec/nodes.h" header file is a definition of
the various Types and their values. These values are from 0 (unknown) to
13 (a Process). You can define your OWN lists. If you do, make sure that
you choose a number above 64. I'm sure that Amiga will be sequentially
"assigning" various Node types for future use. I am sure that Amiga would
want you to choose Node types that wouldn't have much of a chance of Clashing
with future types. Some of the types are listed below:
NT_TASK - A Task node.
NT_INTERRUPT - An Interrupt node.
NT_DEVICE - A Device.
NT_LIBRARY - A Library
NT_FONT - A Font
NT_MESSAGE - A message
NT_MSGPORT - A Message port
These are to name a few. There are more, but these are the most Common
parts of the Amiga operating system. Notice that NOTHING was mentioned about
AmigaDOS. AmigaDOS is a separate "Layer" or set of programs that run "on top"
of the Exec. Because of this, separately generated tasks CANNOT CALL any of
the AmigaDOS functions. A lot of the C Library functions like "printf" must
call the AmigaDOS functions, and CANNOT be used from within a task. They CAN
be called from separate Processes. A Process is different than a task.
There is a very LIGHT discussion on "Processes" in the AmigaDOS manuals
and won't be covered here.
Next time, I'm taking it further into the relationship of how all this
is put together. Just about EVERYTHING works the same way, for instance,
all the EXEC I/O can run as Tasks. When an IO request is made to start up
the Narrator, the "SendIO" function is called which can "Activate" an IO
task, then when that task gets done "Doing its thing", it sends a Message
back to a designated "Message Port" telling the calling routine that It's
finished. In the meantime, your program was running off doing other things
until it recieved a "Signal" from the IO device. The Serial, Parallel,
Sound, Narration, Disk Device, and ALL other devices work in EXACTLY the same
way.
Enuff for a nights work. Now if someone would hire me, and treat me
nice, I would be able to find time to post more of these tidbits of wit.
Adios folks, Till next time......
Crunch (The Cap'n)
Programmers Net
On the WELL
"Da place where us global village folks live"