[Front] [Next Chapter]

Chapter 1 - Multitasker Structure and Functions

Contents of this chapter

Core Modules
Supervisor Module
Real Time Monitor Module
Domain Manager Module
High Level Modules
Virtual Machine Module
Task Manager


This manual describes the programming interface for the multitasking 80386 kernel. This kernel creates multiple virtual DOS machines at the request of the user who controls it through the Task Manager. The user may run a standard DOS application in each of these virtual DOS machines, but may also run applications that are aware of, and take advantage of, the multitasking. This manual tells you how to write such applications, and also multitasking-aware device drivers.

The Task Manager can also run on a single-tasking kernel as a task switcher. It has its own API that allows applications and device drivers to control and respond to task switches. If it finds that it is not running on the multitasking kernel, an additional switcher-specific API can be called by applications. The Task Manager API is described in the DR-DOS System and Programmer's Guide.

Some of the multitasking kernel's features relate directly to those of the 80386 processor. This is particularly the case with the low level functions intended only for the use of programmers of device drivers and system extensions. We use Intel's terminology and direct you to Intel* Corporation's 386 DX Microprocessor Programmer's Reference Guide for descriptions of these features, including memory paging, exception handling, Task State Segments (TSSs) and I/O bitmaps.

Multitasking applications need to call some of the kernel's functions, but many of them are intended only for the system's internal use. To simplify the life of those of you who are writing applications that do not perform low-level processor-specific and operating system-specific operations, we have flagged each function as an application program function (named X_function) or system program function (named Z_function). If you are writing applications you can probably ignore all functions whose names start with Z_.

The major facilities that applications can use include

The terminology used to describe multitasking can be confusing. There are multiple virtual DOS machines, each running an application as a task. A virtual machine and the task in it are called a domain. Each task has one or more threads of execution or processes, each of which has a state represented by its current set of processor registers. Each process must have its own stack, but may share code and static data with other processes.

Each domain has some memory that is private to it, but a domain can also access some shared or global memory. The private memory initially has the same contents as that of the parent domain, but execution of the task can change this without affecting the parent. This private memory is called instance memory and the data in it instance data since each instance of a domain has its own copy.

At any time only one process of one task is actually executing. The system keeps an image in memory of the current registers of all other processes, which it uses to load the processor's registers when it dispatches the corresponding process.

The kernel consists of a number of cooperating modules. Each module provides a complete and distinct set of services, and we therefore describe the kernel module by module. Figure 1-1 shows the internal structure of the core of the multitasking kernel, and Figure 1-5 on page 1-20 shows how the other modules relate to it.

Core Modules

The core consists of six modules.

Real Time Monitor

Memory Manager

Domain Manager

Module Manager

Exception Handler

Figure 1-1
Core Modules

The modules which have functions you can call are

Supervisor Module

The Supervisor module (SUP) performs a variety of jobs on behalf of the other modules. The functions that applications can call are listed in Table 2-2 on page 2-2. The most important is the multitasker installation check, which you must call before you can assume that any of the other calls are supported.

Real Time Monitor Module

The Real Time Monitor module (RTM) includes the dispatcher, and maintains all lists of processes and their states. RTM also includes the queue and mutual-exclusion zone (mutex) functions. Queues are used to communicate between processes and to synchronize activities. Mutual-exclusion zones give exclusive use of a shared resource to one process at a time. System extensions may also use the RTM flag functions.


Processes, or threads, are the active objects that execute code. Table 2-3 on page 2-3 lists the functions you call to create and manage processes. Initially, there is just one process running. When the user tells the Task Manager to create a new task, or when a process calls X_PCreate, a new process is created. The new process is said to be the child of the process that called X_PCreate.

Each process has a set of attributes.


A queue is a fixed length buffer in global memory that can contain a certain number of messages. Each message is of a fixed length. You specify the message length and queue capacity when you create the queue. You also specify the queue's name, which must be unique. Table 2-4 on page 2-3 lists the functions you call to create and use queues.

Any process that knows a queue's name can open it and then send messages to or read messages from it, but the most common use of queues involves just one process reading from any one queue. The content of a message is entirely a matter for the applications reading and writing it.

