?? ctask.doc
字號:
2) Task1 runs up to "temp = val", then is interrupted by the
timer. Task2 executes, and gets 14 as result. Then Task1
continues. Return for Task1 is 14.
3) Task2 runs up to "temp = val", then is interrupted by the
timer. Task1 executes, and gets 6 as result. Then Task2
continues. Return for Task2 is 6.
add to this the effects of optimization, and a loop, and the
outcome is completely random.
Most routines in the C library will not explicitly do something
like this, but all functions that
- do file I/O (read, write, printf, scanf, etc.) or
- change memory allocation (malloc, free, etc.)
Ctask Manual - Version 2.2 - 90-10-12 - Page 12
have to use some static data to do buffering, or to store the
chain of memory blocks in. Interrupting such an operation may
have disastrous effects. The most devilish aspect of non-
reentrancy is that the effects are unpredictable. Your program
may run 1000 times without any error, and then on the 1001th time
crash the system so completely that only the big red switch will
help.
So what can you do about it? A lot. There are several ways to
protect "critical regions" from being entered in parallel. The
most simple method is to disable interrupts. This, however,
should be used only for *very* short periods of time, and not for
rou-tines that might themselves re-enable them (like file-I/O). A
better method is to temporarily disable task preemption. The
routines tsk_dis_preempt and tsk_ena_preempt allow this form of
short-term task switch disable. Interrupts may still be
processed, but tasks will not be preempted. The best way is to
use a "resource". A resource is a special kind of event, which
only one task can possess at a time. Requesting the resource
before entering the routine, and releasing it afterwards, will
protect you from any other task simultaneously entering the
critical region (assuming that this task also requests the
resource).
It is also reasonably safe to use file-I/O without protection if
it goes to different files. The C file control blocks for
different files are distinct, so there will be no conflict
between tasks. Since DOS access is automatically protected by
CTask, concurrent file I/O is possible.
What you may NEVER do is to use a non-reentrant routine that is
not protected by an interrupt disable from an interrupt handler.
An interrupt handler is not a task, and so can not safely request
a resource or disable task preemption. This is the reason why the
CTask routines generally disable interrupts before manipulating
the internal queues rather than only disabling task preemption.
Deadlocks
One thing to watch out for when using resources or similar event
mechanisms is not to get into a situation where Task 1 waits for
a resource that Task 2 has requested, while at the same time Task
2 waits for a resource that Task 1 already has. This situation is
called a deadlock (or, more picturesque, deadly embrace), and it
can only be resolved with outside help (e.g. waking up one of the
tasks forcibly). To illustrate, consider the following example:
Ctask Manual - Version 2.2 - 90-10-12 - Page 13
void far task_1 ()
{
...
request_resource (&rsc1, 0L);
request_resource (&rsc2, 0L);
...
}
void far task_2 ()
{
...
request_resource (&rsc2, 0L);
request_resource (&rsc1, 0L);
...
}
Since interrupts are always enabled on return from a task switch,
even if the statements are enclosed in a critical region, there
is no guarantee that the request_resource calls will be executed
without interruption. In this example, the problem is obvious,
but in a more complex application, where resource requests or
other waits might be buried in some nested routine, you should
watch out for similar situations. One way to avoid problems would
be in this example to change task_2 to
void far task_2 ()
{
int again;
...
do {
request_resource (&rsc2, 0L);
if (again = c_request_resource (&rsc1))
{
release_resource (&rsc2);
delay (2L);
}
} while (again);
...
}
Note that this is only one of many possible approaches, and that
this approach favors task_1 over task_2.
You should also take care not to kill tasks that currently own a
resource. CTask will not detect this, and the resource will never
be freed.
Ctask Manual - Version 2.2 - 90-10-12 - Page 14
Using CTask
CTask comes archived with both source and binaries. The binary
version is compiled in the large model, but since the precompiled
kernel routines don't use any functions from the C library, you
can use all functions in small or other model programs (except
Turbo C's Tiny and Huge models). The include files provided
specify all model dependencies, so you don't have to use the
large model for your application, but always remember to include
"tsk.h" for the type definitions and function prototypes.
The C source files will work without changes for both Microsoft
and Turbo C. The library files are not compatible, so use
"ctaskms.lib" for Microsoft, and "ctasktc.lib" for Turbo C.
In the distributed configuration (i.e. dynamic allocation of
control blocks enabled), the file TSKALLOC.C must be added to the
library or separately linked after compiling it *in the same
model as the main program*. This file uses C-library routines,
and thus must match the main program's memory model. The same
goes for TSKSNAP.C, an optional snapshot-dump utility, and
CONOUT.C, the sample console output handler. The provided
"ctsupms.lib" and "ctsuptc.lib" files have been compiled in the
large model, and may only be used with large model programs.
Turbo C's huge model uses a different segment setup for data
segments. This requires using a special data segment for all
CTask data, and compilation of the CTask kernel in huge model.
For the assembler files, the symbol TC_HUGE must be defined
during assembly, the C files must be compiled with the Data-
Segment and BSS-Segment naming options. Make-files for Turbo C
Huge model are included.
Configuration Options
The file TSKCONF.H contains a number of #define's that allow you
to configure some CTask features. In general, you should
recompile all of CTask when changing one of the flags. Version
2.1 collects all configuration options for both Assembler and C
in this file.
The entries are
CODE_SHARING
If TRUE, the generated kernel supports code sharing. This
requires the entry points to load DS on entry, and compi-
lation with Large model (MSC) or Huge model (TC).
This option is normally disabled (FALSE).
Ctask Manual - Version 2.2 - 90-10-12 - Page 15
NEAR_CODE
If TRUE, all CTask routines are 'near'. Use only with small
or compact model. You will have to change the make-files so
the compiler model is no longer Large, and the code segment
is not named, when turning on this flag. The default is
FALSE. Setting this option TRUE will save code, and make
calls faster, but the library will no longer be model
independent.
This option is normally disabled (FALSE).
LOCALS_FAR
If TRUE, internal CTask routines ar 'far'. This might be
necessary if your compiler/linker does not allow placing all
CTask kernel code in a common segment. Do not set this flag
if NEAR_CODE is set.
This option is normally disabled (FALSE).
CALL_PASCAL
Use Pascal calling sequence for CTask routines. This may
save some code, but may cause naming conflicts if your
compiler limits external Pascal names to 8 characters.
This option is normally disabled (FALSE).
TC_HUGE
(Assembler only) Define TC_HUGE for use with the Turbo C
Huge model, and if it is desired to separate CTask's data
from the normal data segment. This flag causes the
CTASK_DATA segment to be defined, (class is DATA) and DS to
be loaded with this segment on function entry. The C
functions in the CTask kernel must be compiled to use the
same data segment.
This option is normally disabled (undefined).
LOAD_DS
(Assembler only) Define LOAD_DS to reload the data segment
on entry to global functions. This flag must be defined for
TC_HUGE. It can also be defined if the data segment can not
safely be assumed to be loaded in DS on function entry, for
example if the code sharing feature of version 2.1 is used.
The C routines must be compiled with the necessary compiler
switches or the _loadds keyword in this case.
This option is normally disabled (undefined).
Ctask Manual - Version 2.2 - 90-10-12 - Page 16
ROM_CODE
(Assembler only) Define ROM_CODE TRUE for embedded systems
using ROM-based code. This option disables storing variables
in the code segment in some modules. Note that most DOS-
related modules are not ROMable.
This option is normally disabled (FALSE).
FAR_STACK
(Assembler only) Define FAR_STACK TRUE to save some space in
the default DGROUP. With this define TRUE, the local stacks
for the scheduler, the timer, and the interrupt handlers,
are allocated in a separate segment. With Turbo C, you may
have to edit the "c0.asm" startup module to accomodate the
new segment.
This option is normally disabled (FALSE).
TSK_DYNAMIC
If TRUE, you can let CTask dynamically create task and event
control blocks, task stacks, and pipe buffers, by passing
NULL as the block address. Since this requires the C runtime
allocation calls, it is not suitable for non-DOS appli-
cations (except if you provide your own memory allocation
routines).
This option is normally enabled (TRUE).
TSK_DYNLOAD
If FALSE, this instance of the kernel does not include
dynamic allocation routines. Setting this flag to FALSE when
TSK_DYNAMIC is TRUE only makes sense with multiple linked
kernels. A resident kernel might not employ dynamic
allocation, whereas a secondary kernel needs it. In this
situation, TSK_DYNAMIC must be set in the primary so that
the task kill code is included and the kernel configurations
match, but TSK_DYNLOAD may be false to prevent inclusion of
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -