At any point during a time quantum, the kernel can preempt the current task, and immediately switch execution to a more important task if necessary. To determine how and when to switch from one task to another, the kernel reads task states and priorities.
To determine how the ready-to-run tasks are executed, the kernel considers each task's priority.
Priority determines the order in which tasks in the ready queue are executed. The kernel executes only the highest-priority task (or tasks) in the ready queue and doesn't devote any CPU time to lower-priority tasks. If several tasks all share the same highest priority, the kernel rotates among those tasks, devoting one time quantum to each. Lower-priority tasks in the ready queue don't receive any CPU time at all until there are no higher-priority tasks in the ready queue: the higher-priority tasks either finish execution and exit the system, move to the wait queue, or change to a lower priority.
Whenever a new task with a higher priority than the one running enters the ready queue, or whenever an existing task is given a higher priority than the one running, the kernel preempts the running task. It immediately switches execution to the higher-priority task, even if the switch occurs in the middle of a time quantum. The higher-priority task then starts at the beginning of its own time quantum.
Note that round-robin scheduling takes place only when tasks of equal priority have the highest priority in the ready queue. If only one task has the highest priority, only that task runs, and all others languish until it finishes or is kicked out of the CPU limelight by another task with a higher priority. Note also that if a task executes a Yield()
call, it forgoes the rest of its quantum, and yields the CPU immediately to other tasks of equal priority.
WaitSignal()
, WaitIO()
, or WaitPort()
) to define what it's waiting for. It then becomes a waiting task and receives no CPU time. When its wait conditions are satisfied, a task moves to the ready queue, where it can compete for CPU time.The wait queue is an important feature for keeping running tasks working at top speed without, wasting CPU cycles on tasks waiting for external events. For the wait queue to work, each task must not use a loop that constantly checks for an event. Repeatedly checking for an event, known as "busy-waiting," is greatly scorned in the Portfolio world-it eats up unnecessary CPU cycles and makes your task unpopular with other developers and users around the world.
To sever the parent/child relationship between two tasks so that the child task doesn't quit with the parent task, the parent can use the SetItemOwner()
function call to transfer ownership of the child task to the child task itself. When the parent task quits, the child task continues to run.
A parent task spawns child tasks to take care of real-time processing and other operations. A child task has one big disadvantage, it doesn't share memory with the parent. Because it must allocate its own memory in pages, it can waste memory if its memory requirements are small. And because parent and child don't share memory, they can't share values stored in shared data structures. To overcome these disadvantages, a parent task can spawn a thread.
A thread is a child task that shares the parent task's memory. The owner of the thread can transfer ownership of a thread to any thread in the parent's task family except to the thread itself. A task family is a task and all of its threads.