Вы находитесь на странице: 1из 32

PROJECT REPORT ON

VxWORKS

-:BY:-
ABHAY KUMAR
AJAY KUMAR PANCHORE
REEP JYOTI DEKA
KAUSHIK DAS
SREEJITH S NAIR
ACKNOWLEGEMENT

It is a great privilege for us to express our deep sense of gratitude to our guide, Mr.
K.L. Baishnab of Electronics & Communication Engineering Department, NIT Silchar
for his stimulating guidance and profound assistance. I shall always cherish my
association with him for his constant encouragement and freedom to thought and
action that he rendered to me throughout this project.
Finally, I deem it a great pleasure to thank one and all those who helped me carry out
this project.
VxWorks
VxWorks is a real-time operating system(RTOS) made and sold by Wind River Systems of Alameda,
California, USA. Intel acquired Wind River Systems on July 17, 2009.

VxWorks is designed for use in embedded systems. Unlike "self-hosting" systems such as Unix,
VxWorks development is done on a "host" machine running Linux, Unix, or Windows, cross-
compiling target software to run on various "target" CPU architectures.

History
VxWorks started as an improvement on a primitive ROM chip with a 4K kernel sold by Ready
Systems, now a Mentor Graphics product. It contained VRTX, which lacked everything from a file
system to a development environment. Wind River created an accessory called VxWorks that turned
the VRTX kernel into an OS and a development environment. Fiddler and Wilner had negotiated an
agreement to sell VRTX with VxWorks. In 1987, anticipating that Ready Systems was going to cancel
its reseller contract, Wind River developed its own kernel and a smaller, faster, lighter OS – written
by an intern.

The name VxWorks is believed to be a pun (VRTX Works) on the VRTX real-time operating system.

OS overview
VxWorks has been ported to a number of platforms and now runs on practically any modern CPU
that is used in the embedded market. This includes the x86 family, MIPS, PowerPC, Freescale
ColdFire, Intel i960, SH-4 and the closely related family of ARM, StrongARM and xScale CPUs.

The key features of the current OS are:

 Multitasking kernel with preemptive and round-robin scheduling and fast interrupt response
 Memory protection to isolate user applications from the kernel
 SMP support
 Fast, flexible inter-process communication including TIPC
 Error handling framework
 Binary, counting, and mutual exclusion semaphores with priority inheritance
 Local and distributed message queues
 POSIX PSE52 certified conformance
 File system
 IPv6 networking stack
 VxSim simulator
INTRODUCTION ABOUT OUR PROJECT
In this project we explored the VxWorks software basics. First we studied a no. of
embedded system architecture and different types of OS like REAL-TIME OS NON
REAL-TIME OS and their difference.

Since we are new in this software so we have decided to learn the basics of software
like creating new functions , different controls like time control, multitasking, signals
handling etc.

In this whole project we create a no of basic function which is used in embedded


system programming. We have done it in Assignment or Experimental way as we
learn it in step by step so we are presenting it in the same way that we have learned
it.

There are 3 basic work we have learn in this project.

1. Where to write and how to write the code.


2. How to run the codes and how to simulate the code on simulator
(ex. VxSim0, VxSim1_SMP (VxWorks6.6)).
3. How to analyze the output on the simulator.

FROM NEXT PAGE WE ARE SHOWING SOME OF THE BASIC


FUNTIONS OF VxWorks.
Index
1. Timing
2. Multi Tasking
3. Semaphores
4. Message Queues
5. Round Robin Task Scheduling
6. Preemptive Priority Based Task Scheduling
7. Priority Inversion
8. Signals
9. Interrupts Routine Service
Experiment no 1.
Timing

Introduction
To understand and optimize the performance of a real-time system, it can be useful to time some of
the VxWorks and application functions. VxWorks provides a number of timing facilities to help with
this task.

The VxWorks execution timer can time any subroutine or group of subroutines. To time very fast
subroutines, the timer can also repeatedly execute a group of functions until the time of a single
iteration is known with reasonable certainty.

Objectives
The following are the primary objectives of this experiment:

 To demonstrate how to time a single subroutine using the VxWorks timex() routine.

Description
The timex() routine times a single execution of a specified function with up to eight integer
arguments to be passed to the function. When execution us complete, timex() routine displays the
execution time and a margin of error in miliseconds. If the execution was so fast relative to the clock
rate that the time is meaningless(error > 50%), a warning message will appear. In such cases, use
timexN() which will repeatedly execute the function until the time of a single iteration is known with
reasonable certainty.

1. Syntax

void timex(FUNCPTR function_name, int arg1, .., int arg8)


Note: the first argument in timex() routine is a pointer to the function to be timed.

2. Example This small example has two subroutines. The first subroutine "timing" makes a call to
timex() with the function name "printit" which is the subroutine to be timed. The arguments are all
NULL, so no parameters are being passed to "printit".

The second subroutine, "printit", which is being timed iterates 200 times while printing its task
id(using taskIdSelf()) and the increment variable "i".
Example Programs Codes
#include "vxWorks.h"
#include "timexLib.h"
#include "stdio.h"

#define ITERATIONS 200

int printit(void);

void timing() /* Function to perform the timing */


{
FUNCPTR function_ptr = printit; /* a pointer to the function "printit" */
timex(function_ptr,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); /* Timing the
"print" function */
}

int printit(void) /* Function being timed */


{
int i;
for(i=0; i < ITERATIONS; i++) /* Printing the task id number and the
increment variable "i" */
printf("Hello, I am task %d and is i = %d\n",taskIdSelf(),i);
return 0;
}

Note:-
The timings measure the execution time of the routine body, without the usual subroutine
entry and exit code. Also, the time required to set up the arguments and call the routines is
not included in the reported times. This is because the timing routines automatically
calibrate themselves by timing the invocation of a null routine, and thereafter subtracting
that constant overhead.
Experiment no 2.
Multi Tasking

Introduction
Modern real-time systems are based on the complementary concepts of multitasking and intertask
communications. A multitasking environment allows real-time applications to be constructed as a
set of independent tasks, each with a separate thread of execution and its own set of system
resources. The intertask communication facilities allow these tasks to synchronize and coordinate
their activities.

The VxWorks multitasking kernel, wind, uses interrupt-driven, priority- based task
scheduling. It features fast context switch time and low interrupt latency.

Objectives
The following are the primary objectives of this experiment:

 How to initiate multiple processes using Vxworks tasking routines.

Description
Multitasking creates the appearance of many threads of execution running concurrently when, in
fact, the kernel interleaves their execution on a basis of ascheduling algorithm. Each apparently
independent program is called a task. Each task has its own context, which is the CPU environment
and system resources that the task sees each time it is scheduled to run by the kernel.

On a context switch, a task's context is saved in the Task Control Block(TCB). A task's context
includes:

 a thread of execution, that is, the task's program counter


 the CPU registers and floating-point registers if necessary
 a stack of dynamic variables and return addresses of function calls
 I/O assignments for standard input, output, error
 a delay timer
 a timeslice timer
 kernel control structures
 signal handlers
 debugging and performance monitoring values
1. Task Creation and Activation

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

The arguments to taskSpawn() are the new task's name(an ASCII string), priority, an "options" word(also hex
value), stack size(int), main routine address(also main routine name), and 10 integer arguments to be passed
to the main routine as startup parameters.

2. Syntax

id = taskSpawn(name,priority,options,stacksize,function, arg1,..,arg10);

3. Example

This example creates ten tasks which all print their task Id once:

Example Programs Codes


/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "semLib.h"
#include "stdio.h"

#define ITERATIONS 10

void print(void);

spawn_ten() /* Subroutine to perform the spawning */


{
int i, taskId;
for(i=0; i < ITERATIONS; i++) /* Creates ten tasks */
taskId =
taskSpawn("tprint",90,0x100,2000,print,0,0,0,0,0,0,0,0,0,0);
}

void print(void) /* Subroutine to be spawned */


{
printf("Hello, I am task %d\n",taskIdSelf()); /* Print task Id */
}
Experiment no 3.
Semaphores
Introduction
Semaphores permit multitasking applications to coordinate their activities. The most obvious way
for tasks to communicate is via various shared data structures. Because all tasks in VxWorks exist in
a single linear address space, shared data structures between tasks is trivial. Global variables, linear
buffers, ring buffers, link lists, and pointers can be referenced directly by code running in different
context.

However, while shared address space simplifies the exchange of data, interlocking access to
memory is crucial to avoid contention. Many methods exist for obtaining exclusive access to
resources, and one of them is semaphores.

Objectives
The following are the primary objectives of this experiment:

 To demonstrate the use of VxWorks semaphores.

Description
VxWorks semaphores are highly optimized and provide the fastest intertask communication
mechanisms in VxWorks. Semaphores are the primary means for addressing the requirements of
both mutual exclusion and task synchronization.

The are three types of Wind semaphores, optimized to address different classes of problems:

binary

 The fastest, most general purpose semaphore. Optimized for synchronization and can also
be used for mutual exclusion.

mutual exclusion

 A special binary semaphore optimized for problems inherent in mutual exclusion: priority
inheritance, deletion safety and recursion.

counting

 Like the binary semaphore, but keeps track of the number of times the semaphore is given.
Optimized for guarding multiple instances of a resource.
1. Semaphore Control

Wind semaphores provide a single uniform interface for semaphore control. Only the creation
routines are specific to the semaphore type:

 semBCreate(int options, SEM_B_STATE initialState ): Allocate and intialize a binary semaphore.

 semMCreate(int options): Allocate and intialize a mutual exclusion semaphore.

 semCCreate(int options, int initialCount): Allocate and intialize a counting semaphore.

 semDelete(SEM_ID semId): Terminate and free a semaphore.

 semTake(SEM_ID semId, int timeout): Take a semaphore.

 semGive(SEM_ID semId): Give a semaphore.

 semFlush(SEM_ID semId): Ublock all tasks waiting for a semaphore.

Please refer to the VxWorks Reference Manual for valid arguments in the above routines.

2. Example: Binary Semaphore

A binary semaphore can be viewed as a flag that is available or unavailable. When a task takes a
binary semaphore, using semTake(), the outcome depends on whether the semaphore is available
or unavailable at the time of the call. If the semaphore is available, then the semaphore becomes
unavailable and then the task continues executing immediately. If the semaphore is unavailable, the
task is put on a queue of blocked tasks and enters a state of pending on the availability of the
semaphore.

