개요
실시간 시스템에서 서로 다른 태스크 간의 데이터 교환은 매우 중요하다. Xenomai는 실시간 통신을 위한 여러 가지 메커니즘을 제공하며, 그 중 하나가 메일박스(mailbox)이다. 메일박스는 메시지 큐(queue)의 한 형태로, 송신자가 데이터를 전송하면 수신자가 이를 받아 처리할 수 있는 방식으로 동작한다.
메일박스의 기본 개념
메일박스는 실시간 통신을 위해 설계된 데이터 구조로, 메시지를 송수신하는 데 사용된다. 각 메일박스는 특정 크기의 버퍼를 가지며, 메시지를 저장하고 이를 대기 중인 수신자에게 전달한다.
메일박스의 구성 요소
- 메시지 버퍼: 송신된 메시지를 저장하는 버퍼.
- 송신 및 수신 태스크: 데이터를 송신하는 태스크와 이를 수신하는 태스크가 있다.
- 동기화 메커니즘: 송수신 간의 데이터 동기화를 위한 메커니즘.
메일박스 생성 및 초기화
먼저, 메일박스를 생성하고 초기화하는 함수들에 대해 알아본다.
#include <rtdm/rtipc.h>
#include <native/mbox.h>
int rt_mbx_create(RT_QUEUE *mbx, const char *name, size_t size);
rt_mbx_create
함수는 새로운 메일박스를 생성한다. 여기서 mbx
는 메일박스를 참조하는 핸들, name
은 메일박스의 이름, size
는 메일박스의 크기를 나타낸다.
예시:
RT_QUEUE mbx;
rt_mbx_create(&mbx, "Mailbox1", 256);
메일박스 삭제
메일박스를 더 이상 사용하지 않을 때, 이를 삭제해야 메모리 누수를 방지할 수 있다.
int rt_mbx_delete(RT_QUEUE *mbx);
예시:
rt_mbx_delete(&mbx);
데이터 송신
메일박스를 통해 데이터를 송신하는 함수는 다음과 같다.
ssize_t rt_mbx_send(RT_QUEUE *mbx, const void *msg, size_t size);
rt_mbx_send
함수는 특정 메일박스에 데이터를 송신한다. msg
는 송신할 데이터, size
는 데이터의 크기이다.
예시:
char message[] = "Hello, World!";
rt_mbx_send(&mbx, message, sizeof(message));
데이터 수신
메일박스로부터 데이터를 수신하는 함수들은 다음과 같다.
ssize_t rt_mbx_receive(RT_QUEUE *mbx, void *msg, size_t size);
ssize_t rt_mbx_receive_timed(RT_QUEUE *mbx, void *msg, size_t size, const RTIME *timeout);
rt_mbx_receive
함수는 메일박스로부터 데이터를 수신 대기하고, 데이터를 받으면 이를 msg
에 저장한다. rt_mbx_receive_timed
함수는 타임아웃을 지정하여 수신 대기할 수 있다.
예시:
char buffer[256];
rt_mbx_receive(&mbx, buffer, sizeof(buffer));
RTIME timeout = TM_INFINITE; // 무한대기
rt_mbx_receive_timed(&mbx, buffer, sizeof(buffer), &timeout);
동기화 및 오류 처리
메일박스를 사용하면서 동기화와 오류 처리는 매우 중요한 요소이다. 송수신 태스크 간의 동기화 문제를 해결하고, 예상치 못한 상황에서의 오류 처리를 적절히 수행하여 시스템의 신뢰성을 높이는 것이 필요하다.
동기화
실시간 시스템에서는 태스크들이 데이터 전송 시, 동기화 문제를 겪을 수 있다. 메일박스는 이 동기화를 지원하기 위해 다양한 동기화 메커니즘을 제공한다. 예를 들어, rt_mbx_send
함수는 메일박스가 가득 찬 경우, 데이터를 전송하려는 태스크를 블록(block) 상태로 만든다. 반대로 rt_mbx_receive
함수는 메일박스가 비어 있는 경우, 데이터를 기다리는 태스크를 블록 상태로 만든다. 이를 통해 송수신 태스크 간의 동기화가 보장된다.
오류 처리
메일박스를 사용할 때 발생할 수 있는 주요 오류들은 다음과 같다.
- 메일박스 생성 실패: 메일박스 생성 시 사용 가능한 메모리가 부족한 경우 발생.
- 송신 실패: 메일박스가 가득 찬 경우 발생.
- 수신 실패: 메일박스가 비어 있는 경우 발생.
- 삭제 실패: 삭제하려는 메일박스가 유효하지 않은 경우 발생.
이와 같은 오류 상황을 체크하고, 적절한 예외 처리를 통해 시스템의 신뢰성을 확보해야 한다.
예시:
if (rt_mbx_create(&mbx, "Mailbox1", 256) != 0) {
// 오류 처리: 메일박스 생성 실패
}
if (rt_mbx_send(&mbx, message, sizeof(message)) < 0) {
// 오류 처리: 데이터 송신 실패
}
if (rt_mbx_receive(&mbx, buffer, sizeof(buffer)) < 0) {
// 오류 처리: 데이터 수신 실패
}
if (rt_mbx_delete(&mbx) != 0) {
// 오류 처리: 메일박스 삭제 실패
}
샘플 코드
다음은 메일박스를 사용하여 데이터를 송수신하는 간단한 샘플 코드이다.
송신 태스크:
void sender_func(void *arg) {
RT_QUEUE *mbx = (RT_QUEUE *)arg;
char message[] = "Hello, Xenomai!";
while (1) {
if (rt_mbx_send(mbx, message, sizeof(message)) < 0) {
printf("Error sending message\n");
}
rt_task_sleep(1000000000); // 1초 대기
}
}
수신 태스크:
void receiver_func(void *arg) {
RT_QUEUE *mbx = (RT_QUEUE *)arg;
char buffer[256];
while (1) {
if (rt_mbx_receive(mbx, buffer, sizeof(buffer)) < 0) {
printf("Error receiving message\n");
} else {
printf("Received message: %s\n", buffer);
}
rt_task_sleep(500000000); // 0.5초 대기
}
}
메인 함수:
int main() {
RT_QUEUE mbx;
RT_TASK sender, receiver;
// 메일박스 생성
if (rt_mbx_create(&mbx, "Mailbox1", 256) != 0) {
printf("Failed to create mailbox\n");
return -1;
}
// 태스크 생성
rt_task_create(&sender, "Sender", 0, 50, 0);
rt_task_create(&receiver, "Receiver", 0, 50, 0);
// 태스크 시작
rt_task_start(&sender, &sender_func, &mbx);
rt_task_start(&receiver, &receiver_func, &mbx);
// 메인 함수 대기
pause();
// 태스크 종료 및 메일박스 삭제
rt_task_delete(&sender);
rt_task_delete(&receiver);
rt_mbx_delete(&mbx);
return 0;
}
이 샘플 코드는 간단한 송수신 메커니즘을 구현하여, sender 태스크가 주기적으로 메시지를 메일박스로 송신하고, receiver 태스크가 이를 수신하여 출력하는 예제이다.
Xenomai의 메일박스는 실시간 시스템에서 데이터를 효율적으로 송수신할 수 있는 강력한 메커니즘을 제공한다. 이를 통해 실시간 통신이 요구되는 다양한 애플리케이션에서 안정적이고 동기화된 데이터 전송을 구현할 수 있다. 다양한 동기화 메커니즘과 함께 오류 처리 방안을 고려하여, 시스템의 신뢰성을 높이는 것이 중요하다.