메시지 메일박스(Message Mailbox) 기능
메시지 메일박스는 태스크(Task)나 ISR(Interrupt Service Routine)에서 다른 태스크로 포인터(메시지)를 전송하기 위해 사용되는 매우 중요한 커널 오브젝트입니다.
본 포스팅에서는 메일박스의 개념부터 생성/삭제, 메시지 대기/전송, 상태 조회, 그리고 바이너리 세마포어처럼 활용하는 방법까지 차근차근 알아보겠습니다.
목차
- 메시지 메일박스란?
- 메일박스 생성: OSMboxCreate()
- 메일박스 삭제: OSMboxDel()
- 메일박스에서 메시지 대기: OSMboxPend()
- 메일박스로 메시지 보내기: OSMboxPost()
- 대기 없이 메시지 얻기: OSMboxAccept()
- 메일박스 상태 조회: OSMboxQuery()
- 메일박스를 바이너리 세마포어로 사용
- 마무리 및 참고
1. 메시지 메일박스란?
**메시지 메일박스(메일박스)**는 uC/OS-II에서 제공하는 커널 오브젝트 중 하나로,
- 포인터 변수를 전달하여 태스크 간 통신을 수행
- 전달되는 포인터는 보통 사용자가 정의한 “메시지” 구조체를 가리키는 포인터
주요 특징
- 메일박스는 한 번에 하나의 포인터만 저장 가능
- 메일박스에 포인터가 존재하면(차 있음) -> NULL이 아닌 값 보유
- 메일박스가 비어 있으면 -> NULL 포인터
- 태스크 혹은 ISR에서 메일박스에 메시지를 “보내거나(Post)”
- 메일박스에 대기 중이던 태스크가 메시지를 “받거나(Pend)” 함
대표적인 메일박스 관련 API 함수
- OSMboxCreate()
- OSMboxPend()
- OSMboxPost()
- OSMboxPostOpt()
- OSMboxAccept()
- OSMboxQuery()
아래 그림은 태스크/ISR과 메시지 메일박스의 관계를 간단히 보여줍니다.
scss
복사편집
[ Task A ] —>(Post)—+
[ Message Mailbox ]—>(Pend)– [ Task B ]
[ ISR ] —>(Post)—+
2. 메일박스 생성: OSMboxCreate()
메일박스를 생성하려면 OSMboxCreate() 함수를 사용합니다. 이때 메일박스에 들어갈 초기 포인터를 지정할 수 있습니다(주로 NULL로 초기화).
c
복사편집
OS_EVENT *OSMboxCreate (void *msg) {
OS_EVENT *pevent;
…
if (pevent != (OS_EVENT *)0) {
pevent->OSEventType = OS_EVENT_TYPE_MBOX;
pevent->OSEventCnt = 0; // 메일박스에서는 사용되지 않음
pevent->OSEventPtr = msg; // 초기 메시지 포인터(주로 NULL)
OSEventWaitListInit(pevent);
}
return (pevent);
}
- msg: 초기화하고자 하는 메시지 포인터
- 반환값: 생성한 메일박스를 가리키는 OS_EVENT * (ECB: Event Control Block)
생성 시 초기값 예시
- NULL로 생성: 이벤트 발생을 기다리는 용도
- NULL이 아닌 값: 이미 자원이 “사용 가능” 상태(바이너리 세마포어로 응용 가능)
3. 메일박스 삭제: OSMboxDel()
메일박스를 삭제할 수도 있습니다. 단, OS_MBOX_DEL_EN이 1로 설정되어 있어야 OSMboxDel() 함수를 사용할 수 있습니다.
c
복사편집
OS_EVENT *OSMboxDel (OS_EVENT *pevent, INT8U opt, INT8U *err) {
BOOLEAN tasks_waiting;
…
switch (opt) {
case OS_DEL_NO_PEND:
// 메일박스에 대기 중인 태스크가 없어야 삭제 가능
if (tasks_waiting == FALSE) {
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent->OSEventPtr = OSEventFreeList;
OSEventFreeList = pevent;
*err = OS_NO_ERR;
return ((OS_EVENT *)0); // 메일박스 삭제 완료
} else {
*err = OS_ERR_TASK_WAITING; // 대기 중인 태스크가 있어 삭제 불가
return (pevent);
}
…
}
}
- 옵션:
- OS_DEL_NO_PEND: 대기 중인 태스크가 없을 때만 삭제
- OS_DEL_ALWAYS: 강제로 삭제(대기 중인 태스크 모두 해제) – 설정 여부에 따라 달라질 수 있음
4. 메일박스에서 메시지 대기: OSMboxPend()
OSMboxPend() 함수는 지정한 메일박스에 메시지가 들어올 때까지 대기(Pend)하는 역할을 합니다.
- 이미 메시지가 있으면 -> 즉시 메시지 획득
- 메시지가 없다면 -> 태스크가 대기 상태로 전환되고, 메시지가 들어오면 깨어남
c
복사편집
void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) {
void *msg;
// 우선, 메일박스에 메시지가 있는지 확인
msg = pevent->OSEventPtr;
if (msg != (void *)0) {
pevent->OSEventPtr = (void *)0; // 메일박스에서 메시지 꺼냄
*err = OS_NO_ERR;
return (msg);
}
// 메시지가 없으므로 대기 상태로 전환
OSTCBCur->OSTCBStat |= OS_STAT_MBOX;
OSTCBCur->OSTCBDly = timeout; // 타임아웃 설정
OS_EventTaskWait(pevent);
OS_Sched(); // 다른 태스크에게 CPU 양보
// 대기 해제 후(메시지가 도착했거나 타임아웃), 다시 확인
msg = OSTCBCur->OSTCBMsg;
if (msg != (void *)0) {
OSTCBCur->OSTCBMsg = (void *)0;
OSTCBCur->OSTCBStat = OS_STAT_RDY;
*err = OS_NO_ERR;
return (msg);
}
// 타임아웃 등으로 인한 에러 처리
*err = OS_TIMEOUT;
return ( (void *)0 );
}
- timeout: 0이면 무한 대기, 0이 아니라면 지정한 틱(Tick) 수만큼 대기
- 리턴값: 메시지를 가리키는 포인터(없으면 NULL)
5. 메일박스로 메시지 보내기: OSMboxPost()
다른 태스크나 ISR에서 특정 메일박스에 메시지를 전송하려면 OSMboxPost()를 사용합니다.
c
복사편집
INT8U OSMboxPost (OS_EVENT *pevent, void *msg) {
OS_ENTER_CRITICAL();
if (pevent->OSEventGrp != 0x00) {
// 대기 중인 태스크가 있으면 즉시 꺠워서 메시지 전달
OS_EventTaskRdy(pevent, msg, OS_STAT_MBOX);
OS_EXIT_CRITICAL();
OS_Sched();
return (OS_NO_ERR);
}
// 메일박스가 이미 메시지를 보유(가득)하고 있는지 검사
if (pevent->OSEventPtr != (void *)0) {
OS_EXIT_CRITICAL();
return (OS_MBOX_FULL); // 이미 다른 메시지가 차있음
}
// 메시지 대기 태스크도 없고, 메일박스가 비어있다면 메시지 저장
pevent->OSEventPtr = msg;
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
- 메일박스에 이미 메시지가 있다면 -> OS_MBOX_FULL 에러 반환
- 메시지를 기다리는 태스크가 있다면 -> 즉시 태스크를 깨워서(Ready) 메시지 전달
6. 대기 없이 메시지 얻기: OSMboxAccept()
OSMboxAccept()는 대기(Pend) 없이 메일박스에서 메시지를 가져오는 함수입니다.
- 메일박스에 메시지가 없으면 NULL 리턴
- 메시지가 있다면 가져온 후 메일박스를 비움(NULL로 설정)
c
복사편집
void *OSMboxAccept (OS_EVENT *pevent) {
void *msg;
OS_ENTER_CRITICAL();
msg = pevent->OSEventPtr;
pevent->OSEventPtr = (void *)0; // 메시지를 꺼냈다면 메일박스 비움
OS_EXIT_CRITICAL();
return (msg); // 메시지(또는 NULL) 반환
}
7. 메일박스 상태 조회: OSMboxQuery()
**OSMboxQuery()**를 사용하면 특정 메일박스(ECB)의 현재 상태를 얻을 수 있습니다.
- 어떤 태스크들이 대기 중인지, 메일박스에 메시지가 있는지 등을 확인 가능
c
복사편집
INT8U OSMboxQuery (OS_EVENT *pevent, OS_MBOX_DATA *pdata) {
INT8U *psrc, *pdest;
…
OS_ENTER_CRITICAL();
pdata->OSEventGrp = pevent->OSEventGrp;
psrc = &pevent->OSEventTbl[0];
pdest = &pdata->OSEventTbl[0];
// 대기 테이블 복사 (크기만큼)
#if OS_EVENT_TBL_SIZE > 0
*pdest++ = *psrc++;
#endif
…
OS_EXIT_CRITICAL();
…
return (OS_NO_ERR);
}
- OS_MBOX_DATA *pdata 구조체에 메일박스 관련 정보 저장
- 실제로는 OSEventGrp, OSEventTbl[] 등을 통해 대기 중인 태스크 상태 확인
8. 메일박스를 바이너리 세마포어로 사용
메일박스에 NULL이 아닌 값(예: (void *)1)을 저장하여 바이너리 세마포어처럼 활용할 수 있습니다.
- 자원(세마포어)을 획득: OSMboxPend() (메일박스가 NULL이면 대기)
- 자원 해제: OSMboxPost() (메일박스가 비었다면 NULL -> (void *)1로 채움)
c
복사편집
OS_EVENT *MboxSem;
void Task1 (void *pdata) {
INT8U err;
for (;;) {
// 세마포어 획득과 동일하게 메일박스 대기
OSMboxPend(MboxSem, 0, &err);
// 자원 사용
…
// 자원 반납
OSMboxPost(MboxSem, (void *)1);
}
}
장점
- 메일박스와 바이너리 세마포어를 동시에 사용할 필요 없이, **하나의 오브젝트(메일박스)**로 두 가지 기능을 모두 만족
- 코드 공간 절약 및 구조 단순화
9. 마무리 및 참고
이상으로 uC/OS-II 메시지 메일박스(Message Mailbox) 기능을 살펴보았습니다.
- **OSMboxCreate()**로 생성 (초기 포인터 설정)
- 메시지 수신은 OSMboxPend()(대기) 혹은 OSMboxAccept()(대기 없이)
- 메시지 전송은 **OSMboxPost()**로 수행
- 필요 시 **OSMboxDel()**로 삭제 가능
- 메일박스를 바이너리 세마포어처럼 활용 가능
메시지 메일박스는 uC/OS-II에서 태스크 간 통신과 동기화를 간단하게 처리할 수 있는 강력한 도구입니다. 특히 포인터를 통해 자유로운 형식의 데이터를 전달할 수 있으므로, 임베디드 시스템 설계 시 유용하게 사용할 수 있습니다.
함께 보면 좋은 자료
- uC/OS-II 공식 문서
- [uC/OS-II 세마포어, 이벤트 플래그]
- uC/OS-II 메일박스
- 임베디드 RTOS 통신
- Message Mailbox
- OSMboxCreate, OSMboxPost, OSMboxPend
- 바이너리 세마포어 활용
- 임베디드 시스템 동기화