This is an old revision of the document!
The POSIX API forms the basis of real-time applications running under PREEMPT_RT. For the real-time thread a POSIX thread is used (pthread). Every real-time application needs proper handling in several basic areas like scheduling, priority, memory locking and stack prefaulting.
Three basic prerequisites are introduced in the next subsections, followed by a short example illustrating those aspects.
The scheduling policy as well as the priority must be set by the application explicitly. There are two possibilities for this:
sched_setscheduler()
pthread_attr_setschedpolicy()
and
pthread_attr_setschedparam()
offer the interfaces to set policy and
priority. Furthermore scheduler inheritance needs to be set properly
to PTHREAD_EXPLICIT_SCHED by using pthread_attr_setinheritsched()
.
This forces the new thread to use the policy and priority specified by
the pthread attributes and not to use the inherit scheduling of the
thread which created the real-time thread.
In real-time applications it is important to avoid non-deterministic
behavior. If the memory that is needed by the real-time application
is not locked in the RAM, this memory could be paged out. If the
memory is not paged in when the application tries to access the
memory, a page fault occurs causing non-deterministic high latency.
For this reason memory should be locked in real-time applications.
The memory lock persists until the process owning it terminates or
explicitly unlocks it by calling munlock()
or munlockall()
.
Be aware that page faults due to paged out memory occur in systems
with swap as well as in systems without swap. In addition, the binary
of the executed application itself could be paged out.
The following call of mlockall()
locks all current pages mapped
into the address space of the process as well as all pages that will
be mapped in the future.
mlockall(MCL_CURRENT|MCL_FUTURE);
Since page faults cause non-deterministic behavior, the stack should be prefaulted before the real-time critical section starts. In case several real-time threads are used, it should be done for each thread individually. In the following example, a memory block of a certain size is allocated. All of its pages are touched to get them mapped into RAM ensuring that no page faults occur later.
void *buffer;
buffer = mmap(NULL, MSIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); memset(&buffer, 0, MSIZE);
The prefaulted stack can be assigned to a thread. (&attr
is a
pthread_attr_t
pointer; the pthread attribute needs to have been
previously initialized):
pthread_attr_setstack(&attr, buffer, PTHREAD_STACK_MIN);
/* * POSIX Real Time Example * using a single pthread as RT thread */ #include <stdlib.h> #include <stdio.h> #include <time.h> #include <sched.h> #include <sys/mman.h> #include <string.h> #include <pthread.h> #include <limits.h> void *thread_func(void *data) { /* Do RT specific stuff here */ return NULL; } int main(int argc, char* argv[]) { struct sched_param param; void *stack_buf; pthread_t thread; pthread_attr_t attr; int ret; /* Lock memory */ if(mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { printf("mlockall failed: %m\n"); exit(-2); } /* Pre-fault stack for the thread */ stack_buf = mmap(NULL, PTHREAD_STACK_MIN, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (stack_buf == MAP_FAILED) { printf("mmap failed: %m\n"); exit(-1); } memset(stack_buf, 0, PTHREAD_STACK_MIN); /* Initialize pthread attributes (default values) */ ret = pthread_attr_init(&attr); if (ret) { printf("init pthread attributes failed\n"); goto out; } /* Set pthread stack to already pre-faulted stack */ ret = pthread_attr_setstack(&attr, stack_buf, PTHREAD_STACK_MIN); if (ret) { printf("pthread setstack failed\n"); goto out; } /* Set scheduler policy and priority of pthread */ ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO); if (ret) { printf("pthread setschedpolicy failed\n"); goto out; } param.sched_priority = 80; ret = pthread_attr_setschedparam(&attr, ¶m); if (ret) { printf("pthread setschedparam failed\n"); goto out; } /* Use scheduling parameters of attr */ ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); if (ret) { printf("pthread setinheritsched failed\n"); goto out; } /* Create a pthread with specified attributes */ ret = pthread_create(&thread, &attr, thread_func, NULL); if (ret) { printf("create pthread failed\n"); goto out; } /* Join the thread and wait until it is done */ ret = pthread_join(thread, NULL); if (ret) printf("join pthread failed: %m\n"); out: munmap(stack_buf, PTHREAD_STACK_MIN); return ret; }