# 사례 연구: Xenomai를 이용한 분산 시스템

### 개요

이 사례 연구에서는 Xenomai를 활용한 실제 분산 시스템 구축 사례를 통해 기본 개념, 구성 요소, 구현 방법 및 성능 평가를 다룬다. 구체적으로, 시스템 아키텍처, 네트워크 설정, 실시간 태스크 스케줄링, 그리고 데이터 동기화와 관련된 주요 문제를 어떻게 해결하였는지 살펴본다.

### 시스템 아키텍처

#### 전체 구조

이 시스템은 여러 개의 실시간 노드로 구성되며, 각 노드는 Xenomai 커널을 실행하여 실시간 태스크들을 관리한다. 이 노드들은 네트워크를 통해 서로 통신하며, 데이터를 교환하고 연산을 분담한다.

진행되는 예제 시스템은 다음과 같은 구성 요소로 이루어져 있다:

* **노드(Node)**: Xenomai가 설치된 기기. 각 노드는 하나 이상의 실시간 태스크를 실행.
* **통신 서버(Communication Server)**: 노드들 간의 데이터 교환을 조율.
* **클라이언트 애플리케이션(Client Application)**: 시스템의 상태를 모니터링하거나 제어.

#### 네트워크 설정

네트워크는 주로 TCP/IP를 사용하여 구성된다. 통신 서버는 각 노드와의 연결을 유지하며 데이터를 효율적으로 전송한다. 네트워크 지연을 최소화하기 위해 네트워크의 대역폭과 라우팅 경로를 최적화한다.

**주요 네트워크 설정**

1. 고정 IP 할당: 각 노드는 고정 IP를 할당받아 통신의 안정성을 높인다.
2. QoS (Quality of Service) 설정: 네트워크 트래픽 우선순위를 설정하여 실시간 데이터가 우선적으로 전송될 수 있도록 한다.
3. 주기적 데이터 동기화: 주기적으로 데이터를 동기화하여 각 노드 간의 상태가 일치하도록 한다.

### 실시간 태스크 스케줄링

#### 태스크 생성 및 관리

Xenomai는 실시간 태스크(RT-태스크)를 생성하고 관리하는 데 있어 우수한 성능을 제공한다. 실시간 태스크는 주로 다음과 같은 작업들을 수행한다:

* 센서 데이터 수집
* 데이터 처리 및 분석
* 액추에이터 구동

**태스크 예제**

다음은 RT-태스크를 생성하고 실행하는 간단한 예제 코드이다:

```c
#include <native/task.h>
#include <native/timer.h>

void rt_task(void *arg) {
    RT_TASK_INFO info;

    rt_task_inquire(NULL, &info);
    while(1) {
        // 실시간 태스크 작업 수행
        rt_printf("Task %s is running\n", info.name);
        rt_task_sleep(1000000); // 1 ms 휴식
    }
}

int main() {
    RT_TASK task;
    rt_task_create(&task, "MyTask", 0, 50, 0);
    rt_task_start(&task, &rt_task, NULL);

    pause();
    return 0;
}
```

위 예제에서는 `rt_task_create` 함수를 사용하여 태스크를 생성하고, `rt_task_start`를 통해 실행을 시작한다. 태스크는 주기적으로 `rt_task_sleep` 함수를 호출하여 지정된 시간 동안 대기한다.

#### 태스크 스케줄링

Xenomai는 다양한 스케줄링 정책을 제공한다. 예를 들어, 다음과 같은 정책을 사용할 수 있다:

* **FIFO:** 태스크가 생성된 순서대로 실행
* **RR (Round Robin):** 각 태스크가 동일한 시간 할당을 받아 순환적으로 실행
* **EDF (Earliest Deadline First):** 데드라인이 가장 가까운 태스크를 우선 실행

다음은 FIFO 스케줄링을 사용하는 예제이다:

```c
int main() {
    RT_TASK task1, task2;
    rt_task_create(&task1, "Task1", 0, 50, T_JOINABLE | T_FPU | T_CPU(0));
    rt_task_create(&task2, "Task2", 0, 50, T_JOINABLE | T_FPU | T_CPU(0));

    rt_task_set_mode(0, T_WARNSW, NULL); // 스케줄링 경고를 접수
    rt_task_start(&task1, &rt_task, NULL);
    rt_task_start(&task2, &rt_task, NULL);

    pause();
    
    rt_task_join(&task1);
    rt_task_join(&task2);
    return 0;
}
```

### 데이터 동기화

실시간 분산 시스템에서 데이터의 일관성을 유지하는 것은 매우 중요하다. 이를 위해 다양한 데이터 동기화 기법이 사용된다.

#### 공유 메모리 사용

