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.

From Linux to KideRT
Choose an example
Linux

A count, a lock and a condition, and the waiter sleeps until the count is above zero.

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);}
KideRT

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);}
Linux

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, &param);    pthread_create(&thread, &attr, control, NULL);}
KideRT

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, &param);    pthread_create(&thread, &attr, control, NULL);}
Linux

glibc sizes the stack from the stack limit, usually 8 MB.

void start_logger(void){    pthread_t thread;     pthread_create(&thread, NULL, logger, NULL);}
KideRT

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);}
Linux

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;}
KideRT

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.