When a task gives a binary semaphore, using semGive(), the outcome depends on whether the
semaphore is available or unavailable at the time of the call.If the semaphore is already available,
giving the semaphore has no effect at all. If the semaphore is unavailable and no task is waiting to
take it, then the semaphore becomes available. If the semaphore is unavailable and one or more
tasks are pending on its availablity, then the first task in the queue of pending tasks is unblocked,
and the semaphore is left unavailable.

In the example below, two tasks(taskOne and taskTwo), are competing to update the value of a
global variable, called "global." The objective of the program is to toggle the value of the global
variable(1s and 0s). taskOne changes the value of "global" to 1 and taskTwo changes the value back
to 0.Without the use of the semaphore, the value of "global" would be random and the value of
"global" would be corrupted.
Example Programs Codes
/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "semLib.h"
#include "stdio.h"

/* function prototypes */
void taskOne(void);
void taskTwo(void);

/* globals */
#define ITER 10
SEM_ID semBinary;
int global = 0;

void binary(void)
{
int taskIdOne, taskIdTwo;

/* create semaphore with semaphore available and queue tasks on FIFO basis */
semBinary = semBCreate(SEM_Q_FIFO, SEM_FULL);

/* Note 1: lock the semaphore for scheduling purposes */


semTake(semBinary,WAIT_FOREVER);

/* spawn the two tasks */


taskIdOne = taskSpawn("t1",90,0x100,2000,(FUNCPTR)taskOne,0,0,0,0,0,0,0,0,0,0);
taskIdTwo = taskSpawn("t2",90,0x100,2000,(FUNCPTR)taskTwo,0,0,0,0,0,0,0,0,0,0);
}
void taskOne(void)
{
int i;
for (i=0; i < ITER; i++)
{
semTake(semBinary,WAIT_FOREVER); /* wait indefinitely for semaphore */
printf("I am taskOne and global = %d............\n", ++global);
semGive(semBinary); /* give up semaphore */
}
}
void taskTwo(void)
{
int i;
semGive(semBinary); /* Note 2: give up semaphore(a scheduling fix) */
for (i=0; i < ITER; i++)
{
semTake(semBinary,WAIT_FOREVER); /* wait indefinitely for semaphore */
printf("I am taskTwo and global = %d-----------\n", --global);
semGive(semBinary); /* give up semaphore */
}
}
Experiment no 4.
Message Queues

Introduction
In VxWorks, the primary intertask communication mechanism within a single CPU is message
queues. Message queues allow a variable number of messages, each of variable length, to be
queued(FIFO or priority based). Any task can send a message to a message queue and any
task can receive a message from a message queue. Multiple tasks can send to and receive
from the same message queue. Two way communication between two tasks generally
requires two message queues, one for each direction.

Objectives
The following are the primary objectives of this experiment:

 To demonstrate the use of VxWorks message queues.

Description
Wind message queues are created and deleted with the following routines:

 msgQCreate(int maxMsgs, int maxMsgLength, int options): Allocate and initialize a message
queue.

 msgQDelete(MSG_Q_ID msgQId): Terminate and free a message queue.

 msgQSend(MSG_Q_ID msgQId, char *Buffer, UINT nBytes, int timeout, int priority): Send a
message to a message queue.

 msgQReceive(MSG_Q_ID msgQId, char *Buffer, UINT nBytes, int timeout) Send a message to
a message queue.

This library provides messages that are queued in FIFO order, with a single exception: there are two
priority levels, and messages marked as high priority are attached to the head of the queue.
A message queue is created with msgQCreate(). Its parameters specify the maximum number of
messages that can be queued in the message queue and the maximum length in bytes of each
message.

A task sends a message to a message queue with msgQSend(). If no tasks are waiting for messages
on that queue, the message is added to the queue's buffer of messages. If any tasks are waiting for a
message from that message queue, the message is immediately delivered to the first waiting task.
A task receives a message from a message queue with msgQReceive(). If messages are already
available in the queue's buffer, the first message is immediately dequeued and returned to the
caller. If no messages are available, then the calling task blocks and is added to a queue of tasks
waiting for messages. The queue of waiting tasks can be ordered either by task priority or FIFO, as
specified when the queue is created.

 Timeouts: Both msgQSend() and msgQReceive() take timeout parameters. The timeout
parameter specifies how many ticks(clock ticks per second) to wait for space to be available
when sending a message and to wait for a message to be available when receiving a
message.

 Urgent Messages: The msgQSend() function can specify the priority of a message either as
normal MSG_PRI_NORMAL or urgent MSG_PRI_URGENT. Normal priority messages are
added to the tail of the message queue, while urgent priority messages are added to the
head of the message queue.

1. Example of Wind message Queues

Example programs codes


/* includes */
#include "vxWorks.h"
#include "msgQLib.h"

/* function prototypes */
void taskOne(void);
void taskTwo(void);

/* defines */
#define MAX_MESSAGES 100
#define MAX_MESSAGE_LENGTH 50

/* globals */
MSG_Q_ID mesgQueueId;

void message(void) /* function to create the message queue and two tasks
*/
{
int taskIdOne, taskIdTwo;

/* create message queue */


if ((mesgQueueId = msgQCreate(MAX_MESSAGES,MAX_MESSAGE_LENGTH,MSG_Q_FIFO))
== NULL)
printf("msgQCreate in failed\n");

/* spawn the two tasks that will use the message queue */
if((taskIdOne =
taskSpawn("t1",90,0x100,2000,(FUNCPTR)taskOne,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskOne failed\n");
if((taskIdTwo =
taskSpawn("t2",90,0x100,2000,(FUNCPTR)taskTwo,0,0,0,0,0,0,0,0,0,0)) ==
ERROR)
printf("taskSpawn taskTwo failed\n");
}

void taskOne(void) /* task that writes to the message queue */


{
char message[] = "Received message from taskOne";

/* send message */
if((msgQSend(mesgQueueId,message,MAX_MESSAGE_LENGTH, WAIT_FOREVER,
MSG_PRI_NORMAL)) == ERROR)
printf("msgQSend in taskOne failed\n");
}

void taskTwo(void) /* tasks that reads from the message queue */


{
char msgBuf[MAX_MESSAGE_LENGTH];

/* receive message */
if(msgQReceive(mesgQueueId,msgBuf,MAX_MESSAGE_LENGTH, WAIT_FOREVER)
== ERROR)
printf("msgQReceive in taskTwo failed\n");
else
printf("%s\n",msgBuf);
msgQDelete(mesgQueueId); /* delete message queue */
}
Experiment no 5.
Round Robin Task Scheduling
Introduction
Task scheduling is the assignment of starting and ending times to a set of tasks, subject to certain
constraints. Constraints are typically either time constraints or resource constraints. On a time-
sharing operating system, running each active process in turn for its share of time (its "timeslice"),
thus creating the illusion that multiple processes are running simultaneously on a single processor.

Wind task scheduling uses a priority based preemptive scheduling algorithm as default, but it can
also accommodate round-robin scheduling.

Objectives
The following are the primary objectives of this experiment:

 To demonstrate the use of VxWorks round-robin task scheduling facilities.

Description
 Round-Robin Scheduling

A round-robin scheduling algorithm attempts to share the CPU fairly among all ready tasks of
the same priority. Without round-robin scheduling, when multiple tasks of equal priority
must share the processor, a single task can usurp the processor by never blocking, thus never
giving other equal priority tasks a chance to run.

Round-robin scheduling achieves fair allocation of the CPU to tasks of the same priority by an
approach known as time slicing. Each task executes for a defined interval or time slice; then
another task executes for an equal interval, in rotation. The allocation is fair in that no task
of a priority group gets a second slice of time before the other tasks of a group are given a
slice.

Round-robin scheduling can be enabled with routine kernelTimeSlice(), which takes a


parameter for a time slice, or interval. The interval is the amount of time each task is allowed
to run before relinquishing the processor to another equal priority task.

The following routine controls round-robin task scheduling:

 kernelTimeSlice(int ticks): Control round-robin scheduling. The number of ticks(60 ticks


equate to one second) determine the duration of the time slice.
1. Example: Round-robin Based Scheduling

In the example below, three tasks with the same priority print their task ids and task names on the
console. Without round-robin scheduling, "taskOne" would usurp the processor until it was finished,
and then "taskTwo" and "taskThree" would do likewise. In the event that "taskOne" was looping
indefinitely, the other tasks would never get a chance to run.

To insure that the tasks get an equal share of the CPU time, a call is made to kernelTimeSlice(). This
sets the time slice interval value to TIMESLICE. The TIMESLICE value is the time slice interval in
terms of the number of clock ticks(which in the example and the M68040 is 60 ticks which is
equivalent to one second). The sysClkRateGet() can be used to determine the number of clock ticks
per second.

Having setup the time slice in the manner above, the three tasks are spawned. However, here a few
implementation details that should be noted:

 Make sure that sched has a higher priority than the tasks it is spawning! Unless otherwise
specified, tasks have a default priority of 100. Notice that taskOne, taskTwo, and taskThree
all have priorities of 101, which makes them lower in priority than sched.

 Yow must allow enough time for the context switches to occur. Thus the reason for -> for
(j=0; j < LONG_TIME; j++);

 Using printf is not ideal in the example, because it can block .This will of course cause a task
transition which will upset the nice round robin picture. Instead use logMsg() (see vxWorks
reference manual for details). The latter won't block unless the log message queue is full.

Example programs codes

/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "kernelLib.h"
#include "sysLib.h"
#include "logLib.h"

/* function prototypes */
void taskOne(void);
void taskTwo(void);
void taskThree(void);

/* globals */
#define ITER1 100
#define ITER2 10
#define PRIORITY 101
#define TIMESLICE sysClkRateGet()
#define LONG_TIME 1000000
void sched(void) /* function to create the three tasks */
{
int taskIdOne, taskIdTwo, taskIdThree;

if(kernelTimeSlice(TIMESLICE) == OK) /* turn round-robin on */


printf("\n\n\n\n\t\t\tTIMESLICE = %d seconds\n\n\n", TIMESLICE/60);

/* spawn the three tasks */


if((taskIdOne =
taskSpawn("task1",PRIORITY,0x100,20000,(FUNCPTR)taskOne,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskOne failed\n");
if((taskIdTwo =
taskSpawn("task2",PRIORITY,0x100,20000,(FUNCPTR)taskTwo,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskTwo failed\n");
if((taskIdThree =
taskSpawn("task3",PRIORITY,0x100,20000,(FUNCPTR)taskThree,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskThree failed\n");

void taskOne(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0); /* log messages */
for (j=0; j < LONG_TIME; j++); /* allow time for context switch */
}
}
void taskTwo(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0); /* log messages */
for (j=0; j < LONG_TIME; j++); /* allow time for context switch */
}
}
void taskThree(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0); /* log messages */
for (j=0; j < LONG_TIME; j++); /* allow time for context switch */
}
}
Experiment no 6.
Preemptive Priority Based Task Scheduling
Introduction
Task scheduling is the assignment of starting and ending times to a set of tasks, subject to certain
constraints. Constraints are typically either time constraints or resource constraints. On a time-
sharing operating system, running each active process in turn for its share of time (its "timeslice"),
thus creating the illusion that multiple processes are running simultaneously on a single processor.

Wind task scheduling uses a priority based preemptive scheduling algorithm as default, but it can
also accommodate round-robin scheduling.

Objectives
The following are the primary objectives of this experiment:

 To demonstrate the use of VxWorks preemptive priority based task scheduling facilities.

Description
 Preemptive Priority Based Scheduling

With a preemptive priority based scheduler, each task has a priority and the kernel insures
that the CPU is allocated to the highest priority task that is ready to run. This scheduling
method is preemptive in that if a task that has a higher priority than the current task
becomes ready to run, the kernel immediately saves the current tasks's context and switches
to the context of the higher priority task.

The Wind kernel has 256 priority levels(0-255). Priority 0 is the highest and priority 255 is the
lowest. Tasks are assigned a priority when created; however, while executing, a task can
change its priority using taskPrioritySet().
1. Example: Preemptive Priority Based Scheduling

One of the arguments to taskSpawn() is the priority at which the task is to execute:

id = taskSpawn(name, priority, options, stacksize, function, arg1,.. , arg10);


By varying the priority(0-255) of the task spawned, you can affect the priority of the task. Priority 0
is the highest and priority 255 is the lowest.The Note the priority of a task is relative to the priorities
of other tasks. In other words, the task priority number itself has no particular significance by itself.

In addition a task's priority can be changed after its spawned using the following routine:

 taskPrioritySet(int tid, int newPriority): Change the priority of a task. In the


example below, there are three tasks with different priorities(HIGH,MID,LOW). The result of
running the program is that the task with the highest priority, "taskThree" will run to
completion first, followed by the next highest priority task, "taskTwo", and the finally the
task with the lowest priority which is "taskOne."

Example programs codes


/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "logLib.h"

/* function prototypes */
void taskOne(void);
void taskTwo(void);
void taskThree(void);

/* globals */
#define ITER1 100
#define ITER2 1
#define LONG_TIME 1000000
#define HIGH 100 /* high priority */
#define MID 101 /* medium priority */
#define LOW 102 /* low priority */

void sched(void) /* function to create the two tasks */


{
int taskIdOne, taskIdTwo, taskIdThree;
printf("\n\n\n\n\n");
/* spawn the three tasks */
if((taskIdOne = taskSpawn("task2",LOW,0x100,20000,(FUNCPTR)taskOne,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskOne failed\n");
if((taskIdTwo = taskSpawn("task2",MID,0x100,20000,(FUNCPTR)taskTwo,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskTwo failed\n");
if((taskIdThree =
taskSpawn("task3",HIGH,0x100,20000,(FUNCPTR)taskThree,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn taskThree failed\n");
}
void taskOne(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0);
for (j=0; j < LONG_TIME; j++);
}
}
void taskTwo(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0);
for (j=0; j < LONG_TIME; j++);
}
}

void taskThree(void)
{
int i,j;
for (i=0; i < ITER1; i++)
{
for (j=0; j < ITER2; j++)
logMsg("\n",0,0,0,0,0,0);
for (j=0; j < LONG_TIME; j++);
}
}
Experiment no 7.
Priority Inversion
Introduction
Priority inversion occurs when a higher-priority task is forced to wait an indefinite period for the
completion of a lower priority task. For example, prioHigh, prioMedium,and prioLow are task of
high, medium, and low priority, respectively. prioLow has acquired a resource by taking its
associated binary semaphore. When prioHigh preempts prioLow and contends for the resource by
taking the same semaphore, it becomes blocked. If prioHigh would be blocked no longer than the
time it normally takes prioLow to finish with the resource, there would be no problem, because the
resource can't be preempted. However, the low priority task is vulnerable to preemption by the
medium priority task, prioMedium, which could prevent prioLow from relinquishing the resource.
This condition could persist, blocking prioHigh for an extensive period of time.

Objectives
The following are the primary objectives of this experiment:

 To demonstrate VxWorks' priority inversion avoidance mechanisms.

Description
To address the problem of priority inversion, VxWorks provides an additional option when using
mutual exclusion semaphores. This option is SEM_INVERSION_SAFE which enables a priority
inheritance algorithm. This algorithm insures that the task that owns a resource executes at the
priority of the highest priority task blocked on that resource. When execution is complete, the task
relinquishes the resource and returns to its normal priority. Therefore, the inheriting task is
protected from preemption by an intermediate priority task. This option must be used in
conjunction with SEM_Q_PRIORITY:

semId = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE);


1. Example:

The example below illustrates a typical situation in which priority inversion takes place. Here is the
what happens:

1. prioLow task locks the semaphore.

2. prioLow task gets preempted by prioMedium task which runs for a long time which results in the
blocking of prioLow.
3. prioHigh task preempts prioMedium task and tries to lock the semaphore which is currently
locked by prioLow.

Since both prioLow and prioHigh are both blocked, prioMedium runs to completion(a very long
time). By the time prioHigh runs it is likely that it has missed its timing requirements.

Example programs codes


/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "semLib.h"

/* function prototypes */
void prioHigh(void);
void prioMedium(void);
void prioLow(void);

/* globals */
#define ITER 3
#define HIGH 102 /* high priority */
#define MEDIUM 103 /* medium priority */
#define LOW 104 /* low priority */
#define LONG_TIME 3000000
SEM_ID semMutex;

void inversion(void) /* function to create the three tasks */


{
int i, low, medium, high;
printf("\n\n....................##RUNNING##.....................\n\n\n");

/* create semaphore */
semMutex = semMCreate(SEM_Q_PRIORITY); /* priority based semaphore */

/* spawn the three tasks */


if((low = taskSpawn("task1",LOW,0x100,20000,(FUNCPTR)prioLow,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn prioHigh failed\n");
if((medium =
taskSpawn("task2",MEDIUM,0x100,20000,(FUNCPTR)prioMedium,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn prioMedium failed\n");
if((high = taskSpawn("task3",HIGH,0x100,20000,(FUNCPTR)prioHigh,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn prioLow failed\n");
}

void prioLow(void)
{
int i,j;
for (i=0; i < ITER; i++)
{
semTake(semMutex,WAIT_FOREVER); /* wait indefinitely for semaphore */
printf("Low priority task locks semaphore\n");
for (j=0; j < LONG_TIME; j++);
printf("Low priority task unlocks semaphore\n");
semGive(semMutex); /* give up semaphore */
}
printf("..................................Low priority task exited\n");
}

void prioMedium(void)
{
int i;
taskDelay(20);/*allow time for task with the low priority to seize semaphore */
for (i=0; i < LONG_TIME*10; i++)
{
if ((i % LONG_TIME) == 0)
printf("Medium task running\n");
}
printf("----------------------------------Medium priority task exited\n");
}

void prioHigh(void)
{
int i,j;
taskDelay(30);/* allow time for task with the lowe priority to seize semaphore */
for (i=0; i < ITER; i++)
{
printf("High priority task trys to lock semaphore\n");
semTake(semMutex,WAIT_FOREVER); /* wait indefinitely for semaphore */
printf("High priority task locks semaphore\n");
for (j=0; j < LONG_TIME; j++);
printf("High priority task unlocks semaphore\n");
semGive(semMutex); /* give up semaphore */
}
printf(".......................................High priority task exited\n");
}
Experiment no 8.
Signals
Introduction
A signal is a software notification to a task or a process of an event. A signal is generated when the
event that causes the signal occurs. A signal is delivered when a task or a process takes action based
on that signal. The lifetime of a signal is the interval between its generation and its delivery. A signal
that has been generated but not yet delivered is pending. There may be considerable time between
signal generation and signal delivery.

VxWorks supports a software signal facility. The signals asynchronously alter the control flow of
task. Any task can raise a signal for a particular task. The task being signaled immediately suspends
its current thread of execution and the task specified signal handler routine is executed the next
time the task is scheduled to run. The signal handler gets invoked even if the task is blocked on
some action or event. The signal handler is a user supplied routine that is bound to a specific signal
and performs whatever actions are necessary whenever the signal is received. Signals are most
appropriate for error and exception handling, rather than general intertask communication.

The Wind kernel has both BSD 4.3 and POSIX signal interface. The POSIX interface provides a
standardized interface which is more functional than BSD 4.3 interface. Your application should use
only one interface and not mix the two.

Objectives
The following are the primary objectives of this experiment:

 To demonstrate VxWorks' implementation of POSIX signal routines.

Description
The signal facility provides a set of 31 distinct signals(see VxWorks Manual). A signal can be raised by
calling kill(), which is analogous to an interrupt or hardware exception. A signal is bound to a
particular signal with sigaction(). While the signal handler is running, other signals are blocked from
delivery. Tasks can block the occurence of certain signals with sigprocmask(); if a signal is blocked
when it is raised, its handler routine will be called when the signal becomes unblocked.
Signal handlers are typically defined as:

void sigHandlerFunction(int signalNumber)


{
.............. /* signal handler code */
..............
..............
}
where signalNumber is the signal number for which sigHandlerFunction is to be invoked for.The
sigaction function installs signal handlers for a task:

 int sigaction(int signo, const struct sigaction *pAct, struct sigaction *pOact)

A data structure of type struct sigaction holds the handler information. The sigaction call has three
parameters: the signal number to be caught, a pointer to the new handler structure(of type struct sigaction),
and a pointer to the old structure(also of type struct sigaction). If the program does not need the value of the
old handler(*pOact), pass a NULL pointer for *pOact.

To direct a specific signal to a specific task, the kill(int, int) call is made where the first argument the task id
to send signal to, and the second argument is the signal to send to the task .

1. Example:

In the example below, the "sigGenerator" function generates the SIGINT or Ctrl-C signal, and directs the
signal to the "sigCatcher" task. When "sigCatcher" receives the signal, it suspends its normal execution and
branches to a signal hander that it has installed(catchSIGINT function).

Example programs codes


/* includes */
#include "vxWorks.h"
#include "sigLib.h"
#include "taskLib.h"
#include "stdio.h"

/* function prototypes */
void catchSIGINT(int);
void sigCatcher(void);

/* globals */
#define NO_OPTIONS 0
#define ITER1 100
#define LONG_TIME 1000000
#define HIGHPRIORITY 100
#define LOWPRIORITY 101
int ownId;

void sigGenerator(void) /* task to generate the SIGINT signal */


{
int i, j, taskId;
STATUS taskAlive;

if((taskId =
taskSpawn("signal",100,0x100,20000,(FUNCPTR)sigCatcher,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
printf("taskSpawn sigCatcher failed\n");

ownId = taskIdSelf(); /* get sigGenerator's task id */

taskDelay(30); /* allow time to get sigCatcher to run */

for (i=0; i < ITER1; i++)


{
if ((taskAlive = taskIdVerify(taskId)) == OK)
{
printf("+++++++++++++++++++++++++++++++SIGINT sinal generated\n");
kill(taskId, SIGINT); /* generate signal */
/* lower sigGenerator priority to allow sigCatcher to run */
taskPrioritySet(ownId,LOWPRIORITY);
}
else /* sigCatcher is dead */
break;
}
printf("\n***************sigGenerator Exited***************\n");
}

void sigCatcher(void) /* task to handle the SIGINT signal */


{
struct sigaction newAction;
int i, j;

newAction.sa_handler = catchSIGINT; /* set the new handler */


sigemptyset(&newAction.sa_mask); /* no other signals blocked */
newAction.sa_flags = NO_OPTIONS; /* no special options */

if(sigaction(SIGINT, &newAction, NULL) == -1)


printf("Could not install signal handler\n");

for (i=0; i < ITER1; i++)


{
for (j=0; j < LONG_TIME; j++);
printf("Normal processing in sigCatcher\n");
}

printf("\n+++++++++++++++sigCatcher Exited+++++++++++++++\n");
}

void catchSIGINT(int signal) /* signal handler code */


{
printf("-------------------------------SIGINT signal caught\n");
/* increase sigGenerator priority to allow sigGenerator to run */
taskPrioritySet(ownId,HIGHPRIORITY);
}
Experiment no 9.
Interrupts Routine Service
Introduction
Interrupt handling is important in real-time operating systems. The system becomes aware of
external events via the interrupt mechanism and the response of a real-time systems depends on
the speed of the system's response to interrupts and the speed of processing interrupt handlers. To
achieve the best response possible the application writer must be aware of how to take advantage
of the utilities provided by VxWorks.

Objectives
The following are the primary objectives of this experiment:

 To demonstrate VxWorks' implementation of interrupt service routines.

Description
The user may write an interrupt service routine (ISR) and attach it to a particular interrupt using the
intConnect routine provided by VxWorks. What basically happens when an interrupt to the system
occurs, is at the first non-critical code after the interrupt occured, guaranteed to be within musec by
WRS, the ISR is executed. This time span is generally knonw as interrupt latency. Because many
interrupts may occur within a short time of each other and a higher interrupt will block lower
priority interrupts, it is necessary to keep the ISR processing to a minimum. This is the responsibility
of the application writer.

The header files which relate to VxWorks interrupt management are, intLib.h - this is the interrupt
library header file; and ARCH/mc68k/ivMc68k.h. The ISR code does not run in the normal task
context. It has no task control block and all ISR's share a single stack. Because of these differences
there are restrictions to the type of routines that can be used in the ISR.

ISR's should not invoke functions which may cause ``blocking'' of the caller. For example, semTake.
malloc and free cannot be used because they call functions which may cause blocking and thus all
creation and deletion functions are forbidden since they use malloc and free. An ISR must not
perform I/O through the VxWorks I/O system. A call to a device driver may block the system if the
caller needs to wait for the device. However, the VxWorks pipe driver has been designed to permit
writes by interrupt service code.

The best way to print out messages from an ISR is to use the function logMsg or other functions
provided by the library logLib. ISRs should not use floating point instructions since these registers
are not saved on entry to the ISR. If floating point instructions are to be used the registers must be
saved using the functions in fppALib. However, floating point operations are time intensive and
should be avoided in ISRs.

Ideally, an ISR only contains a semGive system call. That is to say, the function of a ISR is to cause
the execution of a task to perform whatever processing is necessary. To improve cooperation
between VxWorks' ISRs and tasks, the best mechanism is semaphores.

1. Example:

In the example below, the interruptGenerator task generates a hard interrupt,


sysBusIntGen(INTERRUPT_NUM,INTERRUPT_LEVEL), which is caught by interruptCatcher.

The synatx for sysBusIntGen is:

SYNOPSIS
STATUS sysBusIntGen
(
int intLevel, /* bus interrupt level to generate */
int vector /* interrupt vector to generate (0-255) */
)

RETURNS
OK, or ERROR if intLevel is out of range or the board cannot generate
a bus interrupt.
interruptCatcher is able to handle this hardware interrupt by installing an interrupt handler,
interruptHandler.

interruptCatcher is "attaches" to the hardware interrupt using


intConnect(INUM_TO_IVEC(INTERRUPT_LEVEL),(VOIDFUNCPTR)interruptHandler,i). The
INUM_TO_IVEC(INTERRUPT_LEVEL) is a macro that converts a hardware interrupt number to a
vector.

The synatx for sysBusIntGen is:

SYNOPSIS
STATUS intConnect
(
VOIDFUNCPTR * vector, /* interrupt vector to attach to */
VOIDFUNCPTR routine, /* routine to be called */
int parameter /* parameter to be passed to routine */
)

DESCRIPTION
This routine connects a specified C routine to a specified interrupt vector. The
address of routine is stored at vector so that routine is called with parameter when the
interrupt occurs. The routine is invoked in supervisor mode at interrupt level. A proper
C environment is established, the necessary registers saved, and the stack set up.
The routine can be any normal C code, except that it must not invoke certain
operating system functions that may block or perform I/O operations.
This routine simply calls intHandlerCreate( ) and intVecSet( ). The address of the
handler returned by intHandlerCreate( ) is what actually goes in the interrupt vector.
RETURNS OK, or ERROR if the interrupt handler cannot be built.
The run time scenario consists of interruptCatcher running and simulating normal processing until
interruptGenerator generates a hardware interrupt. Upon the generation of the interrupt,
interruptCatcher suspends its normal processing and branches to interruptHandler. Once the
interrupt handling code has been executed, control is passed back to interruptCatcher. This activity
is repeated multiple times.

Example programs codes


/* includes */
#include "vxWorks.h"
#include "intLib.h"
#include "taskLib.h"
#include "arch/mc68k/ivMc68k.h"
#include "logLib.h"

/* function prototypes */
void interruptHandler(int);
void interruptCatcher(void);

/* globals */
#define INTERRUPT_NUM 2
#define INTERRUPT_LEVEL 65
#define ITER1 40
#define LONG_TIME 1000000
#define PRIORITY 100
#define ONE_SECOND 100

void interruptGenerator(void) /* task to generate the SIGINT signal */


{
int i, j, taskId, priority;
STATUS taskAlive;

if((taskId =
taskSpawn("interruptCatcher",PRIORITY,0x100,20000,(FUNCPTR)interruptCatcher,0,0,0
,0,0,0,0,
0,0,0)) == ERROR)
logMsg("taskSpawn interruptCatcher failed\n",0,0,0,0,0,0);

for (i=0; i < ITER1; i++)


{
taskDelay(ONE_SECOND);/* suspend interruptGenerator for one second */
/* check to see if interruptCatcher task is alive! */
if ((taskAlive = taskIdVerify(taskId)) == OK)
{
logMsg("++++++++++++++++++++++++++Interrupt
generated\n",0,0,0,0,0,0);
/* generate hardware interrupt 2 */
if((sysBusIntGen(INTERRUPT_NUM,INTERRUPT_LEVEL)) == ERROR)
logMsg("Interrupt not generated\n",0,0,0,0,0,0);
}
else /* interruptCatcher is dead */
break;
}
logMsg("\n***************interruptGenerator
Exited***************\n\n\n\n",0,0,0,0,0,0);
}
void interruptCatcher(void) /* task to handle the interrupt */
{
int i, j;
STATUS connected;

/* connect the interrupt vector, INTERRUPT_LEVEL, to a specific interrupt


handler routine ,interruptHandler, and pass an argument, i */
if((connected =
intConnect(INUM_TO_IVEC(INTERRUPT_LEVEL),(VOIDFUNCPTR)interruptHandler,i)) ==
ERROR)
logMsg("intConnect failed\n",0,0,0,0,0,0);

for (i=0; i < ITER1; i++)


{
for (j=0; j < LONG_TIME; j++);
logMsg("Normal processing in interruptCatcher\n",0,0,0,0,0,0);
}
logMsg("\n+++++++++++++++interruptCatcher Exited+++++++++++++++\n",0,0,0,0,0,0);
}

void interruptHandler(int arg) /* signal handler code */


{
int i;

logMsg("-------------------------------interrupt caught\n",0,0,0,0,0,0);
for (i=0; i < 5; i++)
logMsg("interrupt processing\n",0,0,0,0,0,0);
}
BIBLIOGRAPHY
 vxworks_programmers_guide5.5_2 -----------------by wind river USA
 vxworks_application_programmers_guide_6.6--- by wind river USA
 Real-Time and Embedded guide --------------------by Herman Bruynincks
 Programming Embedded System ---------------------by Michael J. Pont
 Embedded Systems Architecture --------------------by Tammy Neorgaard
 www.wikipedia.com
 www.google.com
 http://www.xs4all.nl/~borkhuis/vxworks/vxworks.html

Вам также может понравиться