Top 30 RTOS Interview Questions — FreeRTOS & Zephyr

Scheduling, Mutexes, Priority Inversion & More


If you are preparing for an embedded systems interview at companies like Qualcomm, Texas Instruments, Broadcom, or any product company working on microcontrollers, RTOS questions are almost guaranteed. This post covers the top 30 RTOS interview questions with clear answers and code examples — covering FreeRTOS and Zephyr.


Section 1 — RTOS Fundamentals


Q1. What is an RTOS and how is it different from a general-purpose OS?

An RTOS (Real-Time Operating System) is an operating system designed to execute tasks within guaranteed time bounds. The key difference is determinism — the system must respond to events within a defined deadline, every single time.

FeatureRTOSGeneral-Purpose OS
Response timeDeterministic (microseconds)Non-deterministic
SchedulingPriority-based, preemptiveFair-share, throughput-focused
FootprintSmall (KB to few MB)Large (hundreds of MB to GB)
ExamplesFreeRTOS, Zephyr, VxWorksLinux, Windows, macOS

Hard RTOS — missing a deadline is a system failure (pacemaker, airbag).

Soft RTOS — missing a deadline degrades quality but is not catastrophic (video streaming).


Q2. What are the core components of an RTOS?

Every RTOS provides these core services:

  • Task/Thread management — create, delete, suspend, resume tasks
  • Scheduler — decides which task runs on the CPU at any moment
  • Inter-task communication — queues, message buffers, mailboxes
  • Synchronization primitives — mutexes, semaphores, event groups
  • Timer services — software timers for periodic or one-shot callbacks
  • Memory management — static or dynamic heap allocation

Q3. What is a task in FreeRTOS? How do you create one?

A task in FreeRTOS is an independent thread of execution with its own stack, priority, and state. Each task runs as if it owns the CPU — the scheduler switches between them transparently.

// Task function signature
void vMyTask(void *pvParameters) {
    for (;;) {
        // task work here
        vTaskDelay(pdMS_TO_TICKS(100));  // yield for 100ms
    }
}

// Create task
xTaskCreate(
    vMyTask,          // function
    "MyTask",         // name (debug only)
    128,              // stack depth in words
    NULL,             // parameter passed to task
    2,                // priority (higher number = higher priority)
    NULL              // task handle (optional)
);

// Start scheduler — never returns
vTaskStartScheduler();

🚀 Bootcamp 4.0 — Early Bird Opens April 25

Want to Master All of This — With Structure & Live Feedback?

Bootcamp 4.0 covers everything you need to crack embedded system interviews at top companies:

C Programming DSA for Embedded RTOS (FreeRTOS & Zephyr) Linux Kernel Device Drivers

Join the Early Bird WhatsApp group — get first access to enrollment and special pricing before anyone else.

💬 Join Bootcamp 4.0 Early Bird Group

✅ Free to join  ·  224+ engineers already inside  ·  Early bird opens April 25

Q4. What are the different task states in FreeRTOS?

         xTaskCreate()
              |
              ▼
           READY ◄─────────────────────────────┐
              |                                 |
     Highest priority selected                  |
              |                            Task preempted /
              ▼                            time slice ends
           RUNNING ──────────────────────────────┘
              |
    vTaskSuspend() / waiting   vTaskDelete()
    for event/delay/semaphore       |
              |                     ▼
           BLOCKED               DELETED
              |
    Event received / delay expired
              |
           READY
  • Running — currently executing on CPU
  • Ready — able to run, waiting for CPU
  • Blocked — waiting for event, delay, semaphore, or queue
  • Suspended — explicitly suspended via vTaskSuspend(), only resumes via vTaskResume()

Q5. What is a tick in FreeRTOS and why does it matter?

The tick is a periodic timer interrupt that drives the FreeRTOS scheduler. Configured via configTICK_RATE_HZ in FreeRTOSConfig.h.

// FreeRTOSConfig.h
#define configTICK_RATE_HZ    1000   // 1ms tick

// Convert ms to ticks correctly — never hardcode ticks
vTaskDelay(pdMS_TO_TICKS(50));   // delay 50ms — tick-rate independent

Why it matters: If configTICK_RATE_HZ = 1000, the minimum delay resolution is 1ms. Using pdMS_TO_TICKS() makes your code portable if you change the tick rate.


Section 2 — Scheduling


Q6. How does the FreeRTOS scheduler work?

FreeRTOS uses a fixed-priority preemptive scheduler by default. The rules are:

1. The highest priority ready task always runs

2. If two tasks share the same priority, they share CPU time (round-robin with time slicing)

3. A running task is preempted the moment a higher-priority task becomes ready

4. Tasks yield the CPU by entering a blocked state (delay, waiting for queue/semaphore)

// configUSE_PREEMPTION = 1  → preemptive (default, recommended)
// configUSE_TIME_SLICING = 1 → round-robin among equal-priority tasks

Q7. What is the idle task and why must it always be able to run?

FreeRTOS automatically creates an Idle Task at priority 0 (lowest). It runs whenever no other task is ready.

Critical rule: At least one task must always be able to run. Since the idle task runs at priority 0, no task should block forever at priority 0 — otherwise the CPU has nothing to execute.

The idle task also performs memory cleanup — when a task is deleted, its stack and TCB (Task Control Block) are only freed when the idle task runs.

// Hook function — runs inside idle task (optional)
void vApplicationIdleHook(void) {
    // Put CPU to sleep to save power
    __WFI();  // Wait For Interrupt — ARM low-power instruction
}

Q8. What is the difference between vTaskDelay and vTaskDelayUntil?

Both delay a task, but they handle timing differently.

// vTaskDelay — relative delay from when it's called
// If task takes variable time, period DRIFTS over time
void vTask_Relative(void *p) {
    for (;;) {
        do_work();                     // takes variable time
        vTaskDelay(pdMS_TO_TICKS(100)); // 100ms AFTER do_work finishes
    }
}

// vTaskDelayUntil — absolute period, NO drift
// Use this for fixed-frequency control loops
void vTask_Absolute(void *p) {
    TickType_t xLastWake = xTaskGetTickCount();
    for (;;) {
        vTaskDelayUntil(&xLastWake, pdMS_TO_TICKS(100)); // exactly 100ms period
        do_work();
    }
}

Interview answer: Use vTaskDelayUntil for periodic tasks like sensor sampling or PID control loops where exact timing matters. Use vTaskDelay for simple delays where exact period doesn't matter.


Q9. What is cooperative vs preemptive scheduling?

Preemptive: Scheduler can forcibly remove a running task from CPU when a higher-priority task becomes ready. Default in FreeRTOS. Better for real-time responsiveness.

Cooperative: A task runs until it voluntarily yields the CPU (by calling taskYIELD() or a blocking API). No forced preemption. Simpler but dangerous — a misbehaving task starves everything else.

// Force yield in cooperative mode or to give equal-priority tasks a chance:
taskYIELD();

// In FreeRTOSConfig.h:
#define configUSE_PREEMPTION  1   // preemptive (recommended)
// #define configUSE_PREEMPTION 0  // cooperative

Q10. How does Zephyr's scheduler differ from FreeRTOS?

FeatureFreeRTOSZephyr
SchedulingFixed-priority preemptiveFixed-priority preemptive + optional round-robin
Thread typesTasks onlyCooperative threads + preemptive threads
Priority range0 (lowest) to configMAX_PRIORITIES-1-CONFIG_NUM_COOP_PRIORITIES to CONFIG_NUM_PREEMPT_PRIORITIES
Tickless idleSupportedSupported
SMP supportFreeRTOS SMP (limited)Full SMP support

In Zephyr, cooperative threads (negative priority) run without preemption by other threads of same type — they must explicitly yield. Preemptive threads (non-negative priority) work like FreeRTOS tasks.

