POSIX interface
KideRT implements a documented subset of POSIX.1-2024 natively in the kernel. Threads, mutexes, semaphores, message queues and timers use the interface your code already knows.
#include <pthread.h>
#include <time.h>
#define PERIOD_NS 250000L /* 250 µs */
static void *sampling(void *arg)
{
struct timespec next;
clock_gettime(CLOCK_MONOTONIC, &next);
for (;;)
{
next.tv_nsec += PERIOD_NS;
if (next.tv_nsec >= 1000000000L)
{
next.tv_sec += 1;
next.tv_nsec -= 1000000000L;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
read_sensor(arg);
}
}#include <pthread.h>
#include <semaphore.h>
static pthread_mutex_t lock;
static sem_t sample_ready;
static void *control(void *arg)
{
for (;;)
{
sem_wait(&sample_ready);
pthread_mutex_lock(&lock);
update_output();
pthread_mutex_unlock(&lock);
}
}
void start_control(void)
{
pthread_attr_t attr;
pthread_t thread;
struct sched_param param = { .sched_priority = 12 };
sem_init(&sample_ready, 0, 0);
pthread_mutex_init(&lock, NULL);
pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
pthread_attr_setschedparam(&attr, ¶m);
pthread_create(&thread, &attr, control, NULL);
}#include <mqueue.h>
#include <fcntl.h>
static mqd_t samples;
static void *telemetry(void *arg)
{
struct sample s;
for (;;)
{
mq_receive(samples, (char *)&s, sizeof s, NULL);
transmit(&s);
}
}
void open_samples(void)
{
struct mq_attr attr = {
.mq_maxmsg = 8,
.mq_msgsize = sizeof(struct sample),
};
samples = mq_open("/samples", O_CREAT | O_RDWR, 0600, &attr);
}#include <signal.h>
#include <time.h>
static void on_timeout(union sigval arg)
{
link_lost();
}
static void *receiver(void *arg)
{
struct sigevent ev = {
.sigev_notify = SIGEV_THREAD,
.sigev_notify_function = on_timeout,
};
struct itimerspec one_shot = {
.it_value = { .tv_nsec = 100000000L }, /* 100 ms */
};
timer_t timeout;
timer_create(CLOCK_REALTIME, &ev, &timeout);
for (;;)
{
timer_settime(timeout, 0, &one_shot, NULL);
receive_frame();
}
}- The loop that runs on Linux runs on KideRT.
- On KideRT, each wakeup is set for the requested time, to the resolution of the hardware timer.
- Priority and policy are set through thread attributes, as on Linux. A higher value is a higher priority.
- A thread waiting on a semaphore or a held mutex blocks without consuming CPU time.
- The receiver blocks until a message arrives; a sender blocks while the queue is full.
- The mode argument belongs to the call. KideRT runs a single process, so it has nothing to apply it to and accepts it as written.
- A timer is created on a clock and armed with
timer_settime(), as on Linux. SIGEV_THREAD names the function that runs when it expires. - On KideRT, each expiry is set for the requested time, to the resolution of the hardware timer.
A familiar interface
POSIX is the interface most systems programmers already know. On KideRT, threads, synchronisation, message queues, clocks and timers are called the way they are called on Linux, with the same headers, the same functions, the same arguments and return values.
The kernel-specific API is small and stays at the edges, which are bring-up, configuration and the target. Application code is written to POSIX, with no wrapper between it and the kernel. A pthread_mutex_lock() call, for example, is a kernel operation.
- Headers
- <pthread.h>, <semaphore.h>, <mqueue.h>, <time.h>, <signal.h>, <sched.h>, <fcntl.h>, <unistd.h>, <sys/types.h> and <errno.h>.
- Calls
- Function names, arguments and return values as POSIX.1-2024 specifies them. Every call is O(1).
- Scheduling
- Thread priorities, with the SCHED_FIFO and SCHED_RR policies set through thread attributes.
- Mutex protocols
- PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT and PTHREAD_PRIO_PROTECT, chosen per mutex.
- Time
- CLOCK_REALTIME and CLOCK_MONOTONIC, with relative and absolute sleeps and timers on either. A sleep or timer can be set in milliseconds, microseconds or nanoseconds; the resolution is that of the hardware timer, not a tick, and
clock_getres()reports it.
Portable between KideRT and Linux
The subset is the part of POSIX that application code on a microcontroller uses, and the part an embedded Linux application uses for the same work. Code written to it depends on the standard, not on KideRT.
Read about porting from LinuxShare code with embedded Linux
Code written to the subset builds for KideRT and for embedded Linux from the same source. Threading, synchronisation and timing code can be shared across a product line with both kinds of device.
Bring existing code with you
Application code and libraries written against pthreads port without a compatibility layer, provided they use interfaces in the subset. The API matrix tells you before you start.
Keep the application independent of the kernel
A proprietary kernel API ties application code to one vendor. Code written to POSIX moves between KideRT and embedded Linux, in either direction.
The specification and the profile
What does POSIX.1-2024 specify?
POSIX.1-2024 is the current revision of IEEE Std 1003.1, the standard that defines the operating system interface Linux follows: the C functions, headers and behaviour an application can rely on. IEEE and The Open Group maintain it jointly. Code written to it moves between the systems that implement it.
Why a subset, and why PSE51?
A microcontroller running one application has no use for most of POSIX: processes, a file system, users, networking. IEEE Std 1003.13 defines four real-time profiles for exactly this reason. The smallest, PSE51, the Minimal Realtime System Profile, describes a system with a single process, many threads, no file system and no memory-management unit. That is a Cortex-M running KideRT. The profile keeps what application code needs, threads, synchronisation, message passing, clocks and timers, and drops what the hardware cannot use. KideRT’s subset follows it.
Is KideRT conformant to PSE51?
No, and KideRT does not claim to be. PSE51 is the model for the shape of the subset; the API matrix defines the subset, function by function. Condition variables and signals, for example, are part of the profile and are not implemented in KideRT. Check the matrix against the interfaces your code uses.
Does a standard interface cost anything on a microcontroller?
KideRT implements the POSIX calls as the kernel’s own interface, not as a layer over a different native API. The memory footprint documentation gives measured kernel sizes per feature, so the cost of each part of the interface can be read off before you commit.
What the subset covers
This summary names the main areas on each side. The API matrix marks every function as supported or not supported.
See the POSIX API matrixSee what replaces the restCovered
- Threads, with cancellation and thread-local storage
- Thread priorities and yielding
- Mutexes, with priority inheritance and priority ceiling
- Semaphores
- Message queues
- Clocks, timers and sleeps
Not covered
- Condition variables
- Read-write locks, barriers and spin locks
- Robust and process-shared mutexes
- Signals
- File descriptor I/O
Porting to KideRT
Discuss the interfaces your code uses, and how they map onto the subset, with our engineers.