Install Xenomai3 on Ubuntu 22.04LTS

Xenomai는 기존 Linux 시스템의 커널과 병행하여 실행되며, Hard Realtime Task를 지원하는 소프트웨어 프레임웍입니다. 설치 방법도 간단하고, POSIX 인터페이스를 지원하여 개발도 쉽게 할 수 있습니다.

인터넷에서 Xenomai 설치 방법을 검색해보면, Xenomai2 버전과 예전 Linux 버전을 기준으로 설명해놓는 것들이 많고 (그만큼 인기가 없다는 얘기…) Xenomai3라고 해도 예전 iPipe를 사용하는 방법이 위주라서, 가장 최신 버전으로 Xenomai를 설치하는 방법을 정리해보았습니다.

물론 Xenoami도 Xenomai4 버전이 출시되어있긴 하지만, 아직까진 성능 검증 및 개발 방법에 대한 친절한 문서를 찾아보기가 힘들었습니다. 이 포스트는 Xenoami3, Ubuntu 22.04 버전을 기준으로 설명합니니다.

먼저 커널을 빌드하기 위한 툴을 설치합니다.

$ sudo apt install git build-essential libncurses-dev flex bison libelf-dev libssl-dev devscripts debhelper

다음으로 작업할 임시 디렉토리를 하나 만들고, 그곳에 커널 및 Xenomai 라이브러리 소스를 받아옵니다.

$ mkdir ~/tmp
$ cd ~/tmp
$ wget https://source.denx.de/Xenomai/linux-dovetail/-/archive/v6.1.61-dovetail1/linux-dovetail-v6.1.61-dovetail1.tar.gz
$ wget https://source.denx.de/Xenomai/xenomai/-/archive/v3.2.4/xenomai-v3.2.4.tar.gz


$ tar zxf linux-dovetail-v6.1.61-dovetail1.tar.gz
$ tar zxf xenomai-v3.2.4.tar.gz

커널 버전은 자유롭게 선택이 가능합니다. 사용하는 디바이스 드라이버 및 소프트웨어 개발환경에 따라 적절히 선택해주면 됩니다. 저는 6.1.61 버전을 선택하였습니다. 이전에는 ipipe 패치를 해줘야 했지만, ipipe는 개발이 종료되었고, dovetail이 그 기능을 이어받아 개발 중입니다. 위 Repository에서는 dovetail이 패치된 커널 소스를 제공하고 있습니다. Xenomai 라이브러리는 가장 최신 버전인 3.2.4를 사용합니다.

Xenomai는 두 가지 모드로 실행이 가능한데, Cobalt는 기존 커널과 Xenomai 커널을 동시에 존재하고, 두 개의 커널 사이의 통신을 위해 dovetail을 이용합니다.

커널을 패치하기 위해 패치를 생성하고, 커널 소스에 적용해 줍니다.

$ cd ~/tmp/xenomai-v3.2.4/scripts
$ ./prepare-kernel.sh --linux=/home/robot/tmp/linux-dovetail-v6.1.61-dovetail1 --arch=x86_64 --outpatch=/home/robot/tmp/patch-cobalt-3.2.4.patch

$ cd ~/tmp/linux-dovetail-v6.1.61-dovetail1
$ patch -p1 < ../patch-cobalt-3.2.4.patch

이제 커널을 설정해줍니다. make menuconfig 를 이용하겠습니다. 현재 설치된 Ubuntu의 커널 설정을 복사하여 기본으로 사용합니다.

$ cd ~/tmp/linux-dovetail-v6.1.61-dovetail1
$ cp /boot/config-`uname -r` .config
$ make menuconfig

몇가지 필수 설정들이 필요하고, 그 외엔 사용하는 환경에 따라 커널 모듈 및 설정을 추가/삭제하면 됩니다.

