• No results found

Multitasking .4 Task Scheduling

Static Libraries, Shared Libraries, and Plug-Ins

INCLUDE_CPLUS_DEMANGLER

6 Multitasking .4 Task Scheduling

6.4.1 Task Priorities

Task scheduling relies on a task’s priority, which is assigned when it is created. The VxWorks kernel provides 256 priority levels, numbered 0 through 255. Priority 0 is the highest and priority 255 is the lowest.

While a task is assigned its priority at creation, you can change it programmatically thereafter. For information about priority assignment, see 6.5.1 Task Creation and Activation, p.108 and 6.5.9 Task Scheduling Control, p.116).

All application tasks should be in the priority range from 100 to 255.

6.4.2 VxWorks Traditional Scheduler

The VxWorks traditional scheduler provides priority-based preemptive scheduling as well as the option of programmatically initiating round-robin scheduling. The traditional scheduler may also be referred to as the original or native scheduler.

The traditional scheduler is included in VxWorks by default with the INCLUDE_VX_TRADITIONAL_SCHEDULER component.

For information about the POSIX thread scheduler and custom schedulers, see 9.15 POSIX and VxWorks Scheduling, p.189 and VxWorks Kernel Programmer’s Guide:

Kernel Customization, respectively.

Priority-Based Preemptive Scheduling

A priority-based preemptive scheduler preempts the CPU when a task has a higher priority than the current task running. Thus, the kernel ensures that the CPU is always allocated to the highest priority task that is ready to run. This means that if a task—with a higher priority than that of the current task—becomes ready to run, the kernel immediately saves the current task’s context, and switches to the context of the higher priority task. For example, in Figure 6-2, task t1 is preempted by higher-priority task t2, which in turn is preempted by t3. When t3 completes, t2 continues executing. When t2 completes execution, t1 continues executing.

The disadvantage of this scheduling policy is that, when multiple tasks of equal priority must share the processor, if a single task is never blocked, it can usurp the processor. Thus, other equal-priority tasks are never given a chance to run.

Round-robin scheduling solves this problem (for more information, see Round-Robin Scheduling, p.106).

NOTE: Network application tasks may need to run at priorities lower than 100. For information in this regard, see the VxWorks Network Stack Programmer’s Guide.

Scheduling and the Ready Queue

The VxWorks scheduler maintains a FIFO ready queue mechanism that includes lists of all the tasks that are ready to run (that is, in the ready state) at each priority level in the system. When the CPU is available for given priority level, the task that is at the front of the list for that priority level executes.

A task’s position in the ready queue may change, depending on the operation performed on it, as follows:

If a task is preempted, the scheduler runs the higher priority task, but the preempted task retains its position at the front of its priority list.

If a task is pended, delayed, suspended, or stopped, it is removed from the ready queue altogether. When it is subsequently ready to run again, it is placed at the end of its ready queue priority list. (For information about task states and the operations that cause transitions between them, see 6.2.1 Task States and Transitions, p.96).

If a task’s priority is changed with taskPrioritySet( ), it is placed at the end of its new priority list.

If a task’s priority is temporarily raised based on the mutual-exclusion semaphore priority-inheritance policy (using the SEM_INVERSION_SAFE option), it returns to the end of its original priority list after it has executed at the elevated priority. (For more information about the mutual exclusion semaphores and priority inheritance, see Priority Inheritance Policy, p.132.)

Round-Robin Scheduling

VxWorks provides a round-robin extension to priority-based preemptive scheduling. Round-robin scheduling accommodates instances in which there are more than one task of a given priority that is ready to run, and you want to share the CPU amongst these tasks.

The round-robin algorithm attempts to share the CPU amongst these tasks by using time-slicing. Each task in a group of tasks with the same priority executes for a defined interval, or time slice, before relinquishing the CPU to the next task in the group. No one of them, therefore, can usurp the processor until it is blocked. See

Figure 6-2 Priority Preemption

t1

KEY: = preemption time HIGH

LOW

priority

t3

t2

= task completion

t1 t2

6 Multitasking 6.4 Task Scheduling

