This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
realtime:documentation:howto:howto_rt_application_base [2016/09/27 16:58] anna-maria adapt link to sched_policy_prio |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== HOWTO build a simple RT application ====== | ||
- | |||
- | 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. | ||
- | |||
- | ===== Basic prerequisites ===== | ||
- | |||
- | Three basic prerequisites are introduced in the next subsections, | ||
- | followed by a short example illustrating those aspects. | ||
- | |||
- | ==== Scheduling and priority ==== | ||
- | |||
- | The [[realtime:documentation:technical_basics:sched_policy_prio|scheduling policy]] as well as the priority | ||
- | must be set by the application explicitly. There are two possibilities | ||
- | for this: | ||
- | |||
- | - **Using ''sched_setscheduler()''** \\ <wrap> | ||
- | This funcion needs to be called in the start routine of the pthread | ||
- | before calculating RT specific stuff. | ||
- | </wrap> | ||
- | - **Using pthread attributes** \\ <wrap> | ||
- | The functions ''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. | ||
- | </wrap> | ||
- | |||
- | ==== Memory locking ==== | ||
- | |||
- | 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); | ||
- | |||
- | ==== Stack prefaulting ==== | ||
- | |||
- | 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); | ||
- | |||
- | ===== Example ===== | ||
- | |||
- | <code c> | ||
- | /* | ||
- | * 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; | ||
- | } | ||
- | |||
- | </code> | ||