General setup  --->
	Preemption Model (Preemptible Kernel --->
		(X) Preemptible Kernel (Low-Latency Desktop)

Processor type and features  --->
	Processor family (Generic-x86-64)  --->
		(X) Core 2/newer Xeon

	[*] Multi-core scheduler support
	[ ]   CPU core priorities scheduler support

Power management and ACPI options  --->
	CPU Frequency scaling  --->
		[ ] CPU Frequency scaling

	[*] ACPI (Advanced Configuration and Power Interface) Support  --->
		< >   Processor

	CPU Idle  --->
		[ ] CPU idle PM support

Memory Management options  --->
	[ ] Transparent Hugepage Support
	[ ] Allow for memory compaction
	[ ] Page migration
  [ ] Contiguous Memory Allocator


[*] Xenomai/cobalt (NEW)  --->
	Drivers  --->
		RTnet  --->
			<M> RTnet, TCP/IP socket interface
				Drivers  --->

    Real-time IPC drivers  --->
      <M> RTIPC protocol family
        [*]   XDDP cross-domain datagram protocol (NEW)
        [*]   IDDP intra-domain datagram protocol (NEW)
        (32)    Number of IDDP communication ports (NEW)
        [*]   Buffer protocol (NEW)
        (32)    Number of BUFP communication ports (NEW)

저장하고, 다시 터미널로 돌아옵니다.

빌드하기 전에 시스템 키 설 정 및 디버깅코드 생성을 중지하도록 설정해줍니다. 시스템 키 설정은 따로 키를 생성하지 않는 한 에러가 발생하고, 디버깅 코드의 생성 중지는 빌드를 조금 더 빠르게 해줍니다.

$ scripts/config --disable SYSTEM_TRUSTED_KEYS
$ scripts/config --set-str CONFIG_SYSTEM_TRUSTED_KEYS ""
$ scripts/config --disable SYSTEM_REVOCATION_KEYS
$ scripts/config --set-str CONFIG_SYSTEM_REVOCATION_KEYS ""
$ scripts/config --disable CONFIG_DEBUG_INFO_BTF

$ scripts/config --disable GDB_SCRIPTS
$ scripts/config --disable DEBUG_INFO
$ scripts/config --disable DEBUG_INFO_SPLIT
$ scripts/config --disable DEBUG_INFO_REDUCED
$ scripts/config --disable DEBUG_INFO_COMPRESSED
$ scripts/config --set-val DEBUG_INFO_NONE y
$ scripts/config --set-val DEBUG_INFO_DWARF5 n
$ scripts/config --disable DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT

이제 빌드를 진행해 줍니다. (아주~~~ 좋은 PC가 아니면 대충 1시간 살짝 넘게 걸리는 듯 합니다. )

$ make -j4 bindeb-pkg

빌드가 완료되면, 생성된 deb 파일을 이용하여 커널을 설치해줍니다.

$ cd ..
$ sudo dpkg -i *.deb

설치를 완료한 이후, 몇가지 설정을 진행해 줍니다.

일단 xenomai 관련한 기능을 sudo 명령없이 사용하도록 현재 유저를 xenomai 그룹에 추가해줍니다.

$ sudo addgroup xenomai
$ sudo addgroup root xenomai
$ sudo usermod -aG xenomai $(whoami)

Group ID (GID)를 찾아봅니다.

$ cat /etc/group | grep xenomai
1001

다음으로 GRUB의 부팅 옵션을 추가해줍니다.

$ sudo vi /etc/default/grub
GRUB_DEFAULT=saved
GRUB_SAVEDEFAULT=true
#GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=10
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash nomodeset i915.enable_rc6=0 i915.enable_dc=0 noapic xenomai.allowed_group=1001 isolcpus=0,1 xenomai.supported_cpus=0x3"
GRUB_CMDLINE_LINUX=""

GRUB_GFXMODE=1280x1024
GRUB_GFXPAYLOAD_LINUX=1280x1024

옵션 중 몇 가지를 설명해보면,

xenomai.allowed_group=1001     % 생성한 그룹의 사용자들이 xenomai를 사용할 수 있도록 권한 부여
isolcpus=0,1                   % CPU0, CPU1을 일반 커널에서 사용하지 못하도록 Isolation
xenomai.supported_cpus=0x3     % CPU0, CPU1을 Xenomai에서 사용하도록 설정

update-grub를 실행하여 변경된 옵션을 적용합니다.

$ sudo update-grub
$ sudo reboot

다음으로 XDDP (비실시간 커널과 실시간 커널과의 통신을 위한 IPC 방법 중 하나)를 사용하기 위해 퍼미션을 설정해줍니다. udev 룰을 이용하여 권한을 부여할 수 있습니다.

$ sudo vi /etc/udev/rules.d/99-xenomai.rules

KERNEL=="rtp[0-32]*", MODE="0666"
SUBSYSTEM=="rtdm", MODE="0666"

다음으로 XDDP 커널 모듈을 부팅 시에 자동으로 로딩하도록 설정합니다.

$ sudo vi /etc/modules

xeno_rtipc

이제 재부팅해줍니다. 재부팅시 설치된 실시간 커널 버전을 선택해줍니다.


커널 버전 확인

$ uname -a
Linux former-0030 6.1.61-xenomai #5 SMP PREEMPT_DYNAMIC IRQ_PIPELINE Fri Nov 17 16:46:35 KST 2023 x86_64 x86_64 x86_64 GNU/Linux

XDDP 커널 모듈 로딩 확인

$ lsmod | grep xeno
xeno_rtipc

Xenomai 라이브러리를 설치해줍니다. 먼저 빌드를 위한 패키지 필요 패키지를 설치합니다.

$ sudo apt install fuse

Xenomai 라이브러리를 빌드하고 설치합니다.

$ cd ~/tmp/xenomai-v3.2.4
$ ./scripts/bootstrap
$ ./configure --with-pic --with-core=cobalt --enable-smp --disable-tls --enable-dlopen-libs --disable-clock-monotonic-raw
$ make -j4
$ sudo make install

PATH 환경변수에 Xenomai bin 경로를 추가하고,

$ echo "export PATH=/usr/xenomai/bin:$PATH" >> ~/.bashrc

라이브러리를 추가해줍니다.

$ sudo vi /etc/ld.so.conf.d/xenomai.conf

/usr/xenomai/lib

$ sudo ldconfig

설치 완료


테스트용 코드를 하나 만들어서 잘 동작되는지 확인해보겠습니다.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <alchemy/task.h>

RT_TASK demo_task;

void demo(void *arg)
{
  RT_TASK_INFO curtaskinfo;
  rt_task_inquire(&demo_task, &curtaskinfo);
  printf("Task name : %s \n", curtaskinfo.name);
}

int main(int argc, char* argv[])
{
  char *str = "demo";

  printf("start task\n");

  rt_task_create(&demo_task, str, 0, 50, 0);
  rt_task_start(&demo_task, &demo, 0);

  sleep(1);
} 
$ gcc -o hello_xenomai hello_xenomai.c $(xeno-config --posix --alchemy --cflags) $(xeno-config --posix --alchemy --ldflags)
$ ./hello_xenomai 
start task
Task name : demo

Latency도 확인해봅니다.

$ sudo sh -c "echo 0 > /proc/xenomai/latency"
$ latency
== Sampling period: 100 us
== Test mode: periodic user-mode task
== All results in microseconds
warming up...
RTT|  00:00:01  (periodic user-mode task, 100 us period, priority 99)
RTH|----lat min|----lat avg|----lat max|-overrun|---msw|---lat best|--lat worst
RTD|      1.919|      1.963|      3.277|       0|     0|      1.919|      3.277
RTD|      1.922|      1.964|      4.412|       0|     0|      1.919|      4.412
RTD|      1.914|      1.958|      3.316|       0|     0|      1.914|      4.412

전체적으로 100us 제어 주기에 약 4us latency가 발생합니다. 이는 User 모드 (dovetail을 통해서 xenomai 커널에 접근)인 경우에 그렇고, 커널 모드 or 커널 IRQ 모드로 실행하면 좀 더 좋은 성능이 나옵니다.

완료.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *