RTOS Seed

Vì sao ISR không được dùng Mutex trong FreeRTOS?

Giải thích vì sao ISR không được block và vì sao Mutex thường không phù hợp trong interrupt context của FreeRTOS/CMSIS-RTOS.

3 phút đọc
RTOS cover

Ghi chú nhanh

ISR không nên dùng Mutex thường vì Mutex sinh ra cho task context, nơi một task có thể chờ, nhường CPU, rồi được scheduler đánh thức lại sau.

ISR thì khác. ISR phải chạy nhanh, dứt khoát, không đứng chờ ai cả.

Task context → có thể block/chờ Mutex
ISR context  → không được block

Nếu ISR mà cũng ngồi chờ Mutex thì giống như đang xử lý cháy nhà nhưng lại xếp hàng lấy số thứ tự. Firmware real-time không thích kiểu lịch sự đó.

Khi nào bạn sẽ đụng lỗi này?

Lỗi này thường không xuất hiện lúc code còn đơn giản. Nó hay xuất hiện khi bạn bắt đầu “tiện tay” dùng chung một API cho cả task và callback interrupt.

Ví dụ:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    Debug_Printf("RX done
"); // nguy hiểm nếu Debug_Printf dùng mutex
}

Nếu Debug_Printf() bên trong có lock Mutex, cấp phát động, hoặc chờ UART rảnh, callback này có thể làm hệ thống bị treo rất khó đoán.

Vì sao ISR không được block?

ISR chạy ở interrupt context. Trong lúc ISR đang chạy, nhiều cơ chế bình thường của task không hoạt động theo cách bạn nghĩ.

Một ISR dài hoặc bị kẹt có thể gây ra:

  • mất interrupt khác;
  • tăng interrupt latency;
  • task quan trọng bị trễ;
  • watchdog reset;
  • bug chỉ xảy ra “thỉnh thoảng”, rất khó tái hiện.

Đây là kiểu bug mà log càng thêm nhiều thì nó càng biến mất hoặc càng nặng hơn. Debug rất vui, theo nghĩa không vui chút nào.

Cách xử lý đúng hơn

Thay vì dùng một API cho mọi nơi, nên tách rõ:

Task API → có thể dùng mutex, queue thường, timeout
ISR API  → dùng API FromISR hoặc chỉ set flag / ghi buffer ngắn

Ví dụ hướng thiết kế:

void Debug_LogFromTask(const char *msg);
void Debug_LogFromISR(const char *msg);

Trong ISR, hãy làm ít nhất có thể:

void EXTI_Callback(void)
{
    BaseType_t higher_woken = pdFALSE;
    xQueueSendFromISR(log_queue, &event, &higher_woken);
    portYIELD_FROM_ISR(higher_woken);
}

Checklist khi review code

Cách nhớ

Task được phép chờ.
ISR chỉ nên báo hiệu rồi đi ra.

Bài liên quan nên đọc tiếp

Thấy nội dung này hữu ích?

Lưu lại hoặc chia sẻ cho người cũng đang học firmware, BIOS/UEFI và embedded systems.

Nội dung liên quan

Một số bài viết, ghi chú hoặc project có liên quan đến nội dung bạn vừa đọc.

Biến note thành bài viết hoàn chỉnh

Notes là nơi ghi nhanh khái niệm.