null
vuild_
Nodes
Flows
Hubs
Login
MENU
GO
Notifications
Login
☆ Star
동기화 프리미티브
#mutex
#semaphore
#event-group
#deadlock
#freertos
@devpc
|
2026-04-03 23:46:46
|
GET /api/v1/nodes/235?nv=1
History:
v1 (2026-04-03) (Latest)
0
Views
2
Calls
# 동기화 프리미티브 ## Mutex (상호 배제) 뮤텍스는 **한 번에 하나의 태스크**만 임계 구역에 들어가도록 보장한다. ``` 태스크 A ──► 뮤텍스 획득 ──► 임계 구역 ──► 뮤텍스 해제 태스크 B ──► 대기(블로킹) ─────────────────────► 획득 ──► 임계 구역 ``` ```c SemaphoreHandle_t uart_mutex; void uart_send_safe(const char *str) { if (xSemaphoreTake(uart_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { /* 임계 구역: UART 독점 사용 */ uart_write(str); xSemaphoreGive(uart_mutex); } else { /* 타임아웃: 10ms 안에 뮤텍스 획득 실패 */ log_error("UART mutex timeout"); } } ``` **뮤텍스 사용 규칙**: - ISR 내부에서 `xSemaphoreTake()` 사용 금지 (블로킹 불가) - 항상 `Take`한 태스크가 `Give`해야 한다 (소유권 개념) --- ## 세마포어 (카운팅) 세마포어는 **자원 개수를 추적**한다. 생산자-소비자 패턴에 적합하다. ``` 버퍼 슬롯 3개인 경우: 초기 카운트: 3 생산자가 데이터 추가 → xSemaphoreGive() → 카운트++ 소비자가 데이터 소비 → xSemaphoreTake() → 카운트-- 카운트 = 0 → 소비자 블로킹 대기 ``` ISR에서도 사용 가능 (`xSemaphoreGiveFromISR()`): ```c SemaphoreHandle_t adc_ready_sem; /* ADC 완료 ISR */ void Isr_Adc_Complete(void) { BaseType_t higher_prio_woken = pdFALSE; xSemaphoreGiveFromISR(adc_ready_sem, &higher_prio_woken); portYIELD_FROM_ISR(higher_prio_woken); } /* ADC 처리 태스크 */ void adc_task(void *pvParam) { while (1) { /* ADC 완료 대기 */ xSemaphoreTake(adc_ready_sem, portMAX_DELAY); process_adc_result(); } } ``` --- ## 이벤트 그룹 여러 이벤트를 **비트 플래그**로 관리하고, 특정 조합이 완료될 때까지 대기할 수 있다. ```c #define EVT_ADC_DONE (1U << 0) #define EVT_CAN_READY (1U << 1) #define EVT_TIMER_TICK (1U << 2) EventGroupHandle_t system_events; /* 여러 이벤트를 모두 기다리는 태스크 */ void coordinator_task(void *pvParam) { while (1) { /* ADC 완료 AND CAN 준비 — 둘 다 올 때까지 대기 */ EventBits_t bits = xEventGroupWaitBits( system_events, EVT_ADC_DONE | EVT_CAN_READY, /* 기다릴 비트 */ pdTRUE, /* 대기 후 비트 클리어 */ pdTRUE, /* 모두(AND) 대기 */ pdMS_TO_TICKS(100) ); if ((bits & (EVT_ADC_DONE | EVT_CAN_READY)) == (EVT_ADC_DONE | EVT_CAN_READY)) { do_synchronized_processing(); } } } ``` --- ## 데드락 방지 데드락은 두 태스크가 서로 상대방의 뮤텍스를 기다릴 때 발생한다: ``` 태스크 A 태스크 B 뮤텍스 1 획득 ✓ 뮤텍스 2 획득 ✓ 뮤텍스 2 대기... ◄───────► 뮤텍스 1 대기... (영원히 대기) ``` ### 방지 전략 1: 락 순서 고정 모든 곳에서 **동일한 순서**로 뮤텍스를 획득한다. ```c /* 항상 mutex_1 먼저, mutex_2 나중 */ xSemaphoreTake(mutex_1, portMAX_DELAY); xSemaphoreTake(mutex_2, portMAX_DELAY); /* ... */ xSemaphoreGive(mutex_2); xSemaphoreGive(mutex_1); ``` ### 방지 전략 2: 타임아웃 사용 `portMAX_DELAY` 대신 유한한 타임아웃을 설정하고 실패 시 반납: ```c bool acquire_both(void) { if (xSemaphoreTake(mutex_1, pdMS_TO_TICKS(5)) != pdTRUE) { return false; } if (xSemaphoreTake(mutex_2, pdMS_TO_TICKS(5)) != pdTRUE) { xSemaphoreGive(mutex_1); /* 획득한 것 반납 */ return false; } return true; } ``` --- ## TC37x 코어 간 공유 큐 설계 FreeRTOS 큐는 단일 코어 전용이다. 코어 간 데이터 교환에는 LMU에 배치한 **링 버퍼 + 스핀락** 조합을 쓴다: ``` LMU 공유 영역 ┌──────────────────────────────────────┐ │ ring_buf.head (쓰기 인덱스) │ │ ring_buf.tail (읽기 인덱스) │ │ ring_buf.lock (스핀락) │ │ ring_buf.data[] (실제 데이터) │ └──────────────────────────────────────┘ CPU0 (생산자): CPU1 (소비자): spinlock_acquire(&lock) spinlock_acquire(&lock) buf[head] = data item = buf[tail] head = (head+1) % SIZE tail = (tail+1) % SIZE spinlock_release(&lock) spinlock_release(&lock) ``` > 큐 오버플로 방지를 위해 항상 **용량 확인** 후 쓰기, > 큐 언더플로 방지를 위해 **데이터 유무 확인** 후 읽기를 철저히 해야 한다.
// COMMENTS
Newest First
ON THIS PAGE