Real-time application development

Real-time application development with PREEMPT_RT

  • No special library is needed, the POSIX real-time API is part of the standard C library
  • The glibc C library is recommended, as support for some real-time features is not mature in other C libraries
    • Priority inheritance mutexes or NPTL on some architectures, for example
  • Compile a program
    • ARCH-linux-gcc -o myprog myprog.c -lrt
  • To get the documentation of the POSIX API
    • Install the manpages-posix-dev package
    • Run man function-name

Process, thread?

  • Confusion about the terms process, thread and task
  • In Unix, a process is created using fork() and is composed of
    • An address space, which contains the program code, data, stack, shared libraries, etc.
    • One thread, that starts executing the main() function.
    • Upon creation, a process contains one thread
  • Additional threads can be created inside an existing process, using pthread_create()
    • They run in the same address space as the initial thread of the process
    • They start executing a function passed as argument to pthread_create()

Process, thread: kernel point of view

  • The kernel represents each thread running in the system by a structure of type task_struct
  • From a scheduling point of view, it makes no difference between the initial thread of a process and all additional threads created dynamically using pthread_create()

Creating threads

  • Linux support the POSIX thread API
  • To create a new thread pthread_create(pthread_t thread, pthread_attr_t attr, void (routine)(void), void arg);

  • The new thread will run in the same address space, but will be scheduled independently

  • Exiting from a thread pthread_exit(void . *value_ptr);
  • Waiting for the termination of a thread pthread_join(pthread_t . thread, void *value_ptr);

Scheduling classes

  • The Linux kernel scheduler support different scheduling classes
  • The default class, in which processes are started by default is a time-sharing class
    • All processes, regardless of their priority, get some CPU time
    • The proportion of CPU time they get is dynamic and affected by the nice value, which ranges from -20 (highest) to 19 (lowest). Can be set using the nice or renice commands
  • The real-time classes SCHED_FIFO and SCHED_RR

    • The highest priority process gets all the CPU time, until it blocks.
    • In SCHED_RR, round-robin scheduling between the processes of the same priority. All must block before lower priority processes get CPU time.
    • Priorities ranging from 0 (lowest) to 99 (highest)
  • An existing program can be started in a specific scheduling class with a specific priority using the chrt command line tool

    • Example: chrt -f 99 ./myprog -f: SCHED_FIFO -r: SCHED_RR
  • The sched_setscheduler() API can be used to change the scheduling class and priority of a process int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);
    • policy can be SCHED_OTHER, SCHED_FIFO, SCHED_RR, etc.
    • param is a structure containing the priority
  • The priority can be set on a per-thread basis when a thread is created

    struct sched_param parm; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedpolicy(&attr, SCHED_FIFO); parm.sched_priority = 42; pthread_attr_setschedparam(&attr,&parm);

  • Then the thread can be created using pthread_create(), passing the attr structure.

  • Several other attributes can be defined this way: stack size, etc.

Memory locking

  • In order to solve the non-determinism introduced by virtualmemory, memory can be locked
    • Guarantee that the system will keep it allocated
    • Guarantee that the system has pre-loaded everything into memory
  • mlockall(MCL_CURRENT | MCL_FUTURE);
    • Locks all the memory of the current address space, for currently mapped pages and pages mapped in the future
  • Other, less useful parts of the API: munlockall, mlock,munlock.
  • Watch out for non-currently mapped pages
    • Stack pages
    • Dynamically-allocated memory

Mutexes

  • Allows mutual exclusion between two threads in the same address space
  • Initialization/destruction
    pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); pthread_mutex_destroy(pthread_mutex_t *mutex);

    • Lock/unlock
      pthread_mutex_lock(pthread_mutex_t *mutex); pthread_mutex_unlock(pthread_mutex_t *mutex);

    • Priority inheritance must be activated explicitly
      pthread_mutexattr_t attr; pthread_mutexattr_init (&attr); pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);

Timers

  • Timer creation
    timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid);
  • clockid is usually CLOCK_MONOTONIC. sigevent defines what happens upon timer expiration: send a signal or start a function in a new thread. timerid is the returned timer identifier.
  • Configure the timer for expiration at a given time

timer_settime(timer_t timerid, int flags,struct itimerspec *newvalue, struct itimerspec *oldvalue);

  • Delete a timer timer_delete(timer_t timerid)
  • Get the resolution of a clock, clock_getres
  • Other functions: timer_getoverrun(), timer_gettime()

Signals

  • Signals are asynchronous notification mechanisms
  • Notification occurs either
    • By the call of a signal handler. Be careful with the limitations of signal handlers!
    • By being unblocked from the sigwait(), sigtimedwait() or sigwaitinfo() functions. Usually better.
  • Signal behaviour can be configured using sigaction()
  • The mask of blocked signals can be changed with pthread_sigmask()
  • Delivery of a signal using pthread_kill() or tgkill()
  • All signals between SIGRTMIN and SIGRTMAX, 32 signals under Linux.

Inter-process communication

  • Semaphores
    • Usable between different processes using named semaphores
    • sem_open(), sem_close(), sem_unlink(), sem_init(), sem_destroy(), sem_wait(), sem_post(), etc.
  • Message queues
    • Allows processes to exchange data in the form of messages.
    • mq_open(), mq_close(), mq_unlink(), mq_send(), mq_receive(), etc.
  • Shared memory
    • Allows processes to communicate by sharing a segment of memory
    • shm_open(), ftruncate(), mmap(), munmap(), close(), shm_unlink()

results matching ""

    No results matching ""