3

POSIX Standard Interfaces



3.1    Introduction

The POSIX standard for real-time extensions (1003.1b) specifies a set of interfaces to kernel facilities. To improve application portability, the VxWorks kernel, wind, includes both POSIX interfaces and interfaces designed specifically for VxWorks.

This chapter uses the qualifier "Wind" to identify facilities designed expressly for use with the VxWorks wind kernel. For example, you can find a discussion of Wind semaphores contrasted to POSIX semaphores in 3.6.1 Comparison of POSIX and Wind Semaphores.

POSIX asynchronous Input/Output (AIO) routines are available in the aioPxLib library. The VxWorks AIO implementation meets the specification in the POSIX 1003.1b standard. For more information, see 4.6 Asynchronous Input/Output.



3.2    POSIX Clocks and Timers

A clock is a software construct (struct timespec, defined in time.h) that keeps time in seconds and nanoseconds. The software clock is updated by system-clock ticks. VxWorks provides a POSIX 1003.1b standard clock and timer interface.

The POSIX standard provides a means of identifying multiple virtual clocks, but only one clock is required--the system-wide real-time clock. No virtual clocks are supported in VxWorks.

The system-wide real-time clock is identified in the clock and timer routines as CLOCK_REALTIME, and is defined in time.h. VxWorks provides routines to access the system-wide real-time clock. For more information, see the reference entry for clockLib.

The POSIX timer facility provides routines for tasks to signal themselves at some time in the future. Routines are provided to create, set, and delete a timer. For more information, see the reference entry for timerLib. When a timer goes off, the default signal, SIGALRM, is sent to the task. To install a signal handler that executes when the timer expires, use the sigaction( ) routine (see 2.3.7 Signals).

Example 3-1:   POSIX Timers

/* This example creates a new timer and stores it in timerid. */ 
 
/* includes */ 
#include "vxWorks.h" 
#include "time.h" 
 
int createTimer (void) 
    { 
    timer_t timerid; 
 
    /* create timer */ 
    if (timer_create (CLOCK_REALTIME, NULL, &timerid) == ERROR) 
        { 
        printf ("create FAILED\n"); 
        return (ERROR); 
        } 
    return (OK); 
    }

An additional POSIX function, nanosleep( ), provides specification of sleep or delay time in units of seconds and nanoseconds, in contrast to the ticks used by the Wind taskDelay( ) function. Nevertheless, the precision of both is the same, and is determined by the system clock rate. Only the units differ.



3.3    POSIX Memory-Locking Interface

Many operating systems perform memory paging and swapping, which copy blocks of memory out to disk and back. These techniques allow you to use more virtual memory than there is physical memory on a system. However, because they impose severe and unpredictable delays in execution time, paging and swapping are undesirable in real-time systems. Consequently, the wind kernel never uses them.

However, the POSIX 1003.1b standard for real-time extensions is also used with operating systems that do perform paging and swapping. On such systems, applications that attempt real-time performance can use the POSIX page-locking facilities to protect certain blocks of memory from paging and swapping.

To facilitate porting programs between other POSIX-conforming systems and VxWorks, VxWorks includes the POSIX page-locking routines. The routines have no adverse affect in VxWorks systems, because all memory is essentially always locked.

The POSIX page-locking routines are part of the memory management library, mmanPxLib, and are listed in Table 3-1. When used in VxWorks, these routines do nothing except return a value of OK (0), since all pages are always kept in memory.

Table 3-1:   POSIX Memory Management Calls


Call
Purpose on Systems with Paging or Swapping

mlockall( )
Locks into memory all pages used by a task.
munlockall( )
Unlocks all pages used by a task.
mlock( )
Locks a specified page.
munlock( )
Unlocks a specified page.

To include the mmanPxLib library, configure VxWorks with the INCLUDE_POSIX_MEM component.



3.4    POSIX Threads

POSIX threads are similar to tasks, but with some additional characteristics, including a thread ID that differs from its task ID.

3.4.1   POSIX Thread Attributes

POSIX characteristics are called attributes. Each attribute contains a set of values, and a set of access functions to retrieve and set those values. You can specify all thread attributes in an attributes object, pthread_attr_t, at thread creation. In a few cases, you can dynamically modify the attribute values in a running thread.

The POSIX attributes and their corresponding access functions are described below.

Stack Size

The stacksize attribute specifies the size of the stack to be used. This value can be rounded up to a page boundary.

  • Attribute Name: stacksize

  • Default Value: Uses the default stack size set for taskLib.

  • Access Functions: pthread_attr_getstacksize( ) and pthread_attr_setstacksize( )

Stack Address

The stackaddr attribute specifies the base of a region of user allocated memory to be used as a stack region for the created thread. Because the default value is NULL, the system should allocate a stack for the thread when it is created.

  • Attribute Name: stackaddr

  • Default Value: NULL

  • Access Functions: pthread_attr_getstackaddr( ) and pthread_attr_setstackaddr( )

Detach State

The detachstate attribute describes the state of a thread. With POSIX threads, the creator of a thread can block until the thread exits (see the entries for pthread_exit( ) and pthread_join( ) in the VxWorks API Reference). In this case, the thread is a joinable thread. Otherwise, it is a detached thread. A thread that was created as a joinable thread can dynamically make itself a detached thread by calling pthread_detach( ).

  • Attribute Name: detachstate

  • Possible Values: PTHREAD_CREATE_DETACHED and PTHREAD_CREATE_JOINABLE

  • Default Value: PTHREAD_CREATE_JOINABLE

  • Access Functions: pthread_attr_getdetachstate( ) and pthread_attr_setdetachstate( )

  • Dynamic Access Function: pthread_detach( )

Contention Scope

The contentionscope attribute describes how threads compete for resources, namely the CPU. Under VxWorks, all tasks compete for the CPU, so the competition is system-wide. Although POSIX allows two values, only PTHREAD_SCOPE_SYSTEM is implemented for VxWorks.

  • Attribute Name: contentionscope

  • Possible Values: PTHREAD_SCOPE_SYSTEM only (PTHREAD_SCOPE_PROCESS not implemented for VxWorks)

  • Default Value: PTHREAD_SCOPE_SYSTEM

  • Access Functions: pthread_attr_getscope( ) and pthread_attr_setscope( )

Inherit Scheduling

The inheritsched attribute determines whether the thread is created with scheduling parameters inherited from its parent thread, or with parameters that are explicitly specified.

  • Attribute Name: inheritsched

  • Possible Values: PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED

  • Default Value: PTHREAD_INHERIT_SCHED

  • Access Functions: pthread_attr_getinheritsched( ) and pthread_attr_setinheritsched( )