// Zephyr thread creation
K_THREAD_DEFINE(my_tid, 1024,   // stack size
                my_thread_fn,    // entry function
                NULL, NULL, NULL, // parameters
                5,               // priority (preemptive)
                0, 0);           // flags, delay

Q11. What is stack overflow in RTOS and how do you detect it?

Each task in FreeRTOS has a fixed-size stack. If a task uses more stack than allocated (deep recursion, large local arrays), it corrupts adjacent memory — a very hard-to-debug bug.

// FreeRTOSConfig.h — enable stack overflow checking
#define configCHECK_FOR_STACK_OVERFLOW  2  // method 2 = most thorough

// Hook called when overflow detected
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
    // Log the task name, halt system
    printf("STACK OVERFLOW in task: %s\n", pcTaskName);
    taskDISABLE_INTERRUPTS();
    for (;;);
}

// Check how much stack a task has left at runtime:
UBaseType_t uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
// Returns minimum free stack words remaining — if close to 0, increase stack size

Q12. What is priority inversion and give a real-world example?

Priority inversion occurs when a high-priority task is indirectly blocked by a low-priority task holding a resource the high-priority task needs.

Classic example — Mars Pathfinder (1997):

  • Low-priority task (L) acquires a shared bus mutex
  • High-priority task (H) wakes up, tries to acquire the same mutex → blocks
  • Medium-priority task (M) preempts L (since M > L)
  • L never gets CPU time to release the mutex
  • H is effectively blocked by M — a lower-priority task
  • Watchdog detects H is not running → resets the rover
Timeline:
  L acquires mutex ──► M preempts L ──► H tries mutex, blocks
  H is now waiting on L, L can't run because M is running
  = Priority Inversion!

Section 3 — Mutexes & Semaphores


Q13. What is the difference between a mutex and a binary semaphore in FreeRTOS?

This is one of the most common RTOS interview questions.

FeatureMutexBinary Semaphore
PurposeMutual exclusion (protect shared resource)Signalling between tasks
OwnershipYes — only the task that takes it can give itNo ownership
Priority inheritanceYes (prevents priority inversion)No
Use from ISRNot recommendedYes — xSemaphoreGiveFromISR()
// Mutex — protect shared resource
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();

void vTask_Writer(void *p) {
    if (xSemaphoreTake(xMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        // Critical section — safe to access shared resource
        update_shared_buffer();
        xSemaphoreGive(xMutex);
    }
}

// Binary Semaphore — signal from ISR to task
SemaphoreHandle_t xSem = xSemaphoreCreateBinary();

void UART_IRQHandler(void) {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(xSem, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

void vTask_UART(void *p) {
    for (;;) {
        xSemaphoreTake(xSem, portMAX_DELAY);  // wait for ISR signal
        process_uart_data();
    }
}

Q14. What is priority inheritance and how does FreeRTOS implement it?

Priority inheritance is a mechanism to reduce priority inversion. When a high-priority task blocks waiting for a mutex held by a low-priority task, the RTOS temporarily boosts the low-priority task's priority to match the high-priority task.

Without priority inheritance:        With priority inheritance:
  H blocked waiting for mutex          H blocked waiting for mutex
  M preempts L → L starves            L boosted to H's priority
  H waits indefinitely                 L finishes, releases mutex
                                        L drops back to original priority
                                        H acquires mutex, runs

FreeRTOS mutexes (xSemaphoreCreateMutex()) automatically implement priority inheritance. Binary semaphores do not.

// Always use Mutex (not binary semaphore) when protecting shared resources
// accessed by tasks of different priorities
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();  // has priority inheritance
SemaphoreHandle_t xSem   = xSemaphoreCreateBinary(); // NO priority inheritance

Q15. What is a counting semaphore and when do you use it?

A counting semaphore maintains a count. Give increments the count, Take decrements it. Blocks when count reaches zero.

Use cases: Track available resources (buffer slots, connection pool), count events from ISR.

// Example: 5 slots in a resource pool
SemaphoreHandle_t xPool = xSemaphoreCreateCounting(5, 5); // max=5, initial=5

void vTask_Consumer(void *p) {
    for (;;) {
        xSemaphoreTake(xPool, portMAX_DELAY);  // wait for a slot
        use_resource();
        xSemaphoreGive(xPool);                 // return slot
    }
}

// Count events from ISR — initial count = 0
SemaphoreHandle_t xEventCount = xSemaphoreCreateCounting(10, 0);
// ISR calls xSemaphoreGiveFromISR() for each event
// Task calls xSemaphoreTake() to process each event one by one

Q16. What is a recursive mutex and when is it needed?

A regular mutex will deadlock if the same task tries to take it twice. A recursive mutex allows the same task to take it multiple times — it just increments an internal counter and requires an equal number of Give calls to release.

SemaphoreHandle_t xRecMutex = xSemaphoreCreateRecursiveMutex();

void vTask(void *p) {
    // Safe to call nested functions that also take the same mutex
    xSemaphoreTakeRecursive(xRecMutex, portMAX_DELAY);
        xSemaphoreTakeRecursive(xRecMutex, portMAX_DELAY); // would deadlock with normal mutex!
            do_work();
        xSemaphoreGiveRecursive(xRecMutex);
    xSemaphoreGiveRecursive(xRecMutex);
}

When to use: When the same task may re-enter a critical section — for example, a library function that takes a mutex internally, called from code that already holds the same mutex.


Q17. What is a deadlock and how do you prevent it in RTOS?

A deadlock occurs when two or more tasks each hold a resource and wait for a resource held by the other — neither can proceed.

Task A holds Mutex1, waits for Mutex2
Task B holds Mutex2, waits for Mutex1
→ Both blocked forever = Deadlock

Prevention strategies:

// 1. Always acquire mutexes in the same fixed order across all tasks
//    WRONG:
//    Task A: take(M1) then take(M2)
//    Task B: take(M2) then take(M1)  ← different order = deadlock risk

//    CORRECT: both tasks always take M1 first, then M2

// 2. Use timeout instead of portMAX_DELAY
if (xSemaphoreTake(xMutex, pdMS_TO_TICKS(200)) == pdFALSE) {
    // Could not acquire — handle gracefully, log, retry
    log_error("Mutex timeout — possible deadlock");
    return ERROR;
}

// 3. Never hold a mutex while waiting for another blocking operation

Q18. How do you use mutexes safely from an ISR in FreeRTOS?

You cannot use a mutex from an ISR in FreeRTOS. Mutexes have priority inheritance which requires task context. ISRs are not tasks.

From an ISR, you must use:

  • xSemaphoreGiveFromISR() — for binary/counting semaphores only
  • xQueueSendFromISR() — for queues
  • xTaskNotifyFromISR() — task notifications (fastest, lowest overhead)
// WRONG — never do this from an ISR:
// xSemaphoreTake(xMutex, portMAX_DELAY);  // CRASH

// CORRECT — use task notification from ISR:
void TIMER_IRQHandler(void) {
    BaseType_t xWoken = pdFALSE;
    vTaskNotifyGiveFromISR(xTaskHandle, &xWoken);
    portYIELD_FROM_ISR(xWoken);
}

void vTask(void *p) {
    for (;;) {
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);  // wait for ISR signal
        process_data();
    }
}

Section 4 — Queues & Communication


Q19. What is a FreeRTOS queue and how does it work?

A queue is a thread-safe FIFO buffer for passing data between tasks or from ISR to task. It copies data by value — the sender's copy is independent of the receiver's.

// Create queue — holds 10 items of type uint32_t
QueueHandle_t xQueue = xQueueCreate(10, sizeof(uint32_t));

// Sender task
void vSender(void *p) {
    uint32_t value = 42;
    xQueueSend(xQueue, &value, pdMS_TO_TICKS(100));  // blocks 100ms if full
}

// Receiver task
void vReceiver(void *p) {
    uint32_t rxVal;
    for (;;) {
        if (xQueueReceive(xQueue, &rxVal, portMAX_DELAY) == pdTRUE) {
            printf("Received: %lu\n", rxVal);
        }
    }
}

// From ISR — use ISR-safe version:
void IRQHandler(void) {
    uint32_t data = read_hw_register();
    BaseType_t xWoken = pdFALSE;
    xQueueSendFromISR(xQueue, &data, &xWoken);
    portYIELD_FROM_ISR(xWoken);
}

Q20. What are Task Notifications in FreeRTOS and why are they faster than semaphores?

Task notifications are a lightweight mechanism to send a notification value directly to a specific task — without creating a separate synchronization object.

Why faster: No separate semaphore/queue object needed. The notification value is stored directly in the Task Control Block (TCB). Lower RAM usage and faster execution than binary semaphores.

// Notify from ISR (faster than semaphore):
void IRQHandler(void) {
    BaseType_t xWoken = pdFALSE;
    vTaskNotifyGiveFromISR(xtaskHandle, &xWoken);
    portYIELD_FROM_ISR(xWoken);
}

// Wait for notification:
void vTask(void *p) {
    for (;;) {
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // clears on exit
        handle_event();
    }
}

// Can also send a value:
xTaskNotify(xHandle, 0x05, eSetBits);     // set bits in notification value
xTaskNotify(xHandle, 42,   eSetValueWithOverwrite);  // set value

Q21. What is an Event Group in FreeRTOS?

An event group holds 24 bits (on 32-bit ports), each bit representing an event flag. Multiple tasks can wait on combinations of bits — AND (all bits) or OR (any bit).

EventGroupHandle_t xEvents = xEventGroupCreate();

#define WIFI_CONNECTED   (1 << 0)
#define DATA_RECEIVED    (1 << 1)
#define SENSOR_READY     (1 << 2)

// Task waits until BOTH wifi connected AND data received:
EventBits_t bits = xEventGroupWaitBits(
    xEvents,
    WIFI_CONNECTED | DATA_RECEIVED,  // bits to wait for
    pdTRUE,   // clear on exit
    pdTRUE,   // wait for ALL bits (AND)
    portMAX_DELAY
);

// Another task sets a bit:
xEventGroupSetBits(xEvents, SENSOR_READY);

Section 5 — Memory & Timers


Q22. Explain the FreeRTOS heap models (heap_1 to heap_5)

FreeRTOS provides 5 memory allocation schemes:

SchemeDescriptionUse Case
heap_1Allocates only, never freesSimplest, safety-critical systems
heap_2Frees memory, no coalescenceLegacy, can fragment
heap_3Wraps standard malloc/freeWhen OS malloc is available
heap_4Frees + coalesces adjacent blocksMost commonly used
heap_5heap_4 across multiple non-contiguous memory regionsMultiple RAM banks
// Check free heap at runtime:
size_t freeHeap = xPortGetFreeHeapSize();
size_t minEver  = xPortGetMinimumEverFreeHeapSize(); // watermark

// FreeRTOSConfig.h — set heap size:
#define configTOTAL_HEAP_SIZE  (16 * 1024)  // 16KB heap

Q23. How do FreeRTOS software timers work?

Software timers call a callback function after a specified period. They run in the context of the Timer Service Task (a dedicated background task) — not in an ISR, not in the calling task.

// One-shot timer — fires once
TimerHandle_t xTimer = xTimerCreate(
    "WatchdogTimer",
    pdMS_TO_TICKS(5000),  // 5 second timeout
    pdFALSE,              // one-shot (pdTRUE = periodic)
    (void*)0,             // timer ID
    vTimerCallback        // callback function
);

void vTimerCallback(TimerHandle_t xTimer) {
    // Called in Timer Service Task context — can use queues/semaphores
    // CANNOT block or delay here
    printf("Timer expired!\n");
}

xTimerStart(xTimer, 0);   // start timer
xTimerReset(xTimer, 0);   // restart/pet the watchdog
xTimerStop(xTimer, 0);    // stop timer

Section 6 — FreeRTOS vs Zephyr


Q24. What are the key differences between FreeRTOS and Zephyr RTOS?

FeatureFreeRTOSZephyr
LicenseMITApache 2.0
Footprint~5-10KB ROM~8KB+ (more features = more)
Build systemMakefile / IDECMake + west
Device driversUser-providedBuilt-in driver model
NetworkingFreeRTOS+TCP (separate)Native (BT, WiFi, Ethernet)
SMP supportLimited (v10+)Full multi-core SMP
CommunityVery large, Arduino ecosystemGrowing, Linux Foundation backed
Best forSimple MCUs, low footprintComplex SoCs, Linux-adjacent stack

Q25. How do you create and synchronize threads in Zephyr?

#include <zephyr/kernel.h>

// Define stack statically (recommended in Zephyr):
K_THREAD_STACK_DEFINE(my_stack, 1024);
struct k_thread my_thread_data;

void my_thread_fn(void *p1, void *p2, void *p3) {
    while (1) {
        printk("Thread running\n");
        k_msleep(500);   // equivalent of vTaskDelay
    }
}

// Create thread at runtime:
k_tid_t tid = k_thread_create(&my_thread_data, my_stack,
    K_THREAD_STACK_SIZEOF(my_stack),
    my_thread_fn, NULL, NULL, NULL,
    5,   // priority
    0, K_NO_WAIT);

// Zephyr Mutex:
K_MUTEX_DEFINE(my_mutex);
k_mutex_lock(&my_mutex, K_FOREVER);
    // critical section
k_mutex_unlock(&my_mutex);

// Zephyr Semaphore:
K_SEM_DEFINE(my_sem, 0, 1);  // initial=0, max=1
k_sem_give(&my_sem);          // from ISR or task
k_sem_take(&my_sem, K_FOREVER);

Q26. How does Zephyr handle ISRs differently from FreeRTOS?

In Zephyr, ISRs are registered either statically (at compile time) or dynamically. Zephyr provides ISR-safe kernel APIs with the _isr suffix pattern or context detection.

// Static ISR registration in Zephyr:
void my_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins) {
    // Run in ISR context
    k_sem_give(&my_sem);  // Zephyr detects ISR context automatically
}

// FreeRTOS requires separate FromISR() functions:
// xSemaphoreGiveFromISR() vs xSemaphoreGive()
// Zephyr handles this transparently in most cases

Section 7 — Advanced Questions


Q27. What is the difference between portMAX_DELAY and a finite timeout, and when should you use each?

// portMAX_DELAY — blocks forever until event occurs
// Use when: task has nothing else to do, MUST wait for this event
xSemaphoreTake(xMutex, portMAX_DELAY);

// Finite timeout — returns pdFALSE if timeout expires
// Use when: task should handle timeout gracefully, detect deadlocks,
//           or retry with different logic
if (xSemaphoreTake(xMutex, pdMS_TO_TICKS(500)) == pdFALSE) {
    log_warning("Mutex not acquired in 500ms — possible deadlock");
    system_recovery();
}

Best practice for production firmware: Use finite timeouts everywhere. Feed a watchdog only when all expected events arrive within their expected time. portMAX_DELAY can hide bugs.


Q28. What is configMAX_SYSCALL_INTERRUPT_PRIORITY in FreeRTOS?

FreeRTOS on ARM Cortex-M uses this to define the highest interrupt priority from which FreeRTOS API functions can be called. Interrupts with priority numerically lower (higher hardware priority) than this value must never call FreeRTOS APIs.

// FreeRTOSConfig.h
#define configMAX_SYSCALL_INTERRUPT_PRIORITY  5  // (on scale 0-15, 0=highest)

// Interrupt at priority 5, 6, 7... — CAN call xQueueSendFromISR(), etc.
// Interrupt at priority 0, 1, 2, 3, 4 — must NEVER call any FreeRTOS API
// (these are critical hardware interrupts that cannot be masked by FreeRTOS)

Common bug: Calling xQueueSendFromISR() from an interrupt with priority numerically lower than configMAX_SYSCALL_INTERRUPT_PRIORITY causes a hard fault or assertion.


Q29. How do you implement a watchdog using FreeRTOS?

// Pattern: each critical task "pets" a shared flag
// Watchdog task checks all flags periodically

#define NUM_TASKS  3
static volatile uint32_t task_alive_mask = 0;
static const uint32_t EXPECTED_MASK = 0x07;  // bits for 3 tasks

// Each task sets its bit:
void vTask1(void *p) {
    for (;;) {
        do_work_1();
        task_alive_mask |= (1 << 0);  // I'm alive
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

// Watchdog task:
void vWatchdogTask(void *p) {
    for (;;) {
        vTaskDelay(pdMS_TO_TICKS(500));  // check every 500ms
        if ((task_alive_mask & EXPECTED_MASK) != EXPECTED_MASK) {
            // One or more tasks missed their deadline
            log_error("Task failure detected — resetting");
            NVIC_SystemReset();  // hardware reset
        }
        task_alive_mask = 0;  // clear for next window
    }
}

Q30. What are the most common RTOS bugs in production and how do you debug them?

1. Stack overflow — Use configCHECK_FOR_STACK_OVERFLOW = 2 and check uxTaskGetStackHighWaterMark() regularly. Size stacks generously.

2. ISR calling non-ISR-safe API — Always use FromISR variants and pass pxHigherPriorityTaskWoken. Enable configASSERT in debug builds.

3. Forgetting to give a mutex — Use goto cleanup pattern or RAII-like wrappers. Finite timeouts catch this at runtime.

4. Missed portYIELD_FROM_ISR — The task woken by ISR won't run until the next tick if you skip this. Always call it after FromISR functions.

5. Deadlock — Always acquire mutexes in the same global order. Use finite timeouts. Log the task name and mutex name on timeout.

// Enable assertions in debug builds — catches most bugs early:
#define configASSERT(x)  if ((x) == 0) { taskDISABLE_INTERRUPTS(); \
                              printf("ASSERT failed: %s:%d\n", __FILE__, __LINE__); \
                              for(;;); }

// Runtime task stats (debug only — uses significant CPU):
#define configUSE_TRACE_FACILITY              1
#define configGENERATE_RUN_TIME_STATS         1
// Then call: vTaskList(buffer) and vTaskGetRunTimeStats(buffer)

Summary — Quick Reference

TopicKey Points
SchedulingFixed-priority preemptive, idle task at priority 0
vTaskDelay vs vTaskDelayUntilRelative delay vs absolute period (no drift)
Mutex vs Binary SemaphoreMutex = ownership + priority inheritance; Semaphore = signalling
Priority InversionHigh task blocked by low task via shared mutex
Priority InheritanceMutex temporarily boosts low task priority
DeadlockFixed acquisition order + finite timeouts prevent it
ISR SafetyUse FromISR() functions + portYIELD_FROM_ISR()
Task NotificationsFastest lightweight signalling — direct to TCB
heap_4Most common — allocate, free, coalesce adjacent blocks
Zephyr vs FreeRTOSZephyr = larger ecosystem + SMP; FreeRTOS = simpler + smaller

Want to Go Deeper?

These 30 questions cover what gets asked in 90% of embedded interviews. But understanding how to write production-quality RTOS code — structuring tasks, sizing stacks, designing ISR-to-task communication, and debugging in the field — takes hands-on practice with real hardware.

Embedded System Interview Prep Bootcamp 3.0 covers all of this with live sessions, code labs, and a focused interview preparation track.

👉 Join Bootcamp 3.0


*Published by EmbeddedShiksha | Follow for weekly embedded systems tips*

🚀 Bootcamp 4.0 — Early Bird

Preparing for Embedded System Interviews?

Join our exclusive Early Bird WhatsApp group — get first access to enrollment, special pricing, and curriculum updates for Bootcamp 4.0 before anyone else.

💬 Join Bootcamp 4.0 Early Bird Group

✅ Free to join  ·  224+ engineers already inside  ·  Early bird opens April 25