Although each queue's buffer will hold a fixed number of messages, processes that attempt to read an empty queue or write to a full one are suspended, so the buffer capacity and sequence of events is hidden from them. To writers, the queue appears to have an unlimited capacity. To readers it seems always to have a message. If a process does not want to be suspended, it can make the read or write conditional on immediate success. Also, you can read a message without removing it from the queue. This is called a non-destructive read.

Figure 1-2 shows the various states a queue may be in and the transitions between them. When you create a queue it is empty. Any number of processes may wait to read from an empty queue. When a process at last writes a message to the queue, the process which has been waiting longest is woken and reads the message.

Figure 1-2

A similar sequence of events takes place if processes try to write to a full queue. The writing processes are suspended. When a process reads from the queue, there will be space for one more message in the queue buffer. The message from one waiting process is immediately written into this space, and the writing process becomes ready to run again.

Queues are thus a very convenient means to synchronize processes, as well as to pass information between processes.

Mutual Exclusion Zones (Mutexes)

Mutual exclusion zones ensure that only one process at a time is performing some activity. This is one of the most common requirements when multiple processes may be running asynchronously with respect to one another.

A mutual exclusion zone is not necessarily a physical entity such as a section of code. It can be any activity that consists of two or more actions that must be completed as a unit because they require exclusive access to a shared resource. Examples of such activities and resources include shared non-reentrant code, data base updating and printing.

Mutual exclusion is achieved through the use of mutexes, a special type of queue. They have several features that enhance their usefulness.


Flags have been provided to allow Interrupt Service Routines (ISRs) to signal asynchronous events to applications. The quickest way for an ISR to wake up a synchronous process is to call Z_FlagSet.

Table 2-6 on page 2-5 lists the functions you call to create and use flags.

Only one process can wait on any given flag.

Before you can use a flag you must ask RTM to allocate one to you. There are a limited number of flags in the system, so use them sparingly. Each flag is identified only by its number.

The process that is waiting for the event should call Z_FlagWait. If the flag is in its Off state, the process will sleep until the ISR calls Z_FlagSet or until an optional timeout expires. If the flag has already been set, the process continues immediately.

If several interrupts occur before the process calls Z_FlagWait, the flag remains in its On state with a count of the number of "missed" interrupts. The flag stays in the On state until the corresponding number of Z_FlagWait calls has been made. An error code is returned both to the ISR and to the process that calls Z_FlagWait, to show that overrun has occurred.

Figure 1-4
Flag states

Memory Manager Module

The Memory Manager module (MEM) and Domain Manager module (DOM) work together to allocate and map memory and descriptors so that each application can have the resources it needs. Many of the functions in MEM are needed only by other parts of the operating system. Table 2-7 on page 2-6 lists the functions in MEM.

Applications will not normally need to call MEM directly. If they need to allocate memory or descriptors, they should do so through higher level portable interfaces, such as XMS, VCPI and DPMI, which in turn call MEM.

Domain Manager Module

A major capability of the operating system is its ability to run multiple DOS applications simultaneously. Each application sees a real mode 8086-like environment of its own, complete with an operating system, device drivers, keyboard, mouse, screen and so on. This environment is called a virtual machine, because it does not exist physically. The combination of a virtual machine and the application running in it is called a domain. Domains are created and managed by the Domain Manager (DOM). Table 2-8 on page 2-8 lists the functions you call to create and control domains.

When the kernel starts up, only one domain is created. It is normally the Task Manager which, in response to hot-key combinations pressed by the user, instructs DOM to create domains, delete them and switch selected domains to the foreground (that is, attach them to the keyboard, mouse and screen).

DOM actually performs only the lower level functions needed to create virtual 8086 and virtual DOS machines. Higher level functions such as virtualizing a VGA are carried out by the VM module.

A virtual machine is achieved by mapping the required physical memory into the first four megabytes of linear address space, setting traps to intercept I/O, then putting the processor into virtual 8086 mode. Virtual 8086 mode is practically indistinguishable from real mode as far as DOS applications are concerned. The required physical memory is a combination of unshared instance memory and shared global memory. The virtualization includes very sophisticated exception handling that can distinguish between the many sources of exceptions and interrupts. By trapping, for example, attempts to read from the screen memory, it allows applications to continue to run in the background. Each application "sees" a full screen. Only the foreground application's screen is visible to the user, but an application can force urgent messages to be displayed immediately, even if its domain is in the background, by calling the Virtual Machine module (VM); see page 1-21 for a description of VM.

I/O Bitmaps

Each domain has its own I/O bitmap. The bitmap has a bit for each I/O port address. The bits corresponding to virtualized ports are set. This causes a General Exception whenever an application tries to read from or write to the port. The system recognizes that the exception was caused by an I/O operation, so it invokes the Virtual Machine module's I/O exception handlers, which make sure the port ownership rules are obeyed; see "Virtual Machine Module" on page 1-21 for a description of the rules.

If you are writing system software that similarly needs to trap accesses to other I/O ports, you can call DOM functions to read, set and reset bits in the I/O bitmap of any domain. These functions' names all start Z_IOBitmap...

See the description of Z_HandlerGenEx for a full explanation of exception and interrupt handling, including the use of installable handlers.


Before the Task Manager is loaded, only one domain exists. In its initialization, Task Manager must determine which memory areas should be instanced. It uses several methods to do this, including making an INT 2FH with AX=4B05H to ask global programs to identify their instance data. Programs that need to instance their data in a multitasking environment must hook INT 2FH and process it appropriately. Any memory owned by device drivers and other resident programs that they do not identify as instanced is assumed to be global. Programs loaded after the Task Manager is running will be loaded into local memory, that is, instance memory within one domain only.

The Task Manager informs DOM which addresses are to be instanced when it has found them all.

Memory instancing uses 80386 page tables for large areas and software-controlled swapping for small ones. Each domain has its own page table for the first 4 MB of memory. These page tables point to globally shared pages and unshared instanced pages. A context switch that includes a domain switch involves four steps to map memory.

  1. Saving the instanced portions of global pages into a data area owned by the outgoing domain
  2. Switching to the new domain's base page table

  3. Copying the global portions of instance pages from the previous domain's copy

  4. Loading the instanced portions of global pages from a data area owned by the incoming domain

As well as mapping memory, a domain switch loads the I/O bitmap and exception handlers for the incoming domain, because these may vary from domain to domain.

Domain Screen Switching

Applications do not normally need to be aware of which domain owns the physical screen, and which domains are running in the background. Unless you are writing system software, you can skip this section.

Global programs that need to be notified of domain screen switches should hook INT 2FH and be alert for the function Build Notification Chain (AX=4B01H), which will be the first indication they have that the Task Manager is active. This informs the kernel of the program's callback function's address, and informs the program of the kernel's service functions' address.

While the Task Manager is active, programs can call Detect Switcher (INT 2FH / AX=4B02H) and then Hook Notification Chain to achieve the same thing.

Programs can call the service functions to perform the following:

High Level Modules

On top of the core modules, the multitasker has several higher level modules, shown in Figure 1-5 and described in the rest of this chapter.

Figure 1-5
High Level Modules

Of the modules shown in Figure 1-5 only the Virtual Machine (VM) has an API that is unique to the multitasker, though the Task Manager's API is reduced when running on the multitasker. Other modules have no API, or behave the same whether running on the multitasker or raw single tasking DOS, with or without the Task Manager. Their descriptions are therefore not included in this manual.

Virtual Machine Module

The Virtual Machine module (VM) completes the virtualization of a PC from the application's perspective. Its major tasks are

Pop Up Messages

A TSR running in a domain can call VM to ask it to prepare for, and optionally display, a pop-up message on the TSR domain's virtual console. Similarly, an application whose domain is currently in the background may display a message over the foreground screen. In both cases VM saves the previous screen contents and restores them after the message has been shown.

The screen may have been in a graphic mode, so VM clears it and puts it into the default text mode, allowing you to display text by INT 10 or direct screen writes.

Domain Message Mode

Use Domain Messages if your program is a TSR or thread that does not own its domain's screen, and therefore needs to save the screen mode and contents before displaying a message. VM has two related calls: X_MsgDomEnter and X_MsgDomDisplay. They both save the call parameters, put the domain into Domain Message Mode and return immediately to the caller. The message has not been displayed, and nor is the screen in text mode at this point, unless the domain is in the foreground.