Scheduling Policy

The schedpolicy attribute describes the scheduling policy for the thread, and is valid only if the value of the inheritsched attribute is PTHREAD_EXPLICIT_SCHED.

  • Attribute Name: schedpolicy

  • Possible Values: SCHED_FIFO (preemptive priority scheduling) and SCHED_RR (round-robin scheduling by priority)

  • Default Value: SCHED_RR

  • Access Functions:pthread_attr_getschedpolicy( ) and pthread_attr_setschedpolicy( )

Note that because the default value for the inheritsched attribute is PTHREAD_INHERIT_SCHED, the schedpolicy attribute is not used by default. For more information, see 3.5.3 Getting and Displaying the Current Scheduling Policy.

Scheduling Parameters

The schedparam attribute describes the scheduling parameters for the thread, and is valid only if the value of the inheritsched attribute is PTHREAD_EXPLICIT_SCHED.

  • Attribute Name: schedparam

  • Range of Values: 0-255

  • Default Value: Uses default task priority set for taskLib.

  • Access Functions: pthread_attr_getschedparam( ) and pthread_attr_setschedparam( )

  • Dynamic Access Functions: pthread_getschedparam( ) and pthread_setschedparam( ) using thread ID, or sched_getparam( ) and sched_setparam( ) using task ID

Note that because the default value the inheritsched attribute is PTHREAD_INHERIT_SCHED, the schedparam attribute is not used by default. For more information, see 3.5.2 Getting and Setting POSIX Task Priorities.

Specifying Attributes when Creating pThreads

Following are examples of creating a thread using the default attributes and using explicit attributes.

Example 3-2:   Creating a pThread Using Explicit Scheduing Attributes

pthread_t tid; 
pthread_attr_t attr; 
int ret; 
pthread_attr_init(&attr); 
 
/* set the inheritsched attribute to explicit */ 
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); 
 
/* set the schedpolicy attribute to SCHED_FIFO */ 
pthread_attr_setschedpolicy(&attr, SCHED_FIFO); 
 
/* create the pthread */ 
ret = pthread_create(&tid, &attr, entryFunction, entryArg);

Example 3-3:   Creating a pThread Using Default Attributes

pthread_t tid; 
int ret; 
 
/* create the pthread with NULL attributes to designate default values */ 
ret = pthread_create(&tid, NULL, entryFunction, entryArg);

Example 3-4:   Designating Your Own Stack for a pThread

pthread_attr_init(&attr); 
 
/* allocate memory for a stack region for the thread */ 
stackbase = malloc(2 * 4096); 
 
if (stackbase == NULL) 
    { 
    printf("FAILED: mystack: malloc failed\n"); 
    exit(-1); 
    } 
 
/* set the stack pointer to the base address */ 
stackptr = (void *)((int)stackbase); 
 
/* explicitly set the stackaddr attribute */ 
pthread_attr_setstackaddr(&attr, stackptr); 
 
/* set the stacksize attribute to 4096 */ 
pthread_attr_setstacksize(&attr, (4096));
/* set the schedpolicy attribute to SCHED_FIFO */ 
pthread_attr_setschedpolicy(&attr, SCHED_FIFO); 
 
/* create the pthread */ 
ret = pthread_create(&tid, &attr, mystack_thread, 0);  

3.4.2   Thread Private Data

When a thread needs access to private data, POSIX uses a key to access that data. A location is created by calling to pthread_key_create( ) and released by calling pthread_key_delete( ). The location is then accessed by calling pthread_getspecific( ) and pthread_setspecific( ). The pthread_key_create( ) routine has an option for a destructor function, which is called when the creating thread exits, if the value associated with the key is non-NULL.

3.4.3   Thread Cancellation

POSIX provides a mechanism, called cancellation, to terminate a thread gracefully. There are two types of cancellation, synchronous and asynchronous. Synchronous cancellation causes the thread to explicitly check to see if it was cancelled or to call a function that contains a cancellation point. Asynchronous cancellation causes the execution of the thread to be interrupted and a handler to be called, much like a signal.1

Routines that can be used with cancellation are listed in Table 3-2.

Table 3-2:   Thread Cancellation Routines


Routine
Meaning

pthread_setcancelstate( )
Enables or disables cancellation.
pthread_setcanceltype( )
Selects synchronous or asynchronous cancellation.
pthread_cleanup_push( )
Registers a function to be called when the thread is cancelled.
pthread_cleanup_pop( )
Unregisters a function to be called when a thread is cancelled, and then calls the function.

A thread can register and unregister functions to be called when it is cancelled by pthread_cleanup_push( ) and pthread_cleanup_pop( ). The pthread_cleanup_pop( ) routine can optionally call the function when unregistering it.



3.5    POSIX Scheduling Interface

The POSIX 1003.1b scheduling routines, provided by schedPxLib, are shown in Table 3-3. These routines let you use a portable interface to get and set task priority, get the scheduling policy, get the maximum and minimum priority for tasks, and if round-robin scheduling is in effect, get the length of a time slice. This section describes how to use these routines, beginning with a list of the minor differences between the POSIX and Wind methods of scheduling.

Table 3-3:   POSIX Scheduling Calls


Call
Description

sched_setparam( )
Sets a task's priority.
sched_getparam( )
Gets the scheduling parameters for a specified task.
sched_setscheduler( )
Sets the scheduling policy and parameters for a task.
sched_yield( )
Relinquishes the CPU.
sched_getscheduler( )
Gets the current scheduling policy.
sched_get_priority_max( )
Gets the maximum priority.
sched_get_priority_min( )
Gets the minimum priority.
sched_rr_get_interval( )
If round-robin scheduling, gets the time slice length.

To include the schedPxLib library of POSIX scheduling routines, configure VxWorks with the INCLUDE_POSIX_SCHED component.

3.5.1   Comparison of POSIX and Wind Scheduling

POSIX and Wind scheduling routines differ in the following ways:

  • POSIX scheduling is based on processes. Wind scheduling is based on tasks.

  • The POSIX standard uses the term FIFO scheduling. VxWorks documentation uses the term preemptive priority scheduling. Only the terms differ; both describe the same priority-based policy.

  • POSIX applies scheduling algorithms on a process-by-process basis. Wind applies scheduling algorithms on a system-wide basis--meaning that all tasks use either a round-robin scheme or a preemptive priority scheme.

  • The POSIX priority numbering scheme is the inverse of the Wind scheme. In POSIX, the higher the number, the higher the priority; in the Wind scheme, the lower the number, the higher the priority, where 0 is the highest priority. Accordingly, the priority numbers used with the POSIX scheduling library, schedPxLib, do not match those used and reported by all other components of VxWorks. You can override this default by setting the global variable posixPriorityNumbering to FALSE. If you do this, schedPxLib uses the Wind numbering scheme (smaller number = higher priority) and its priority numbers match those used by the other components of VxWorks.

