Tasks and scheduling

All task use a common address space !!

A task can be in the following states :

  PEND   -   READY  -   DELAY 
     \                 /
           SUSPEND 

taskPrioritySet()
set priority
task(Un)Lock()
disables task rescheduling, but do not lock out interrupts However if a task blocks the scheduler runs another task.
int(Un)Lock()
taskLock with disabling interrupts Can be used for mutual exclusion, but it also locks out higher priority task without any relation to the critical region -> bad real-time response

Scheduling types :

Problem : Priority inversion
When a low priority task takes a mutex, it can block out a higher priority task which wants to take this mutex in a later instance. The higher priority task may run when the lower priority task releases the mutex. Normally this happens, but sometimes the lower priority task is preempted by a middle priority task. This middle priority task can keep the high priority task from running for a long long time. You could say that this task has virtually a higher priority : priority inversion!

Solution : Priority Inheritance
The priority-inheritance protocol assures that a task that holds a resource executes at the priority of the highest-priority task blocked on that resource. This means that our lower priority task holding the mutex, will be elevated to the priority of the higher priority task when this wants to take the mutex. When the lower priority task is running with higher priority it cannot be preempted by the middle priority task!! Thus it can quickly end its task, and release the mutex without being preempted by any task with a priority less than the higher priority. When releasing the mutex the lower priority task is set back to its lower priority, and the higher priority task is scheduled to run.

Why is priority inversion a problem?
With priority inheritance enabled one can calculate the worst case performance time of the higher priority task by taking the worst case execution time of the code it has to run plus the worst case execution time of the code between taking and releasing the mutex in the lower priority task. Without priority inheritance this would be impossible to calculate. Thus, priority inheritance makes scheduling calculations possible!!

In vxworks we can activate the priority-inheritance protocol by using a mutual-exclusion semaphore with the option SEM_INVERSION_SAFE enabled. (see below)

Task control

A task has a name and a ID.

It is also possible to add hook routines to create,switch and delete task events.

Errors and exceptions

Many functions return only the status values OK(0) or ERROR(-1). Functions that return a pointer usually NULL(0) to indicate an error.

errno
On error sometimes also a specific error code is set in "errno" variable. This global variable is never cleared by vxworks routines, thus it value always specify the last error status set.

printErrno()
A string constant associated with "errno" can be displayed with printErrno() if it has a string set in the error status symbol table statSymTbl.

Note : each interrupt and task has its own "errno" variable!!

hardware exceptions
are handled by the default exception handler in vxworks which suspends the task that caused the exception and save the state of the task. The kernel and other tasks continue uninterrupted. A description of the exceprion is send to the tornado IDE.

Task can also attach their own handlers for certain hardware exceptions through the signal facility.

Shared code

Shared code by different tasks must be reentrant. This code should use reentrent functions. Most routines in vxworks are reentrant. You should assume that if xx_r() and xx() exist xx is not reentrant and xx_r is reentrant. Vxworks I/O and driver routines are reentrant, but require careful application design.

The majority of vxworks routines use the following reentrancy techniques

System tasks

Root task : tUsrRoot
The root task is the first task executed by the kernel. It initializes vxWorks and than terminates.

Logging Task : tLogTask
The log task is used by VxWorks modules to log system messages without to having perform I/O in the current task context.

Exception Task: tExcTask
The Exception task supports the vxWorks exception handling package by perfomring functions that cannot be performed occur at interrupt level. It is also for actions that cannot be performed in the current task's context, such as task suicide. It must have the highest priority in the system.

Network Task: tNetTask
The tNetTask daemon handles the task-level functions required by the vxworks network.

Target Agent Task: tWdbTask
The target agent task is created if the target agent is set to run in task mode.

Optional tasks are tShell (target shell), tRlogind (rsh server), tTelnetd (telnet server), tPortmapd (RPC server).

Intertask communication on single CPU

Shared data

all task use the same address space

Prevent resource corruption with mutual exclusion

semaphore operations :

Coordinating multiple access to multiple copies of a resource

Synchronization task execution by Signaling

Compare binary semaphores with events

Communicating information which can also be used for synchronization task execution

note: message queues and pipes only usable within a single CPU

Signal facility

Signals are more appropriate for error and exception handling than as a general purpose intertask communication. Because signals are asynchronous, it is difficult to predict which resources might be unavailable when a particular signal is raised. Therefore it is advisable to use synchronous intertask communications primitives like semaphores, message queues and events instead.

Signal handlers should be treated like ISR, so to be perfectly safe call only those routines that can safely be called from an ISR.

A signal asynchronously alters the control flow of a task. Any task or ISR can raise a signal. The task being signaled immediately suspends its current thread of execution and executes the task-specified signal handler routine the next time it is scheduled to run!

The signal handler executes in the receiving task's context and makes use of the task's stack.

The signal handler is even invoked if the task is blocked.

operations

Network-transparant intertask communication

note: TCP/IP can be used to communicate across networks, but it is low level and not intended for real-time use -> use distributed message queues for real time intertask communication between multiple nodes

Interrupt service code

Interrupts are used for informing the system in quickly manner of external events. (e.g. sensor detected something) For the fastest possible response to interrupts, vxworks runs interrupt service routines (ISRs) in a special context outside of any task's context. Thus, interrupt handling involves no task context switch.

All ISRs use the same interrupt stack, which is allocated and initialized at system startup. It must be large enough to handle the worst possible combination of nested interrupts. Use the checkstack() facility during development to see how far it is used.

ISRs must NOT invoke routines that might cause the ISR to block!

interrupt routines

routines allowed to be called from interrupts

Interrupt-to-task Communication

The following techniques can be used to communicate from ISRs to task-level code :

Watchdog Timers

Functions invoked by watchdog timers execute as interrupt service code at the the interrupt level of the system clock. However if the kernel is unable to execute it immediately (e.g. previous interrupt or kernel state) the function is placed on the tExcTask work queue (priority of TExcTask usually 0)

operations :

Let's compare the basic vxworks clock/timer libraries with the POSIX versions supported :

Vxworks :

  1. clock :
  2. timer

POSIX :

  1. clock :
  2. timer :

Note: Watchdog is normally run at interrupt but otherwise run at priority of tExcTask usually 0. A POSIX timer is by sending a SIGALARM signal to the task, thus is handled when the task is scheduled. So watchdog events are must faster handled!

VxWorks tips

Write from vxworks client to ftp access on server (windows pc)

    FILE * fp;
    variabele = 3;
    printf("hello world\n");
  
    fp = fopen ("host:/test2.txt", "wb");
   
    fputc(fp, 61);
    fclose(fp);

Let log function vxworks write on ftp server

      fdl = open("host:/log7-1.txt",O_CREAT|O_WRONLY, 0644);
          
      if (fdl == ERROR) {
        printf("Log file niet geopend\n");
      } else {
        printf("Log file is open\n");
        logFdSet(fdl);
      }
        # works the same as printf
      logMsg("canOpen() failed with error %d!\n", retvalue);

Script to load CAN drivers

  # cat C:\tornado2.2\host\x86-win32\bin\ldcan
  
  cd  "C:/Tornado2.2/driverdisk_CAN_VW55/PENTIUM"
  ld < c200i.sys
  ld < c200iini
  ld < ntcan.o
  ld < cantest
  c200iStart

APPENDIX

intertask communication between multiple CPU's

intertask communication between multiple CPU's on multiple nodes

note: TCP/IP can be used to communicate across networks, but it is low level and not intended for real-time use