If the domain is in the background, its application can continue to write to the virtual screen as normal. When the domain comes to the foreground, VM saves the screen mode and contents, then either displays a message string you passed it (if you called X_MsgDomDisplay) or calls a function you specified (if you called X_MsgDomEnter).

The domain is in the foreground when your message display function is called, and can therefore use the keyboard to take the user's instructions. When the user has responded and you want to remove the message, call X_MsgDomExit. VM restores the original screen mode and contents, and turns off Domain Message Mode.

While in Domain Message Mode, the user can switch a different domain to the foreground. If so, your message will not be seen again until its domain is again in the foreground. X_MsgDomEnter calls your message display function each time the domain comes to the foreground.

Global Message Mode

Use Global Message Mode if your application needs urgently to display a message when its domain is, or may be, in the background. Again, VM has two related calls: X_MsgGlobalEnter and X_MsgGlobalDisplay. They immediately bring the domain to the foreground, save the screen mode and contents, and put the screen into its default text mode. At this point, X_MsgGlobalEnter returns to the caller, whereas X_MsgDomDisplay first displays a message string you passed it.

Because the domain is now in the foreground, you can read the keyboard and output to the screen (though with some restrictions described on page 2-121). When the user has responded and you want to remove the message and return to the background, call X_MsgGlobalExit.

While in Global Message Mode the Task Manager is disabled, so the user cannot avoid seeing the message.

Serial and Parallel Ports

The standard ports known to the ROM BIOS are initially set up to use their default port numbers and IRQs. You can call VM to change these and to tell it the port numbers and IRQs of COM and LPT ports (up to COM4 and LPT3), which the BIOS does not know about. These values are physical and therefore global.

While the Task Manager is running, VM virtualizes the serial and parallel ports. The method it uses to decide which domain can use the physical ports is based on timeouts and traps, as follows.

Timeout values (t) are initially set to 5 seconds for a COM port, and 15 seconds for a printer port, though you may call VM to change the timeout on any port. The operation of timeouts is:

  1. Initially, VM traps all accesses of all virtualized ports (those that have a non-zero base address and timeout).
  2. The first domain to access a given COM or PRN port becomes the "owner" of that port, thus other domains cannot use the port. VM stops trapping the port for a time of t/2.

  3. When that timeout expires, VM sets its trap again, and restarts another timeout of t/2. If the owner accesses the port within that time, VM immediately clears its trap and goes back to Step 2, otherwise Step 4 is performed.

  4. The port is freed, and will be given to the next domain to access it.

The specified timeout is therefore the maximum time which can elapse between the last access by the owner and the point at which the port is freed. The minimum time is t/2. The timeout value should be set to at least twice the maximum interval expected between port accesses while the port is being used.

A timeout value of 0 disables virtualization of the port. Any domain may access it freely at any time.

A timeout value of -1 sets an "infinite" timeout; thus, once the port is given to a domain, it never becomes free again, unless the domain is deleted.

Task Manager

The Task Manager controls switching between multiple DOS applications by giving the VM and DOM modules the appropriate sequences of commands. It accepts instructions from the user via keyboard hot-key combinations. It can also be controlled by software, using its native API (INT 2FH / AH=27H), though only the functions listed in Table 2-10 on page 2-11 are available when the Task Manager is running on the multitasking kernel.

The Task Manager also supports the industry-standard INT 2FH / AH=4BH API for task switchers with its corresponding service and notification functions. This interface is provided to support global programs written to run on other DOS task switchers. Although these functions are not described in this manual, they are listed in Table 2-10 on page 2-11 to Table 2-13 on page 2-13.

When the Task Manager is not present, the kernel does not respond if an application or device driver tries to detect the presence of a switcher (INT 2FH / AX=4B02H) or asks for a switcher ID for itself (INT 2FH / AX=4B03H). When the Task Manager is present, the kernel does respond to these requests. However, the Task Manager does not itself call INT 2FH / AX=4B02H, so it cannot be run if a task switcher is already present.

[Front] [Next Chapter]

Copyright © 1994, 1997, Caldera, Inc. All rights reserved.