Figure 6-3 for an illustration of this activity. When the time slice expires, the task moves to last place in the ready queue list for that priority (for information about the ready queue, see Scheduling and the Ready Queue, p.106).

Note that while round-robin scheduling is used in some operating systems to provide equal CPU time to all tasks (or processes), regardless of their priority, this is not the case with VxWorks. Priority-based preemption is essentially unaffected by the VxWorks implementation of round-robin scheduling. Any higher-priority task that is ready to run immediately gets the CPU, regardless of whether or not the current task is done with its slice of execution time. When the interrupted task gets to run again, it simply continues using its unfinished execution time.

It may be useful to use round-robin scheduling in systems that execute the same application in more than one process. In this case, multiple tasks would be executing the same code, and it is possible that a task might not relinquish the CPU to a task of the same priority running in another process (running the same binary).

Note that round-robin scheduling is global, and controls all tasks in the system (kernel and processes); it is not possible to set round-robin scheduling for selected processes.

Enabling Round-Robin Scheduling

Round-robin scheduling is enabled by calling kernelTimeSlice( ), which takes a parameter for a time slice, or interval. It is disabled by using zero as the argument to kernelTimeSlice( ).

Time-slice Counts and Preemption

The time-slice or interval defined with a kernelTimeSlice( ) call is the amount of time that each task is allowed to run before relinquishing the processor to another equal-priority task. Thus, the tasks rotate, each executing for an equal interval of time. No task gets a second slice of time before all other tasks in the priority group have been allowed to run.

If round-robin scheduling is enabled, and preemption is enabled for the executing task, the system tick handler increments the task’s time-slice count. When the specified time-slice interval is completed, the system tick handler clears the counter and the task is placed at the end of the ready queue priority list for its priority level. New tasks joining a given priority group are placed at the end of the priority list for that priority with their run-time counter initialized to zero.

Enabling round-robin scheduling does not affect the performance of task context switches, nor is additional memory allocated.

If a task blocks or is preempted by a higher priority task during its interval, its time-slice count is saved and then restored when the task becomes eligible for execution. In the case of preemption, the task resumes execution once the higher priority task completes, assuming that no other task of a higher priority is ready to run. In the case where the task blocks, it is placed at the end of the ready queue list for its priority level. If preemption is disabled during round-robin scheduling, the time-slice count of the executing task is not incremented.

Time-slice counts are accrued by the task that is executing when a system tick occurs, regardless of whether or not the task has executed for the entire tick interval. Due to preemption by higher priority tasks or ISRs stealing CPU time from the task, it is possible for a task to effectively execute for either more or less total CPU time than its allotted time slice.

Figure 6-3 shows round-robin scheduling for three tasks of the same priority: t1, t2, and t3. Task t2 is preempted by a higher priority task t4 but resumes at the count where it left off when t4 is finished.

6.5 Task Creation and Management

The following sections give an overview of the basic VxWorks task routines, which are found in the VxWorks library taskLib. These routines provide the means for task creation and control, as well as for retrieving information about tasks. See the VxWorks API reference for taskLib for further information.

For interactive use, you can control VxWorks tasks with the host tools or the kernel shell; see the Wind River Workbench by Example guide, the Wind River Workbench Host Shell User’s Guide, and VxWorks Kernel Programmer’s Guide: Target Tools.

6.5.1 Task Creation and Activation

The routines listed in Table 6-2 are used to create tasks.

The arguments to taskSpawn( ) are the new task’s name (an ASCII string), the task’s priority, an options word, the stack size, the main routine address, and 10 arguments to be passed to the main routine as startup parameters:

id = taskSpawn ( name, priority, options, stacksize, main, arg1, …arg10 );

Note that a task’s priority can be changed after it has been spawned; see 6.5.9 Task Scheduling Control, p.116.

The taskSpawn( ) routine creates the new task context, which includes allocating the stack and setting up the task environment to call the main routine (an ordinary subroutine) with the specified arguments. The new task begins execution at the entry to the specified routine.

Figure 6-3 Round-Robin Scheduling

t1

KEY: = preemption

time HIGH

LOW

priority

t2 t3 t1

t4

t2 t2

= task completion time slice

t3

6 Multitasking