?? ctask.doc
字號(hào):
same time by switching the processor between the tasks you de-
fine. And since most of the time your program is waiting for some
slow device (like the human hand) to provide feedback, this
switching is completely transparent, and will not noticeably slow
your program down.
Switching the Context
So what is needed to allow the user to edit a file while at the
same time downloading another and printing a third? First, you
have to have some form of "context switching". This means that
you have to be able to interrupt the processing of the download
when the user presses a key, process the key, and return to the
download "task" at the exact same point it was interrupted. One
solution to this would be to include a poll for the keyboard at
several points in the download routine, and call the editor task
when a key is available. But apart from cluttering your code
with lots of unrelated calls, there is another problem. What if
the operation the user requested is more involved than just
putting the character on the screen, like writing the file to
disk? This might take so long that your download times out. There
must be a way to pass control back and forth between the two
tasks, such that no task is delayed for an extended period of
time, and also to activate the print spooler task at some defined
interval to output the data to the printer. This context
switching is called "scheduling" in CTask. The "scheduler" is
Ctask Manual - Version 2.2 - 90-10-12 - Page 2
invoked on every system timer tick, and will save the context of
the current task. The scheduler then takes the first element from
the queue of tasks that are eligible to be run, and restores the
context of this task, returning to the point where the task was
interrupted. This switching is completely automatic, and requires
no special programming in the tasks itself. All you have to do is
to tell CTask that there are three tasks, the download task, the
spooler task, and the editor task.
You have Mail
All you have to do for context switching, that is. There's a bit
more to multitasking than meets the eye. How do you tell the
spooler task what files to spool, and the download task what
files to download? You can't call a task like an ordinary func-
tion, so what you need for this is "inter-task communication".
There must be a way to pass a message containing the filename to
be printed to the spooler task, and there are several in CTask,
one of them the "mailbox". The spooler can use a CTask call,
wait_mail, to wait for a message to arrive at its mailbox. As
long as nothing arrives, the spooler task will no longer be
scheduled, so if there is nothing to print, it will not use any
processor time. When you send a message with send_mail to the
mailbox, the spooler will wake up, and process the file. You can
also send more file name messages to the spooler while it still
prints a file, leaving the messages in the mailbox until the
spooler is ready to process the next file.
Reentrancy and Resources
This last example seems innocent enough, but there's a big stumb-
ling block hidden in it. You allocate the file name messages in
the controlling task with malloc, and you free them in the
spooler with free, no problem, right? Wrong, there is a big
problem, "reentrancy". Reentrancy means that you can re-enter a
routine while another task is already using it, and that this
will not disturb the operation of the interrupted task. But
malloc and free share and modify global data, the chain of free
memory blocks. Imagine the following: You just called malloc from
the controlling task. Malloc has loaded the address of a free
element into a local variable, and is about to write back the
pointer to the next free element into the last. At exactly this
moment, the timer ticks, and the spooler is activated. It has
just finished printing, so it calls free. Free steps through the
chain of free blocks to find the right place to insert the block.
According to Murphy's law, it will find just the place where
malloc is about to write back the pointer. Free coerces the
elements, points the next pointer to the element malloc just
wants to take off the chain, and returns. Malloc writes its next
pointer into the middle of the newly coerced block, and now re-
turns an element which is still in the free list. Compared to the
Ctask Manual - Version 2.2 - 90-10-12 - Page 3
job of finding this kind of bug, stepping in for Tantalus may
feel like a vacation. This kind of problem code is called a
"critical region". There must be a way to make sure that no two
tasks simultaneously enter such a region, and, you guessed it,
CTask provides one, the "resource". When you request a resource
in one task, all other tasks trying to request the same resource
after that are put to sleep until you call release_resource. Only
then will the highest priority task that waits for the resource
wake up, and get access to the protected region. So you would
have to substitute malloc and free calls in your routines with
calls to functions that first request a resource, execute the
function, and then release the resource.
DOS Access
But, you might ask, isn't there another reentrancy problem in
this example, since both the spooler and the download task might
simultaneously call DOS to do their file-I/O, and DOS is not
reentrant? Do I have to substitute all my calls to fread and
fwrite, too? The answer to this, luckily, is no. CTask traps all
your DOS calls, and automatically encloses them in the necessary
resource request and release calls, so you don't have to worry
about trashing your disk by simultaneous DOS requests. The
limited multitasking capabilities of DOS are exploited to allow
some parallel processing in DOS, and CTask will also detect and
handle DOS calls by resident background programs like the DOS
PRINT utility.
Handling the Keyboard
CTask also allows you to circumvent DOS for keyboard input, so
that waiting for the keyboard will not block other tasks from
access to DOS functions. Previous versions of CTask used a "pipe"
to store all keyboard input. Starting with version 1.2, CTask
uses a "flag" to signal that keyboard input might be available. A
"flag" is another form of inter-task communication that just sig-
nals that some event occurred, without passing any specific data.
The reason for using flags in the keyboard handler is compati-
bility to TSR's. If a keyboard interrupt occurs, the interrupt
handler just sets a flag, and passes on the interrupt. The key-
board routines wait for this flag to be set, and then check the
keyboard buffer if a character has arrived. If the keyboard buf-
fer is empty, the flag is again cleared, and the keyboard rou-
tines put the waiting task to sleep again, so the processor is
free to do more interesting things than to loop waiting for the
user to press a key.
Ctask Manual - Version 2.2 - 90-10-12 - Page 4
Serial I/O and Timeouts
The "pipe" is similar to the mailbox in that you can wait for
items to be sent to a pipe. But unlike mailboxes, pipes use their
own buffer to store the items (which are limited to bytes and
words), so you don't have to allocate mail blocks for each item.
When waiting on the pipe, your task is put to sleep, freeing the
processor. Pipes are used for the serial I/O handler included
with CTask that makes some of the work you've put into your
communications package obsolete. When outputting data to the
serial port via the CTask routines, the data is buffered in a
pipe, and incoming data is also placed in a pipe. All interrupt
handling, and the processing of modem status and XON/XOFF proto-
cols, is done by CTask, so you can concentrate on implementing
the higher level protocols. Since CTask allows all calls that
wait for pipes, mail, and other events, to specify a timeout that
is based on the system tick, you do not have to resort to timed
waiting loops to detect communication line faults. You simply
give a time limit on the wait call, and if that limit expires,
the wait routine will return with an error indication.
Priorities
If the protocol you implement requires fast responses to incoming
blocks, you can influence the response of CTask to your comm
task's needs by giving this task a higher priority. CTask allows
65535 different priority levels, and tasks having higher priority
are scheduled before tasks with lower priority. Also, high prio-
rity tasks will get access to mail, pipes, and resources, before
other tasks. It might even be sensible to split the comm task
into two separate tasks, one of high priority that assembles the
incoming bytes into blocks and handles the protocol, and a lower
priority task that reads the received blocks from a mailbox and
stores them on the disk. In extremely time critical applications,
you can even turn off task preemption, so the timer tick will no
longer cause a task switch.
Change to your liking
CTask provides all basic building blocks for implementing
concurrent programs in an easy and comprehensible way. Since
CTask is mainly implemented in C, it may not be the fastest
possible system, but due to the straightforward design which uses
few shortcuts, modifying the sources to suit your needs and taste
can be done without weeks of studying assembler code that
squeezes every microsecond from the processor. CTask is public
domain code, and there are no restrictions on its use. It is
distributed in source form, so you are free to change all aspects
of the package. Multitasking programs, especially in embedded
applications, tend to be very diverse in their needs for specific
constructs. So although CTask is ready to run under DOS, and is
Ctask Manual - Version 2.2 - 90-10-12 - Page 5
easily adaptable for embedded applications, you should see CTask
more as a starting point for your own thoughts, and as a toolbox
from which you can pick the instruments you need, than as a
finished and fixed block of code you simply plug into your
application.
Ctask Manual - Version 2.2 - 90-10-12 - Page 6
General Notes
What can CTask NOT be used for?
CTask is not intended to provide for multitasking on the command
level of MS-DOS. Although version 1.2 of CTask added the ability
to TSR and spawn other programs, and to communicate between
multiple copies of CTask, a full DOS-process management (which
would have to include keeping track of memory allocation and DOS
process control blocks) is not included. Adding this functio-
nality would not be trivial (although certainly worthwhile).
CTask also is not a true "Real-Time" multitasking system. Due to
the completely dynamic structure of tasks and events, and the
minimal restrictions on what interrupt handlers may do, it is
nearly impossible to calculate a maximum interrupt or task switch
latency. If you have critical timing requirements, you should
consider getting a professional package like AMX. CTask has been
used successfully in embedded control applications, and if your
timing requirements are not that critical, or you're ready to
take up the task of measuring and calculating latencies with your
specific task setup, CTask may be useful even for Real-Time
applications. However, you're more or less on your own in this
field.
And, there is no warranty that CTask does perform without errors,
or does exactly what you or I intended. So CTask should not be
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -