Porting from embedded Linux
Linux on a Cortex-A, KideRT on a Cortex-M beside it, on one chip or one board. Both run pthreads, so threading and timing code can be shared. Code that leans on Linux defaults, or on calls outside the subset, needs a few changes.
static pthread_mutex_t lock;static pthread_cond_t ready;static int pending; static void *control(void *arg){ for (;;) { pthread_mutex_lock(&lock); while (pending == 0) { pthread_cond_wait(&ready, &lock); } pending--; pthread_mutex_unlock(&lock); update_output(); }} void sample_done(void){ pthread_mutex_lock(&lock); pending++; pthread_cond_signal(&ready); pthread_mutex_unlock(&lock);} void init_control(void){ pthread_mutex_init(&lock, NULL); pthread_cond_init(&ready, NULL);}One semaphore does all three. sem_post() counts an event, sem_wait() takes one or blocks. Where the condition guards data, use a message queue.
static sem_t ready; static void *control(void *arg){ for (;;) { sem_wait(&ready); update_output(); }} void sample_done(void){ sem_post(&ready);} void init_control(void){ sem_init(&ready, 0, 0);}SCHED_FIFO priorities run from 1 to 99 on Linux. The control thread sits near the top, at 90.
void start_control(void){ pthread_attr_t attr; struct sched_param param = { .sched_priority = 90 }; pthread_t thread; pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_FIFO); pthread_attr_setschedparam(&attr, ¶m); pthread_create(&thread, &attr, control, NULL);}KideRT’s priorities go up to 31, so 90 is out of range. sched_get_priority_max() gives the top priority on both systems.
void start_control(void){ pthread_attr_t attr; struct sched_param param; pthread_t thread; pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_FIFO); param.sched_priority = sched_get_priority_max(SCHED_FIFO); pthread_attr_setschedparam(&attr, ¶m); pthread_create(&thread, &attr, control, NULL);}glibc sizes the stack from the stack limit, usually 8 MB.
void start_logger(void){ pthread_t thread; pthread_create(&thread, NULL, logger, NULL);}The default comes from the kernel configuration, sized for a microcontroller. Size each stack for its thread, and keep the sizes in a header per target: glibc rejects stacks below its own minimum.
void start_logger(void){ pthread_attr_t attr; pthread_t thread; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 4096); pthread_create(&thread, &attr, logger, NULL);}The kernel is already running: main() starts the threads and waits for them.
int main(void){ pthread_t thread; pthread_create(&thread, NULL, control, NULL); pthread_join(thread, NULL); return 0;}main() runs before the kernel. It brings up the board and the system timer, then krt_kernel_boot() starts the kernel and runs startup() as the first thread. It returns only if the kernel cannot start, with the error.
static void *startup(void *arg){ pthread_t thread; pthread_create(&thread, NULL, control, NULL); return NULL;} int main(void){ bsp_init(); krt_systimer_configure(bsp_systickreload, bsp_systickgetcount, TIMER_MAX_PERIOD, TIMER_HZ, BSP_SYSTICK_IRQN); return krt_kernel_boot(NULL, NULL, startup, NULL);}What the subset leaves out
The calls the API matrix marks not supported are declared, so code that uses them still builds, and the linker will not flag them. Replace them with these.
See the POSIX API matrix- Condition variables
- A semaphore to signal an event; a message queue to pass data with it.
pthread_cond_wait()pthread_cond_signal() - Read-write locks, barriers and spin locks
- A mutex for the locks. A barrier becomes a count under a mutex and a semaphore.
pthread_rwlock_rdlock()pthread_barrier_wait()pthread_spin_lock() - Robust and process-shared mutexes
- A plain mutex, because KideRT runs one process. A cleanup handler unlocks it if its owner is cancelled.
pthread_mutexattr_setrobust()pthread_mutexattr_setpshared() - Signals
- A semaphore or a message queue to wake a thread.
pthread_kill()pthread_sigmask() - File descriptor I/O
- Driver functions, called directly.
read()write()
Porting questions
What happens to a call KideRT doesn’t support?
It still builds. The calls the API matrix marks not supported are declared, and they fail at run time. Check the matrix before you port; the linker won’t catch them.
Are thread priorities the same numbers as on Linux?
No. Linux’s real-time priorities go up to 99, KideRT’s up to 31. On both, a higher value is a higher priority.
How many threads, timers, mutexes and semaphores can I create?
Threads are capped at 256. Semaphores, mutexes, message queues and timers have no fixed count.
Can I leave out calls I don’t use?
Yes. Each system call can be switched off in the configuration. A call that’s switched off returns ENOSYS.
Porting to KideRT
Discuss the interfaces your code uses, and how they map onto the subset, with our engineers.
A count, a lock and a condition, and the waiter sleeps until the count is above zero.
*/