Nancy Grace Roman Space Telescope's
ACS (Attitude Control System)
So Real-Time means really fast...right?
PREEMPT_RT
:
SCHED_OTHER
#define _GNU_SOURCE #include <printf.h> #include <pthread.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void pin_to_cpu(int cpu) { cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); } void* low_prio_thread(void* arg) { pin_to_cpu(0); printf("low_prio: started\n"); for(volatile long i = 0; i < 1e10; ++i) ; printf("low_prio: finished\n"); return NULL; } void* high_prio_thread(void* arg) { pin_to_cpu(0); // ensure low prio thread has started sleep(1); printf("hig_prio: started\n"); for(volatile long i = 0; i < 1e10; ++i) ; printf("hig_prio: finished\n"); return NULL; } int main() { // initialize threads pthread_t low, high; pthread_attr_t attr; pthread_attr_init(&attr); pthread_create(&low, &attr, low_prio_thread, NULL); pthread_create(&high, &attr, high_prio_thread, NULL); // cleanup pthread_join(low, NULL); pthread_join(high, NULL); pthread_attr_destroy(&attr); return 0; }
SCHED_OTHER
low_prio: started
hig_prio: started
low_prio: finished
hig_prio: finished
SCHED_FIFO
/SCHED_RR
#define _GNU_SOURCE #include <printf.h> #include <pthread.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void pin_to_cpu(int cpu) { cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); } void* low_prio_thread(void* arg) { pin_to_cpu(0); printf("low_prio: started\n"); for(volatile long i = 0; i < 1e10; ++i) ; printf("low_prio: finished\n"); return NULL; } void* high_prio_thread(void* arg) { pin_to_cpu(0); // ensure low prio thread has started sleep(1); printf("hig_prio: started\n"); for(volatile long i = 0; i < 1e10; ++i) ; printf("hig_prio: finished\n"); return NULL; } int main() { // initialize threads pthread_t low, high; pthread_attr_t attr; struct sched_param param; pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_FIFO); // pthread_attr_setschedpolicy(&attr, SCHED_OTHER); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); param.sched_priority = 10; pthread_attr_setschedparam(&attr, ¶m); pthread_create(&low, &attr, low_prio_thread, NULL); param.sched_priority = 30; pthread_attr_setschedparam(&attr, ¶m); pthread_create(&high, &attr, high_prio_thread, NULL); // cleanup pthread_join(low, NULL); pthread_join(high, NULL); pthread_attr_destroy(&attr); return 0; }
Requires root or high enough rtprio
in limits.conf
[3]
SCHED_FIFO
/SCHED_RR
[3] low_prio: started
hig_prio: started
hig_prio: finished
low_prio: finished
#define _GNU_SOURCE #include <printf.h> #include <pthread.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> pthread_mutex_t mutex; void pin_to_cpu(int cpu) { cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); } void* low_prio_thread(void* arg) { pin_to_cpu(0); printf("low_prio: started, trying to acq mutex\n"); pthread_mutex_lock(&mutex); printf("low_prio: acquired mutex\n"); // simulate long operation // both sleep and busy wait work, though differently // sleep(4); for(volatile long i = 0; i < 1e10; ++i) ; printf("low_prio: releasing mutex\n"); pthread_mutex_unlock(&mutex); printf("low_prio: finished\n"); return NULL; } void* high_prio_thread(void* arg) { pin_to_cpu(0); // ensure low prio thread has lock sleep(1); printf("hig_prio: started, trying to acq mutex\n"); pthread_mutex_lock(&mutex); printf("hig_prio: acquired mutex\n"); printf("hig_prio: releasing mutex\n"); pthread_mutex_unlock(&mutex); printf("hig_prio: finished\n"); return NULL; } void* medium_prio_thread(void* arg) { pin_to_cpu(0); // ensure high prio thread waits for lock sleep(2); printf("med_prio: started\n"); // simulate CPU-intensive task // don't sleep here because we want to block the CPU for(volatile long i = 0; i < 1e10; ++i) ; printf("med_prio: finished\n"); return NULL; } int main() { // initialize mutex pthread_mutexattr_t mutex_attr; pthread_mutexattr_init(&mutex_attr); pthread_mutex_init(&mutex, &mutex_attr); // initialize threads pthread_t low, medium, high; pthread_attr_t attr; struct sched_param param; pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_FIFO); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); param.sched_priority = 10; pthread_attr_setschedparam(&attr, ¶m); pthread_create(&low, &attr, low_prio_thread, NULL); param.sched_priority = 30; pthread_attr_setschedparam(&attr, ¶m); pthread_create(&high, &attr, high_prio_thread, NULL); param.sched_priority = 20; pthread_attr_setschedparam(&attr, ¶m); pthread_create(&medium, &attr, medium_prio_thread, NULL); // cleanup pthread_join(low, NULL); pthread_join(medium, NULL); pthread_join(high, NULL); pthread_attr_destroy(&attr); pthread_mutex_destroy(&mutex); pthread_mutexattr_destroy(&mutex_attr); return 0; }
low_prio: started, trying to acq mutex
low_prio: acquired mutex
hig_prio: started, trying to acq mutex
med_prio: started
med_prio: finished
low_prio: releasing mutex
hig_prio: acquired mutex
hig_prio: releasing mutex
hig_prio: finished
low_prio: finished
#define _GNU_SOURCE #include <printf.h> #include <pthread.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> pthread_mutex_t mutex; void pin_to_cpu(int cpu) { cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); } void* low_prio_thread(void* arg) { pin_to_cpu(0); printf("low_prio: started, trying to acq mutex\n"); pthread_mutex_lock(&mutex); printf("low_prio: acquired mutex\n"); // simulate long operation // both sleep and busy wait work, though differently // sleep(4); for(volatile long i = 0; i < 1e10; ++i) ; printf("low_prio: releasing mutex\n"); pthread_mutex_unlock(&mutex); printf("low_prio: finished\n"); return NULL; } void* high_prio_thread(void* arg) { pin_to_cpu(0); // ensure low prio thread has lock sleep(1); printf("hig_prio: started, trying to acq mutex\n"); pthread_mutex_lock(&mutex); printf("hig_prio: acquired mutex\n"); printf("hig_prio: releasing mutex\n"); pthread_mutex_unlock(&mutex); printf("hig_prio: finished\n"); return NULL; } void* medium_prio_thread(void* arg) { pin_to_cpu(0); // ensure high prio thread waits for lock sleep(2); printf("med_prio: started\n"); // simulate CPU-intensive task // don't sleep here because we want to block the CPU for(volatile long i = 0; i < 1e10; ++i) ; printf("med_prio: finished\n"); return NULL; } int main() { // initialize mutex pthread_mutexattr_t mutex_attr; pthread_mutexattr_init(&mutex_attr); // pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_NONE); pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT); pthread_mutex_init(&mutex, &mutex_attr); // initialize threads pthread_t low, medium, high; pthread_attr_t attr; struct sched_param param; pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_FIFO); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); param.sched_priority = 10; pthread_attr_setschedparam(&attr, ¶m); pthread_create(&low, &attr, low_prio_thread, NULL); param.sched_priority = 30; pthread_attr_setschedparam(&attr, ¶m); pthread_create(&high, &attr, high_prio_thread, NULL); param.sched_priority = 20; pthread_attr_setschedparam(&attr, ¶m); pthread_create(&medium, &attr, medium_prio_thread, NULL); // cleanup pthread_join(low, NULL); pthread_join(medium, NULL); pthread_join(high, NULL); pthread_attr_destroy(&attr); pthread_mutex_destroy(&mutex); pthread_mutexattr_destroy(&mutex_attr); return 0; }
low_prio: started, trying to acq mutex
low_prio: acquired mutex
hig_prio: started, trying to acq mutex
low_prio: releasing mutex
hig_prio: acquired mutex
hig_prio: releasing mutex
hig_prio: finished
med_prio: started
med_prio: finished
low_prio: finished
rt_mutex
[6]Kernel Config
CONFIG_PREEMPT_NONE
CONFIG_PREEMPT_VOLUNTARY
might_sleep
in kernelCONFIG_PREEMPT
CONFIG_PREEMPT_RT
raw_spinlock_t
or IRQraw_spinlock_t
static inline void __raw_spin_lock(raw_spinlock_t *lock) { preempt_disable(); spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); } #endif /* !CONFIG_GENERIC_LOCKBREAK || CONFIG_DEBUG_LOCK_ALLOC */ static inline void __raw_spin_unlock(raw_spinlock_t *lock) { spin_release(&lock->dep_map, _RET_IP_); do_raw_spin_unlock(lock); preempt_enable(); }
raw_spinlock_t
raw_spinlock_t
without preempt_disable
rt_mutex
spinlock_t
doesn't spin #ifndef CONFIG_PREEMPT_RT // --snip-- typedef struct spinlock { union { struct raw_spinlock rlock; // --snip-- }; } spinlock_t; // --snip-- #else /* !CONFIG_PREEMPT_RT */ // --snip-- typedef struct spinlock { struct rt_mutex_base lock; // --snip-- } spinlock_t;
struct rt_mutex_base { raw_spinlock_t wait_lock; struct rb_root_cached waiters; struct task_struct *owner; }; // --snip-- struct rt_mutex { struct rt_mutex_base rtmutex; // --snip-- };
SCHED_FIFO
/SCHED_RR
PTHREAD_PRIO_INHERIT
PREEMPT_RT
rt_mutex
instead of raw_spinlock_t