3.5.2   Getting and Setting POSIX Task Priorities

The routines sched_setparam( ) and sched_getparam( ) set and get a task's priority, respectively. Both routines take a task ID and a sched_param structure (defined in installDir/target/h/sched.h). A task ID of 0 sets or gets the priority for the calling task.

When sched_setparam( ) is called, the sched_priority member of the sched_param structure specifies the new task priority. The sched_getparam( ) routine fills in the sched_priority with the specified task's current priority.

Example 3-5:   Getting and Setting POSIX Task Priorities

/* This example sets the calling task's priority to 150, then verifies 
 * that priority. To run from the shell, spawn as a task: -> sp priorityTest 
 */ 
 
/* includes */ 
#include "vxWorks.h" 
#include "sched.h" 
 
/* defines */ 
#define PX_NEW_PRIORITY 150 
 
STATUS priorityTest (void) 
    { 
    struct sched_param myParam; 
 
    /* initialize param structure to desired priority */ 
 
    myParam.sched_priority = PX_NEW_PRIORITY; 
    if (sched_setparam (0, &myParam) == ERROR) 
        { 
        printf ("error setting priority\n"); 
        return (ERROR); 
        } 
 
    /* demonstrate getting a task priority as a sanity check; ensure it 
     * is the same value that we just set. 
     */ 
 
    if (sched_getparam (0, &myParam) == ERROR) 
        { 
        printf ("error getting priority\n"); 
        return (ERROR); 
        } 
 
    if (myParam.sched_priority != PX_NEW_PRIORITY) 
        { 
        printf ("error - priorities do not match\n"); 
        return (ERROR); 
        } 
    else 
        printf ("task priority = %d\n", myParam.sched_priority); 
 
    return (OK); 
    }

The routine sched_setscheduler( ) is designed to set both scheduling policy and priority for a single POSIX process, which corresponds in most other cases to a single Wind task. In the VxWorks kernel, sched_setscheduler( ) controls only task priority, because the kernel does not allow tasks to have scheduling policies that differ from one another. If its policy specification matches the current system-wide scheduling policy, sched_setscheduler( ) sets only the priority, thus acting like sched_setparam( ). If its policy specification does not match the current one, sched_setscheduler( ) returns an error.

The only way to change the scheduling policy is to change it for all tasks; there is no POSIX routine for this purpose. To set a system-wide scheduling policy, use the Wind function kernelTimeSlice( ) described in Round-Robin Scheduling.   

3.5.3   Getting and Displaying the Current Scheduling Policy

The POSIX routine sched_getscheduler( ) returns the current scheduling policy. There are two valid scheduling policies in VxWorks: preemptive priority scheduling (in POSIX terms, SCHED_FIFO) and round-robin scheduling by priority (SCHED_RR). For more information, see Scheduling Policy.

Example 3-6:   Getting POSIX Scheduling Policy

/* This example gets the scheduling policy and displays it. */ 
 
/* includes */ 
 
#include "vxWorks.h" 
#include "sched.h" 
 
STATUS schedulerTest (void) 
    { 
    int policy; 
 
    if ((policy = sched_getscheduler (0)) == ERROR) 
        { 
        printf ("getting scheduler failed\n"); 
        return (ERROR); 
        } 
 
    /* sched_getscheduler returns either SCHED_FIFO or SCHED_RR */ 
 
    if (policy == SCHED_FIFO) 
        printf ("current scheduling policy is FIFO\n"); 
    else 
        printf ("current scheduling policy is round robin\n"); 
 
    return (OK); 
    }

3.5.4   Getting Scheduling Parameters: Priority Limits and Time Slice

The routines sched_get_priority_max( ) and sched_get_priority_min( ) return the maximum and minimum possible POSIX priority, respectively.

If round-robin scheduling is enabled, you can use sched_rr_get_interval( ) to determine the length of the current time-slice interval. This routine takes as an argument a pointer to a timespec structure (defined in time.h), and writes the number of seconds and nanoseconds per time slice to the appropriate elements of that structure.

Example 3-7:   Getting the POSIX Round-Robin Time Slice

/* The following example checks that round-robin scheduling is enabled, 
 * gets the length of the time slice, and then displays the time slice. 
 */ 
 
/* includes */ 
 
#include "vxWorks.h" 
#include "sched.h" 
 
STATUS rrgetintervalTest (void) 
    { 
    struct timespec slice; 
 
    /* turn on round robin */ 
 
    kernelTimeSlice (30); 
 
    if (sched_rr_get_interval (0, &slice) == ERROR) 
        { 
        printf ("get-interval test failed\n"); 
        return (ERROR); 
        } 
 
    printf ("time slice is %l seconds and %l nanoseconds\n",  
            slice.tv_sec, slice.tv_nsec); 
    return (OK); 
    } 


3.6    POSIX Semaphores

POSIX defines both named and unnamed semaphores, which have the same properties, but use slightly different interfaces. The POSIX semaphore library provides routines for creating, opening, and destroying both named and unnamed semaphores. When opening a named semaphore, you assign a symbolic name,2 which the other named-semaphore routines accept as an argument. The POSIX semaphore routines provided by semPxLib are shown in Table 3-4.

Table 3-4:   POSIX Semaphore Routines 


Call
Description

semPxLibInit( )
Initializes the POSIX semaphore library (non-POSIX).
sem_init( )
Initializes an unnamed semaphore.
sem_destroy( )
Destroys an unnamed semaphore.
sem_open( )
Initializes/opens a named semaphore.
sem_close( )
Closes a named semaphore.
sem_unlink( )
Removes a named semaphore.
sem_wait( )
Lock a semaphore.
sem_trywait( )
Lock a semaphore only if it is not already locked.
sem_post( )
Unlock a semaphore.
sem_getvalue( )
Get the value of a semaphore.

To include the POSIX semPxLib library semaphore routines, configure VxWorks with the INCLUDE_POSIX_SEM component. The initialization routine semPxLibInit( ) is called by default when POSIX semaphores have been included in VxWorks.

3.6.1   Comparison of POSIX and Wind Semaphores

POSIX semaphores are counting semaphores; that is, they keep track of the number of times they are given. The Wind semaphore mechanism is similar to that specified by POSIX, except that Wind semaphores offer additional features listed below. When these features are important, Wind semaphores are preferable.

  • priority inheritance

  • task-deletion safety

  • the ability for a single task to take a semaphore multiple times

  • ownership of mutual-exclusion semaphores

  • semaphore timeouts

  • the choice of queuing mechanism

The POSIX terms wait (or lock) and post (or unlock) correspond to the VxWorks terms take and give, respectively. The POSIX routines for locking, unlocking, and getting the value of semaphores are used for both named and unnamed semaphores.

The routines sem_init( ) and sem_destroy( ) are used for initializing and destroying unnamed semaphores only. The sem_destroy( ) call terminates an unnamed semaphore and deallocates all associated memory.

The routines sem_open( ), sem_unlink( ), and sem_close( ) are for opening and closing (destroying) named semaphores only. The combination of sem_close( ) and sem_unlink( ) has the same effect for named semaphores as sem_destroy( ) does for unnamed semaphores. That is, it terminates the semaphore and deallocates the associated memory.


*      
WARNING: When deleting semaphores, particularly mutual-exclusion semaphores, avoid deleting a semaphore still required by another task. Do not delete a semaphore unless the deleting task first succeeds in locking that semaphore. Similarly for named semaphores, close semaphores only from the same task that opens them.

3.6.2   Using Unnamed Semaphores

When using unnamed semaphores, typically one task allocates memory for the semaphore and initializes it. A semaphore is represented with the data structure sem_t, defined in semaphore.h. The semaphore initialization routine, sem_init( ), lets you specify the initial value.

Once the semaphore is initialized, any task can use the semaphore by locking it with sem_wait( ) (blocking) or sem_trywait( ) (non-blocking), and unlocking it with sem_post( ).

Semaphores can be used for both synchronization and exclusion. Thus, when a semaphore is used for synchronization, it is typically initialized to zero (locked). The task waiting to be synchronized blocks on a sem_wait( ). The task doing the synchronizing unlocks the semaphore using sem_post( ). If the task blocked on the semaphore is the only one waiting for that semaphore, the task unblocks and becomes ready to run. If other tasks are blocked on the semaphore, the task with the highest priority is unblocked.

When a semaphore is used for mutual exclusion, it is typically initialized to a value greater than zero, meaning that the resource is available. Therefore, the first task to lock the semaphore does so without blocking; subsequent tasks block if the semaphore value was initialized to 1.

Example 3-8:   POSIX Unnamed Semaphores

/* This example uses unnamed semaphores to synchronize an action between the 
 * calling task and a task that it spawns (tSyncTask). To run from the shell, 
 * spawn as a task: 
 *   -> sp unnameSem  
 */ 
 
/* includes */ 
 
#include "vxWorks.h" 
#include "semaphore.h" 
 
/* forward declarations */ 
void syncTask (sem_t * pSem); 
 
void unnameSem (void) 
    { 
    sem_t * pSem; 
 
    /* reserve memory for semaphore */ 
    pSem = (sem_t *) malloc (sizeof (sem_t)); 
 
    /* initialize semaphore to unavailable */ 
    if (sem_init (pSem, 0, 0) == -1) 
        { 
        printf ("unnameSem: sem_init failed\n");   
        free ((char *) pSem); 
        return; 
        } 
 
    /* create sync task */ 
    printf ("unnameSem: spawning task...\n"); 
    taskSpawn ("tSyncTask", 90, 0, 2000, syncTask, pSem); 
 
    /* do something useful to synchronize with syncTask */
    /* unlock sem */ 
    printf ("unnameSem: posting semaphore - synchronizing action\n"); 
    if (sem_post (pSem) == -1) 
        { 
        printf ("unnameSem: posting semaphore failed\n"); 
        sem_destroy (pSem); 
        free ((char *) pSem); 
        return; 
        }
    /* all done - destroy semaphore */ 
    if (sem_destroy (pSem) == -1) 
      { 
      printf ("unnameSem: sem_destroy failed\n"); 
      return; 
      } 
    free ((char *) pSem); 
    } 
 
void syncTask 
    ( 
    sem_t * pSem 
    ) 
    { 
    /* wait for synchronization from unnameSem */ 
    if (sem_wait (pSem) == -1) 
        { 
        printf ("syncTask: sem_wait failed \n"); 
        return; 
        } 
    else 
        printf ("syncTask:sem locked; doing sync'ed action...\n"); 
 
    /* do something useful here */ 
    }

3.6.3   Using Named Semaphores

The sem_open( ) routine either opens a named semaphore that already exists or, as an option, creates a new semaphore. You can specify which of these possibilities you want by combining the following flag values:

O_CREAT
Create the semaphore if it does not already exist (if it exists, either fail or open the semaphore, depending on whether O_EXCL is specified).

O_EXCL
Open the semaphore only if newly created; fail if the semaphore exists.

The results, based on the flags and whether the semaphore accessed already exists, are shown in Table 3-5. There is no entry for O_EXCL alone, because using that flag alone is not meaningful.

Table 3-5:   Possible Outcomes of Calling sem_open( )


Flag Settings
If Semaphore Exists
If Semaphore Does Not Exist

None
Semaphore is opened.
Routine fails.
O_CREAT
Semaphore is opened.
Semaphore is created.
O_CREAT and O_EXCL
Routine fails.
Semaphore is created.

A POSIX named semaphore, once initialized, remains usable until explicitly destroyed. Tasks can explicitly mark a semaphore for destruction at any time, but the semaphore remains in the system until no task has the semaphore open.

If VxWorks is configured with INCLUDE_POSIX_SEM_SHOW, you can use show( ) from the shell to display information about a POSIX semaphore:3

-> show semId 
value = 0 = 0x0

The output is sent to the standard output device, and provides information about the POSIX semaphore mySem with two tasks blocked waiting for it:

Semaphore name         :mySem 
sem_open() count       :3 
Semaphore value        :0 
No. of blocked tasks     :2

For a group of collaborating tasks to use a named semaphore, one of the tasks first creates and initializes the semaphore, by calling sem_open( ) with the O_CREAT flag. Any task that needs to use the semaphore thereafter, opens it by calling sem_open( ) with the same name (but without setting O_CREAT). Any task that has opened the semaphore can use it by locking it with sem_wait( ) (blocking) or sem_trywait( ) (non-blocking) and unlocking it with sem_post( ).

To remove a semaphore, all tasks using it must first close it with sem_close( ), and one of the tasks must also unlink it. Unlinking a semaphore with sem_unlink( ) removes the semaphore name from the name table. After the name is removed from the name table, tasks that currently have the semaphore open can still use it, but no new tasks can open this semaphore. The next time a task tries to open the semaphore without the O_CREAT flag, the operation fails. The semaphore vanishes when the last task closes it.

Example 3-9:   POSIX Named Semaphores

/*  
 * In this example, nameSem() creates a task for synchronization. The 
 * new task, tSyncSemTask, blocks on the semaphore created in nameSem().  
 * Once the synchronization takes place, both tasks close the semaphore, 
 * and nameSem() unlinks it. To run this task from the shell, spawn 
 * nameSem as a task: 
 *   -> sp nameSem, "myTest" 
 */
/* includes */ 
#include "vxWorks.h" 
#include "semaphore.h" 
#include "fcntl.h" 
 
/* forward declaration */ 
int syncSemTask (char * name); 
 
int nameSem  
    ( 
    char * name 
    ) 
    { 
    sem_t * semId; 
 
    /* create a named semaphore, initialize to 0*/ 
    printf ("nameSem: creating semaphore\n"); 
    if ((semId = sem_open (name, O_CREAT, 0, 0)) == (sem_t *) -1) 
        { 
        printf ("nameSem: sem_open failed\n");  
        return; 
        } 
 
    printf ("nameSem: spawning sync task\n"); 
    taskSpawn ("tSyncSemTask", 90, 0, 2000, syncSemTask, name); 
 
    /* do something useful to synchronize with syncSemTask */ 
 
    /* give semaphore */ 
    printf ("nameSem: posting semaphore - synchronizing action\n"); 
    if (sem_post (semId) == -1) 
        { 
        printf ("nameSem: sem_post failed\n"); 
        return; 
        } 
 
    /* all done */ 
    if (sem_close (semId) == -1) 
        { 
        printf ("nameSem: sem_close failed\n"); 
        return; 
        } 
 
    if (sem_unlink (name) == -1) 
        { 
        printf ("nameSem: sem_unlink failed\n"); 
        return; 
        } 
 
    printf ("nameSem: closed and unlinked semaphore\n"); 
    } 
 
int syncSemTask 
    ( 
    char * name 
    ) 
    { 
    sem_t * semId; 
 
    /* open semaphore */ 
    printf ("syncSemTask: opening semaphore\n"); 
    if ((semId = sem_open (name, 0)) == (sem_t *) -1) 
        { 
        printf ("syncSemTask: sem_open failed\n"); 
        return; 
        } 
 
    /* block waiting for synchronization from nameSem */ 
    printf ("syncSemTask: attempting to take semaphore...\n"); 
    if (sem_wait (semId) == -1) 
        { 
        printf ("syncSemTask: taking sem failed\n"); 
        return; 
        } 
 
    printf ("syncSemTask: has semaphore, doing sync'ed action ...\n"); 
 
    /* do something useful here */ 
 
    if (sem_close (semId) == -1) 
        { 
        printf ("syncSemTask: sem_close failed\n"); 
        return; 
        } 
    }


3.7    POSIX Mutexes and Condition Variables

Mutexes and condition variables provide compatibility with the POSIX standard (1003.1c). They perform essentially the same role as mutual exclusion and binary semaphores (and are in fact implemented using them). They are available with pthreadLib. Like POSIX threads, mutexes and condition variables have attributes associated with them.

Mutex attributes are held in a data type called pthread_mutexattr_t, which contains two attributes, protocol and prioceiling.

Protocol

The protocol mutex attribute describes how the mutex deals with the priority inversion problem described in the section for mutual-exclusion semaphores (Mutual-Exclusion Semaphores).

  • Attribute Name: protocol

  • Possible Values: PTHREAD_PRIO_INHERIT and PTHREAD_PRIO_PROTECT

  • Access Routines: pthread_mutexattr_getprotocol( ) and pthread_mutexattr_setprotocol( )

To create a mutual-exclusion semaphore with priority inheritance, use the SEM_Q_PRIORITY and SEM_PRIO_INHERIT options to semMCreate( ). Mutual-exclusion semaphores created with the priority protection value use the notion of a priority ceiling, which is the other mutex attribute.

Priority Ceiling

The prioceiling attribute is the POSIX priority ceiling for a mutex created with the protocol attribute set to PTHREAD_PRIO_PROTECT.

  • Attribute Name: prioceiling

  • Possible Values: any valid (POSIX) priority value

  • Access Routines: pthread_mutexattr_getprioceiling( ) and pthread_mutexattr_setprioceiling( )

  • Dynamic Access Routines: pthread_mutex_getprioceiling( ) and pthread_mutex_setprioceiling( )

Note that the POSIX priority numbering scheme is the inverse of the Wind scheme. See 3.5.1 Comparison of POSIX and Wind Scheduling.

A priority ceiling is defined by the following conditions:

  • Any thread attempting to acquire a mutex, whose priority is higher than the ceiling, cannot acquire the mutex.

  • Any thread whose priority is lower than the ceiling value has its priority elevated to the ceiling value for the duration that the mutex is held.

  • The thread's priority is restored to its previous value when the mutex is released.



3.8    POSIX Message Queues

The POSIX message queue routines, provided by mqPxLib, are shown in Table 3-6.

Table 3-6:   POSIX Message Queue Routines


Call
Description

mqPxLibInit( )
Initializes the POSIX message queue library (non-POSIX).
mq_open( )
Opens a message queue.
mq_close( )
Closes a message queue.
mq_unlink( )
Removes a message queue.
mq_send( )
Sends a message to a queue.
mq_receive( )
Gets a message from a queue.
mq_notify( )
Signals a task that a message is waiting on a queue.
mq_setattr( )
Sets a queue attribute.
mq_getattr( )
Gets a queue attribute.

To configure VxWorks to include the POSIX message queue routine, include the INCLUDE_POSIX_MQ component. The initialization routine mqPxLibInit( ) makes the POSIX message queue routines available, and is called automatically when the INCLUDE_POSIX_MQ component is included in the system.

3.8.1   Comparison of POSIX and Wind Message Queues

The POSIX message queues are similar to Wind message queues, except that POSIX message queues provide messages with a range of priorities. The differences between the POSIX and Wind message queues are summarized in Table 3-7.

Table 3-7:   Message Queue Feature Comparison


Feature
Wind Message Queues
POSIX Message Queues

Message Priority Levels
1
32
Blocked Task Queues
FIFO or priority-based
Priority-based
Receive with Timeout
Optional
Not available
Task Notification
Not available
Optional (one task)
Close/Unlink Semantics
No
Yes

POSIX message queues are also portable, if you are migrating to VxWorks from another 1003.1b-compliant system. This means that you can use POSIX message queues without having to change the code, thereby reducing the porting effort.

3.8.2   POSIX Message Queue Attributes

A POSIX message queue has the following attributes:

  • an optional O_NONBLOCK flag
  • the maximum number of messages in the message queue
  • the maximum message size
  • the number of messages currently on the queue

Tasks can set or clear the O_NONBLOCK flag (but not the other attributes) using mq_setattr( ), and get the values of all the attributes using mq_getattr( ).

Example 3-10:   Setting and Getting Message Queue Attributes

/*  
 * This example sets the O_NONBLOCK flag and examines message queue 
 * attributes. 
 */ 
 
/* includes */ 
#include "vxWorks.h" 
#include "mqueue.h" 
#include "fcntl.h" 
#include "errno.h" 
 
/* defines */ 
#define MSG_SIZE    16 
 
int attrEx 
    ( 
    char * name 
    ) 
    { 
    mqd_t           mqPXId;            /* mq descriptor */ 
    struct mq_attr  attr;              /* queue attribute structure */ 
    struct mq_attr  oldAttr;           /* old queue attributes */ 
    char            buffer[MSG_SIZE]; 
    int             prio; 
 
    /* create read write queue that is blocking */ 
    attr.mq_flags = 0; 
    attr.mq_maxmsg = 1; 
    attr.mq_msgsize = 16; 
    if ((mqPXId = mq_open (name, O_CREAT | O_RDWR , 0, &attr))  
        == (mqd_t) -1) 
        return (ERROR); 
    else 
        printf ("mq_open with non-block succeeded\n"); 
 
    /* change attributes on queue - turn on non-blocking */ 
    attr.mq_flags = O_NONBLOCK;  
    if (mq_setattr (mqPXId, &attr, &oldAttr) == -1) 
        return (ERROR); 
    else 
        { 
        /* paranoia check - oldAttr should not include non-blocking. */ 
        if (oldAttr.mq_flags & O_NONBLOCK)  
            return (ERROR); 
        else 
            printf ("mq_setattr turning on non-blocking succeeded\n"); 
        } 
 
    /* try receiving - there are no messages but this shouldn't block */ 
    if (mq_receive (mqPXId, buffer, MSG_SIZE, &prio) == -1) 
        { 
        if (errno != EAGAIN) 
            return (ERROR); 
        else 
            printf ("mq_receive with non-blocking didn't block on empty queue\n"); 
        } 
    else 
        return (ERROR); 
 
    /* use mq_getattr to verify success */ 
    if (mq_getattr (mqPXId, &oldAttr) == -1) 
        return (ERROR); 
    else 
        { 
        /* test that we got the values we think we should */ 
        if (!(oldAttr.mq_flags & O_NONBLOCK) || (oldAttr.mq_curmsgs != 0)) 
            return (ERROR); 
        else 
            printf ("queue attributes are:\n\tblocking is %s\n\t 
                    message size is: %d\n\t 
                    max messages in queue: %d\n\t 
                    no. of current msgs in queue: %d\n", 
                    oldAttr.mq_flags & O_NONBLOCK ? "on" : "off", 
                    oldAttr.mq_msgsize, oldAttr.mq_maxmsg,  
                    oldAttr.mq_curmsgs); 
        } 
 
    /* clean up - close and unlink mq */ 
    if (mq_unlink (name) == -1) 
        return (ERROR); 
    if (mq_close (mqPXId) == -1) 
        return (ERROR); 
    return (OK); 
    }

3.8.3   Displaying Message Queue Attributes

The VxWorks show( ) command produces a display of the key message queue attributes, for either POSIX or Wind message queues. To get information on POSIX message queues, configure VxWorks to include the INCLUDE_POSIX_MQ_SHOW component.

For example, if mqPXId is a POSIX message queue:

-> show mqPXId 
value = 0 = 0x0

The output is sent to the standard output device, and looks like the following:

Message queue name          : MyQueue 
No. of messages in queue    : 1  
Maximum no. of messages     : 16 
Maximum message size           : 16

Compare this to the output when myMsgQId is a Wind message queue:

-> show myMsgQId 
Message Queue Id  : 0x3adaf0  
Task Queuing      : FIFO  
Message Byte Len  : 4  
Messages Max      : 30  
Messages Queued   : 14 
Receivers Blocked : 0  
Send timeouts     : 0  
Receive timeouts    : 0 


*      
NOTE: The built-in show( ) routine handles Wind message queues; see the Tornado User's Guide: Shell for information on built-in routines. You can also use the Tornado browser to get information on Wind message queues; see the Tornado User's Guide: Browser for details.

3.8.4   Communicating Through a Message Queue

Before a set of tasks can communicate through a POSIX message queue, one of the tasks must create the message queue by calling mq_open( ) with the O_CREAT flag set. Once a message queue is created, other tasks can open that queue by name to send and receive messages on it. Only the first task opens the queue with the O_CREAT flag; subsequent tasks can open the queue for receiving only (O_RDONLY), sending only (O_WRONLY), or both sending and receiving (O_RDWR).

To put messages on a queue, use mq_send( ). If a task attempts to put a message on the queue when the queue is full, the task blocks until some other task reads a message from the queue, making space available. To avoid blocking on mq_send( ), set O_NONBLOCK when you open the message queue. In that case, when the queue is full, mq_send( ) returns -1 and sets errno to EAGAIN instead of pending, allowing you to try again or take other action as appropriate.

One of the arguments to mq_send( ) specifies a message priority. Priorities range from 0 (lowest priority) to 31 (highest priority); see 3.5.1 Comparison of POSIX and Wind Scheduling.

When a task receives a message using mq_receive( ), the task receives the highest-priority message currently on the queue. Among multiple messages with the same priority, the first message placed on the queue is the first received (FIFO order). If the queue is empty, the task blocks until a message is placed on the queue.

To avoid pending on mq_receive( ), open the message queue with O_NONBLOCK; in that case, when a task attempts to read from an empty queue, mq_receive( ) returns -1 and sets errno to EAGAIN.

To close a message queue, call mq_close( ). Closing the queue does not destroy it, but only asserts that your task is no longer using the queue. To request that the queue be destroyed, call mq_unlink( ). Unlinking a message queue does not destroy the queue immediately, but it does prevent any further tasks from opening that queue, by removing the queue name from the name table. Tasks that currently have the queue open can continue to use it. When the last task closes an unlinked queue, the queue is destroyed.

Example 3-11:   POSIX Message Queues

/* In this example, the mqExInit() routine spawns two tasks that  
 * communicate using the message queue. 
 */ 
 
/* mqEx.h - message example header */ 
 
/* defines */ 
#define MQ_NAME "exampleMessageQueue" 
 
/* forward declarations */ 
void receiveTask (void); 
void sendTask (void); 
 
/* testMQ.c - example using POSIX message queues */ 
 
/* includes */ 
#include "vxWorks.h" 
#include "mqueue.h" 
#include "fcntl.h" 
#include "errno.h" 
#include "mqEx.h" 
 
/* defines */ 
#define HI_PRIO     31 
#define MSG_SIZE      16 
 
int mqExInit (void) 
    { 
    /* create two tasks */ 
    if (taskSpawn ("tRcvTask", 95, 0, 4000, receiveTask, 0, 0, 0, 0,  
                 0, 0, 0, 0, 0, 0) == ERROR) 
        { 
        printf ("taskSpawn of tRcvTask failed\n"); 
        return (ERROR); 
        } 
 
    if (taskSpawn ("tSndTask", 100, 0, 4000, sendTask, 0, 0, 0, 0,  
                 0, 0, 0, 0, 0, 0) == ERROR) 
        { 
        printf ("taskSpawn of tSendTask failed\n"); 
        return (ERROR); 
        } 
    } 
 
void receiveTask (void) 
    { 
    mqd_t    mqPXId;         /* msg queue descriptor */ 
    char     msg[MSG_SIZE];  /* msg buffer */ 
    int      prio;           /* priority of message */ 
 
    /* open message queue using default attributes */ 
    if ((mqPXId = mq_open (MQ_NAME, O_RDWR | O_CREAT, 0, NULL))  
        == (mqd_t) -1) 
        { 
        printf ("receiveTask: mq_open failed\n"); 
        return; 
        } 
 
    /* try reading from queue */ 
    if (mq_receive (mqPXId, msg, MSG_SIZE, &prio) == -1) 
        { 
        printf ("receiveTask: mq_receive failed\n"); 
        return; 
        } 
    else 
        { 
        printf ("receiveTask: Msg of priority %d received:\n\t\t%s\n", 
                prio, msg); 
        } 
    }
/* sendTask.c - mq sending example */
/* includes */ 
#include "vxWorks.h" 
#include "mqueue.h" 
#include "fcntl.h" 
#include "mqEx.h" 
 
/* defines */ 
#define MSG   "greetings" 
#define HI_PRIO 30 
 
void sendTask (void) 
    { 
    mqd_t    mqPXId;          /* msg queue descriptor */\ 
 
    /* open msg queue; should already exist with default attributes */
 
    if ((mqPXId = mq_open (MQ_NAME, O_RDWR, 0, NULL)) == (mqd_t) -1) 
        { 
        printf ("sendTask: mq_open failed\n"); 
        return; 
        } 
 
    /* try writing to queue */ 
    if (mq_send (mqPXId, MSG, sizeof (MSG), HI_PRIO) == -1) 
        { 
        printf ("sendTask: mq_send failed\n"); 
        return; 
        } 
    else 
        printf ("sendTask: mq_send succeeded\n"); 
    }

3.8.5   Notifying a Task that a Message is Waiting

A task can use the mq_notify( ) routine to request notification when a message for it arrives at an empty queue. The advantage of this is that a task can avoid blocking or polling to wait for a message.

The mq_notify( ) call specifies a signal to be sent to the task when a message is placed on an empty queue. This mechanism uses the POSIX data-carrying extension to signaling, which allows you, for example, to carry a queue identifier with the signal (see 3.9 POSIX Queued Signals).

The mq_notify( ) mechanism is designed to alert the task only for new messages that are actually available. If the message queue already contains messages, no notification is sent when more messages arrive. If there is another task that is blocked on the queue with mq_receive( ), that other task unblocks, and no notification is sent to the task registered with mq_notify( ).

Notification is exclusive to a single task: each queue can register only one task for notification at a time. Once a queue has a task to notify, no attempts to register with mq_notify( ) can succeed until the notification request is satisfied or cancelled.

Once a queue sends notification to a task, the notification request is satisfied, and the queue has no further special relationship with that particular task; that is, the queue sends a notification signal only once per mq_notify( ) request. To arrange for one particular task to continue receiving notification signals, the best approach is to call mq_notify( ) from the same signal handler that receives the notification signals. This reinstalls the notification request as soon as possible.

To cancel a notification request, specify NULL instead of a notification signal. Only the currently registered task can cancel its notification request.

Example 3-12:   Notifying a Task that a Message Queue is Waiting

/*  
 *In this example, a task uses mq_notify() to discover when a message 
 * is waiting for it on a previously empty queue. 
 */
/* includes */ 
#include "vxWorks.h" 
#include "signal.h" 
#include "mqueue.h" 
#include "fcntl.h" 
#include "errno.h"
/* defines */ 
#define QNAM       "PxQ1" 
#define MSG_SIZE    64        /* limit on message sizes */
/* forward declarations */ 
static void exNotificationHandle (int, siginfo_t *, void *); 
static void exMqRead (mqd_t);
/* 
 * exMqNotify - example of how to use mq_notify() 
 * 
 * This routine illustrates the use of mq_notify() to request notification 
 * via signal of new messages in a queue. To simplify the example, a 
 * single task both sends and receives a message. 
*/
int exMqNotify 
    ( 
    char * pMess              /* text for message to self */ 
    )
    { 
    struct mq_attr    attr;                 /* queue attribute structure */ 
    struct sigevent   sigNotify;            /* to attach notification */ 
    struct sigaction  mySigAction;          /* to attach signal handler */ 
    mqd_t             exMqId                /* id of message queue */
    /* Minor sanity check; avoid exceeding msg buffer */ 
    if (MSG_SIZE <= strlen (pMess)) 
        { 
        printf ("exMqNotify: message too long\n"); 
        return (-1); 
        }
    /* 
     * Install signal handler for the notify signal and fill in  
     * a sigaction structure and pass it to sigaction(). Because the handler 
     * needs the siginfo structure as an argument, the SA_SIGINFO flag is 
     * set in sa_flags. 
     */
    mySigAction.sa_sigaction = exNotificationHandle; 
    mySigAction.sa_flags     = SA_SIGINFO; 
    sigemptyset (&mySigAction.sa_mask);
    if (sigaction (SIGUSR1, &mySigAction, NULL) == -1) 
        { 
        printf ("sigaction failed\n"); 
        return (-1); 
        }
    /*  
     * Create a message queue - fill in a mq_attr structure with the 
     * size and no. of messages required, and pass it to mq_open(). 
     */
    attr.mq_flags  = O_NONBLOCK;             /* make nonblocking */ 
    attr.mq_maxmsg = 2; 
    attr.mq_msgsize = MSG_SIZE; 
 
    if ( (exMqId = mq_open (QNAM, O_CREAT | O_RDWR, 0, &attr)) ==  
         (mqd_t) - 1 ) 
        { 
        printf ("mq_open failed\n"); 
        return (-1); 
        }
    /*  
     * Set up notification: fill in a sigevent structure and pass it  
     * to mq_notify(). The queue ID is passed as an argument to the 
     * signal handler. 
     */
    sigNotify.sigev_signo      = SIGUSR1; 
    sigNotify.sigev_notify     = SIGEV_SIGNAL; 
    sigNotify.sigev_value.sival_int = (int) exMqId; 
 
    if (mq_notify (exMqId, &sigNotify) == -1) 
        { 
        printf ("mq_notify failed\n"); 
        return (-1); 
        }
    /*  
     * We just created the message queue, but it may not be empty; 
     * a higher-priority task may have placed a message there while 
     * we were requesting notification. mq_notify() does nothing if 
     * messages are already in the queue; therefore we try to  
     * retrieve any messages already in the queue. 
     */
    exMqRead (exMqId);
    /*  
     * Now we know the queue is empty, so we will receive a signal 
     * the next time a message arrives.  
     * 
     * We send a message, which causes the notify handler to be invoked.  
     * It is a little silly to have the task that gets the notification  
     * be the one that puts the messages on the queue, but we do it here  
     * to simplify the example. A real application would do other work 
     * instead at this point.  
     */
    if (mq_send (exMqId, pMess, 1 + strlen (pMess), 0) == -1) 
        { 
        printf ("mq_send failed\n"); 
        return (-1); 
        }
    /* Cleanup */ 
    if (mq_close (exMqId) == -1) 
        { 
        printf ("mq_close failed\n"); 
        return (-1); 
        }
    /* More cleanup */ 
    if (mq_unlink (QNAM) == -1) 
        { 
        printf ("mq_unlink failed\n"); 
        return (-1); 
        } 
 
    return (0); 
    }
/* 
* exNotificationHandle - handler to read in messages 
* 
* This routine is a signal handler; it reads in messages from a 
* message queue. 
*/
static void exNotificationHandle  
    ( 
    int       sig,            /* signal number */ 
    siginfo_t * pInfo,        /* signal information */ 
    void  *    pSigContext    /* unused (required by posix) */ 
    ) 
    { 
    struct sigevent   sigNotify; 
    mqd_t             exMqId;
    /* Get the ID of the message queue out of the siginfo structure. */ 
    exMqId = (mqd_t) pInfo->si_value.sival_int;
    /*  
     * Request notification again; it resets each time  
     * a notification signal goes out. 
     */
    sigNotify.sigev_signo = pInfo->si_signo; 
    sigNotify.sigev_value = pInfo->si_value; 
    sigNotify.sigev_notify = SIGEV_SIGNAL; 
 
    if (mq_notify (exMqId, &sigNotify) == -1) 
        { 
        printf ("mq_notify failed\n"); 
        return; 
        }
    /* Read in the messages */ 
    exMqRead (exMqId); 
    }
/* 
 * exMqRead - read in messages 
 * 
 * This small utility routine receives and displays all messages 
 * currently in a POSIX message queue; assumes queue has O_NONBLOCK. 
 */
static void exMqRead 
    ( 
    mqd_t      exMqId 
    ) 
    { 
    char       msg[MSG_SIZE]; 
    int        prio;
    /*  
     * Read in the messages - uses a loop to read in the messages 
     * because a notification is sent ONLY when a message is sent on 
     * an EMPTY message queue. There could be multiple msgs if, for 
     * example, a higher-priority task was sending them. Because the 
     * message queue was opened with the O_NONBLOCK flag, eventually 
     * this loop exits with errno set to EAGAIN (meaning we did an 
     * mq_receive() on an empty message queue). 
     */
    while (mq_receive (exMqId, msg, MSG_SIZE, &prio) != -1) 
        { 
        printf ("exMqRead: received message: %s\n",msg); 
        } 
 
    if (errno != EAGAIN) 
        { 
        printf ("mq_receive: errno = %d\n", errno); 
        } 
    }


3.9    POSIX Queued Signals

The sigqueue( ) routine provides an alternative to kill( ) for sending signals to a task. The important differences between the two are:

  • sigqueue( ) includes an application-specified value that is sent as part of the signal. You can use this value to supply whatever context your signal handler finds useful. This value is of type sigval (defined in signal.h); the signal handler finds it in the si_value field of one of its arguments, a structure siginfo_t. An extension to the POSIX sigaction( ) routine allows you to register signal handlers that accept this additional argument.

  • sigqueue( ) enables the queueing of multiple signals for any task. The kill( ) routine, by contrast, delivers only a single signal, even if multiple signals arrive before the handler runs.

VxWorks includes seven signals reserved for application use, numbered consecutively from SIGRTMIN. The presence of these reserved signals is required by POSIX 1003.1b, but the specific signal values are not; for portability, specify these signals as offsets from SIGRTMIN (for example, write SIGRTMIN+2 to refer to the third reserved signal number). All signals delivered with sigqueue( ) are queued by numeric order, with lower-numbered signals queuing ahead of higher-numbered signals.

POSIX 1003.1b also introduced an alternative means of receiving signals. The routine sigwaitinfo( ) differs from sigsuspend( ) or pause( ) in that it allows your application to respond to a signal without going through the mechanism of a registered signal handler: when a signal is available, sigwaitinfo( ) returns the value of that signal as a result, and does not invoke a signal handler even if one is registered. The routine sigtimedwait( ) is similar, except that it can time out.

For detailed information on signals, see the reference entry for sigLib.

Table 3-8:   POSIX 1003.1b Queued Signal Calls


Call
Description

sigqueue( )
Sends a queued signal.
sigwaitinfo( )
Waits for a signal.
sigtimedwait( )
Waits for a signal with a timeout.

To configure VxWorks with POSIX queued signals, use the INCLUDE_POSIX_SIGNALS component. This component automatically initializes POSIX queued signals with sigqueueInit( ). The sigqueueInit( ) routine allocates buffers for use by sigqueue( ). which requires a buffer for each currently queued signal. A call to sigqueue( ) fails if no buffer is available.


1:  Asynchronous cancellation is actually implemented with a special signal, SIGCANCEL, which users should be careful to not block or ignore.

2:  Some host operating systems, such as UNIX, require symbolic names for objects that are to be shared among processes. This is because processes do not normally share memory in such operating systems. In VxWorks, there is no requirement for named semaphores, because all kernel objects have unique identifiers. However, using named semaphores of the POSIX variety provides a convenient way of determining the object's ID.

3:  This is not a POSIX routine, nor is it designed for use from programs; use it from the Tornado shell (see the Tornado User's Guide: Shell for details).