Xenomai는 서로 다른 태스크 간에 데이터를 공유하기 위해 공유 메모리를 사용할 수 있다.

```c
#include <native/mem.h>

int main() {
    RT_HEAP heap;
    void *shared_data;
    rt_heap_create(&heap, "MyHeap", 1024, H_SINGLE | H_SHARED);

    rt_heap_alloc(&heap, 1024, TM_INFINITE, &shared_data);

    // 데이터 접근 및 수정
    strcpy((char*)shared_data, "Hello, Xenomai!");

    rt_heap_free(&heap, shared_data);
    rt_heap_delete(&heap);
    return 0;
}
```

위 예제에서는 `rt_heap_create` 및 `rt_heap_alloc` 함수를 사용하여 공유 메모리를 생성하고 할당한다.

#### 네트워크를 통한 데이터 동기화

네트워크를 통해 데이터를 실시간으로 동기화하는 것도 매우 중요하다. 이를 위해 주로 TCP/IP 또는 UDP 프로토콜을 사용한다. TCP는 데이터 전송의 신뢰성을 보장하며, UDP는 낮은 지연과 높은 속도를 제공한다.

**예제: TCP를 이용한 데이터 전송**

다음은 TCP 소켓을 사용하여 데이터를 전송하는 간단한 예제이다:

서버측 코드:

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};

    // 소켓 파일 서술자 생성
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 소켓 옵션 설정
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);

    // 소켓에 바인드
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 들어오는 연결을 대기
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    // 새로운 연결 수락
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    // 데이터 수신
    read(new_socket, buffer, 1024);
    printf("Received: %s\n", buffer);

    // 데이터 전송
    char *hello = "Hello from server";
    send(new_socket, hello, strlen(hello), 0);
    printf("Hello message sent\n");

    return 0;
}
```

클라이언트측 코드:

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
    struct sockaddr_in address;
    int sock = 0;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    char buffer[1024] = {0};

    // 소켓 파일 서술자 생성
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(8080);

    // IPv4 주소 변환
    if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    // 서버에 연결
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }
    
    // 데이터 전송
    send(sock, hello, strlen(hello), 0);
    printf("Hello message sent\n");

    // 데이터 수신
    read(sock, buffer, 1024);
    printf("Received: %s\n", buffer);

    return 0;
}
```

### 성능 최적화

#### 태스크 우선순위 조정

실시간 시스템에서 태스크의 우선순위를 조정하여 중요한 작업이 지연 없이 실행될 수 있도록 한다. Xenomai는 우선순위 기반 스케줄링을 지원하므로, 주요 태스크에 높은 우선순위를 할당할 수 있다.

**예제: 태스크 우선순위 설정**

```c
int main() {
    RT_TASK task_high, task_low;

    // 높은 우선순위 태스크 생성
    rt_task_create(&task_high, "HighPriorityTask", 0, 80, 0);
    rt_task_start(&task_high, &rt_task, NULL);

    // 낮은 우선순위 태스크 생성
    rt_task_create(&task_low, "LowPriorityTask", 0, 20, 0);
    rt_task_start(&task_low, &rt_task, NULL);

    pause();
    return 0;
}
```

#### 시스템 모니터링

시스템의 성능을 모니터링하여 병목 현상을 파악하고 최적화할 수 있다. Xenomai는 다양한 모니터링 도구와 인터페이스를 제공한다.

**예제: 시스템 모니터링**

```c
#include <native/timer.h>
#include <native/task.h>
#include <native/sem.h>
#include <rtdk.h>

void monitor_task() {
    RT_TASK_INFO info;
    while(1) {
        rt_task_inquire(NULL, &info);
        rt_printf("Task: %s, Priority: %d, State: %d\n", info.name, info.cprio, info.status);
        rt_task_sleep(1000000); // 1ms 주기
    }
}

int main() {
    RT_TASK mon_task;
    rt_task_create(&mon_task, "MonitorTask", 0, 99, 0);
    rt_task_start(&mon_task, &monitor_task, NULL);
    
    pause();
    return 0;
}
```

위 예제에서는 `rt_task_inquire` 함수를 사용하여 현재 실행 중인 태스크의 정보를 출력한다. 이 정보를 통해 시스템의 상태를 파악하고 성능을 최적화할 수 있다.

***

이 사례 연구에서는 Xenomai를 이용한 분산 시스템의 구축, 관리, 최적화 방법을 자세히 다뤘다. Xenomai는 고성능 실시간 응용 프로그램을 개발하는 데 신뢰할 수 있는 프레임워크를 제공하며, 다양한 기능과 도구를 활용하여 시스템의 효율성을 극대화할 수 있다. 실제 구현 사례를 통해 이러한 개념들을 실무에 적용할 수 있는 방법을 배웠다.
