Kernel Objects Overview

Zephyr provides various kernel objects for synchronization and inter-thread communication. This guide helps you choose the right one.

Object Taxonomy

flowchart TB
    ROOT[Kernel Objects] --> SYNC[Synchronization]
    ROOT --> DATA[Data Passing]
    ROOT --> MEM[Memory]

    SYNC --> MUTEX[Mutex]
    SYNC --> SEM[Semaphore]
    SYNC --> CONDVAR[Condition Variable]
    SYNC --> SPIN[Spinlock]
    SYNC --> ATOMIC[Atomics]

    DATA --> MSGQ[Message Queue]
    DATA --> FIFO[FIFO]
    DATA --> LIFO[LIFO]
    DATA --> PIPE[Pipe]
    DATA --> MBOX[Mailbox]
    DATA --> EVENT[Events/Poll]

    MEM --> HEAP[Heap]
    MEM --> POOL[Memory Pool]
    MEM --> SLAB[Memory Slab]

Quick Reference

Object Use Case ISR Safe? Blocking?
Mutex Protect shared resource No Yes
Semaphore Signaling, counting Give: Yes Yes
Spinlock Short critical sections Yes Busy-wait
Message Queue Fixed-size message passing Put: Yes Yes
FIFO Variable-size data, pointers Put: Yes Yes
LIFO Stack-like data Put: Yes Yes
Pipe Byte streams No Yes
Mailbox Synchronous messaging No Yes
Events Multi-event waiting Signal: Yes Yes

Selection Flowchart

flowchart TD
    START[Need kernel object] --> Q1{Protect shared data?}

    Q1 -->|Yes| Q2{ISR involved?}
    Q1 -->|No| Q3{Pass data between threads?}

    Q2 -->|Yes| SPIN[Use Spinlock]
    Q2 -->|No| MUTEX[Use Mutex]

    Q3 -->|Yes| Q4{Data size fixed?}
    Q3 -->|No| Q5{Signal event?}

    Q4 -->|Yes| MSGQ[Use Message Queue]
    Q4 -->|No| Q6{Need copies?}

    Q6 -->|Yes| PIPE[Use Pipe]
    Q6 -->|No| FIFO[Use FIFO]

    Q5 -->|Yes| Q7{Multiple events?}
    Q5 -->|No| Q8{Count resources?}

    Q7 -->|Yes| EVENT[Use Events/Poll]
    Q7 -->|No| SEM[Use Semaphore]

    Q8 -->|Yes| SEM
    Q8 -->|No| CONDVAR[Use Condition Variable]

Object Summaries

Mutex

Best for protecting shared resources in thread context:

K_MUTEX_DEFINE(my_mutex);
k_mutex_lock(&my_mutex, K_FOREVER);
/* Critical section */
k_mutex_unlock(&my_mutex);
  • Has priority inheritance
  • Not ISR-safe
  • Tracks ownership

Semaphore

Best for signaling and resource counting:

K_SEM_DEFINE(my_sem, 0, 10);
k_sem_give(&my_sem);      /* ISR-safe */
k_sem_take(&my_sem, K_FOREVER);
  • ISR-safe for give
  • No ownership tracking
  • Count-based

Spinlock

Best for short critical sections with ISRs:

K_SPINLOCK_DEFINE(my_lock);
k_spinlock_key_t key = k_spin_lock(&my_lock);
/* Very short critical section */
k_spin_unlock(&my_lock, key);
  • ISR-safe
  • Busy-waits
  • Keep sections minimal

Message Queue

Best for fixed-size message passing:

K_MSGQ_DEFINE(my_msgq, sizeof(struct msg), 10, 4);
k_msgq_put(&my_msgq, &msg, K_NO_WAIT);  /* ISR-safe */
k_msgq_get(&my_msgq, &msg, K_FOREVER);
  • Copies data
  • Fixed message size
  • Put is ISR-safe (non-blocking)

FIFO

Best for passing pointers/variable data:

K_FIFO_DEFINE(my_fifo);
k_fifo_put(&my_fifo, item);  /* ISR-safe */
void *item = k_fifo_get(&my_fifo, K_FOREVER);
  • Passes pointers
  • Variable sizes
  • First-in-first-out

Events

Best for waiting on multiple conditions:

K_EVENT_DEFINE(my_event);
k_event_post(&my_event, 0x01);  /* ISR-safe */
k_event_wait(&my_event, 0x03, false, K_FOREVER);
  • Bitmask-based
  • Wait for any/all
  • Signal is ISR-safe

Common Patterns

Producer-Consumer

/* Producer (can be ISR) */
void producer(void)
{
    struct data_item item = get_data();
    k_msgq_put(&data_queue, &item, K_NO_WAIT);
}

/* Consumer thread */
void consumer(void *p1, void *p2, void *p3)
{
    struct data_item item;
    while (1) {
        k_msgq_get(&data_queue, &item, K_FOREVER);
        process_item(&item);
    }
}

Resource Pool

K_SEM_DEFINE(buffer_sem, NUM_BUFFERS, NUM_BUFFERS);

void *acquire_buffer(k_timeout_t timeout)
{
    if (k_sem_take(&buffer_sem, timeout) == 0) {
        return allocate_from_pool();
    }
    return NULL;
}

void release_buffer(void *buf)
{
    return_to_pool(buf);
    k_sem_give(&buffer_sem);
}

Event-Driven Processing

#define EVENT_DATA_READY  BIT(0)
#define EVENT_TIMEOUT     BIT(1)
#define EVENT_SHUTDOWN    BIT(2)

void event_thread(void *p1, void *p2, void *p3)
{
    while (1) {
        uint32_t events = k_event_wait(&my_event,
                                       EVENT_DATA_READY | EVENT_SHUTDOWN,
                                       false, K_FOREVER);

        if (events & EVENT_SHUTDOWN) {
            break;
        }
        if (events & EVENT_DATA_READY) {
            process_data();
        }
    }
}

Memory Objects

Memory Slab (Fixed-Size Blocks)

K_MEM_SLAB_DEFINE(my_slab, BLOCK_SIZE, NUM_BLOCKS, ALIGN);

void *block;
k_mem_slab_alloc(&my_slab, &block, K_NO_WAIT);
/* Use block */
k_mem_slab_free(&my_slab, block);

Heap

/* System heap */
void *ptr = k_malloc(size);
k_free(ptr);

/* Custom heap */
K_HEAP_DEFINE(my_heap, HEAP_SIZE);
void *ptr = k_heap_alloc(&my_heap, size, K_NO_WAIT);
k_heap_free(&my_heap, ptr);

Decision Table

Scenario Recommended Object
Protect global variable Mutex
ISR signals thread Semaphore
Pass sensor readings Message Queue
Pass network packets FIFO + buffer pool
Thread waits for multiple sources Events + k_poll
Fixed-size buffer allocation Memory Slab
Variable-size allocation Heap
Short ISR-thread shared access Spinlock

Next Steps

Part 4 covers Synchronization and IPC in detail.


Back to top

Zephyr RTOS Programming Guide is not affiliated with the Zephyr Project or Linux Foundation. Content is provided for educational purposes.

This site uses Just the Docs, a documentation theme for Jekyll.