리눅스 실시간 산업 통신 — PREEMPT_RT·EtherCAT Master 운영
#한 줄 요약
“Linux로 산업 이더넷 마스터를 돌리려면 커널·CPU·IRQ·NIC 네 곳 모두에서 결정성을 짜야 합니다.” — PREEMPT_RT 한 줄로 끝나는 일이 아닙니다. isolation·affinity·hw timestamp·드라이버 튜닝까지 전부 같이 갖춰야 100µs jitter가 나옵니다.
EtherCAT IgH 마스터, openPOWERLINK MN, OPC UA Pub/Sub publisher 같은 RT 애플리케이션을 Linux에서 돌리는 일은 이제 표준이 되었습니다. PLC 벤더가 softPLC라는 이름으로 Linux 기반 PLC를 판매하는 시대입니다(B&R APROL, CODESYS Runtime, Bosch ctrlX, Phoenix Contact PLCnext).
이 장은 어떻게 Linux를 그 수준까지 가져가는가를 4단계로 풉니다. PREEMPT_RT 활성화, CPU isolation, NIC 튜닝, EtherCAT IgH 설정. 각 단계가 어디서 결정성을 얼마나 가져오는지 측정하면서 갑니다.
#왜 vanilla Linux로는 안 되는가
표준 Linux 커널은 throughput 최적화가 목표입니다. 최악 응답 시간은 다음과 같은 곳에서 망가집니다.
| 원인 | 추가 latency |
|---|---|
| 커널 코드 안의 spinlock | 수십 µs |
| irq 처리 non-preemptible | 수십 µs |
| RCU grace period | 수십~수백 ms |
| Workqueue 지연 | 수~수십 ms |
| Page fault (swap, demand-paging) | 수ms~수초 |
| NMI (perf 등) | µs~ms |
| Power management (cpuidle, cpufreq) | µs~ms |
cyclictest로 vanilla Linux를 측정하면 최대 latency가 수 ms 단위로 튑니다. EtherCAT 1ms 사이클에서 이건 연속 사이클 miss를 만듭니다.
# vanilla 6.6 kernel, idle system$ cyclictest -p99 -t 4 -i 1000 -n -D 60T: 0 ( 1234) P:99 I:1000 C: 60000 Min: 4 Act: 8 Avg: 7 Max: 3421 ^^^^ ms 단위로 튐3.4ms의 최악 latency면 1ms 사이클은 불가능합니다.
#PREEMPT_RT — mainline에 통합
PREEMPT_RT는 가능한 많은 커널 실행 구간을 선점 가능하게 만들고, spinlock·타이머·IRQ 처리 경로를 실시간 우선순위와 함께 동작시키는 패치셋입니다. 20년에 걸친 외부 패치 끝에 2024년 Linux 6.12부터 mainline에 통합되었습니다. 모든 커널 구간이 무조건 선점 가능한 것은 아닙니다.
Linux 6.12 LTS 기준:
- Linux 6.12 LTS — PREEMPT_RT가 config option (
CONFIG_PREEMPT_RT=y). - Xenomai 3 — co-kernel 방식, legacy. Linux 5.x까지 active maintenance.
- Xenomai 4 / EVL — EVL Project로 이름 바뀜. user-level RT를 위한 새 API. 적극 개발 중.
- RTAI — 사실상 unmaintained.
선택 가이드는 단순합니다.
| 시나리오 | 추천 |
|---|---|
| 새 프로젝트, 사이클 ≥ 250µs | PREEMPT_RT (mainline) |
| 사이클 ≤ 100µs, 극단적 결정성 | EVL / Xenomai 4 |
| 기존 Xenomai 3 시스템 유지 | Xenomai 3 (단, EOL 계획 수립) |
PREEMPT_RT 활성화:
# Debian/Ubuntu (kernel 6.12+)sudo apt install linux-image-rt-amd64
# 또는 source buildmake menuconfig General setup Preemption Model -> Fully Preemptible Kernel (Real-Time)make -j && sudo make modules_install installPREEMPT_RT만 활성화한 결과:
$ uname -aLinux rt 6.12.0-rt amd64$ cyclictest -p99 -t 4 -i 1000 -n -D 60T: 0 ( 1234) P:99 I:1000 C: 60000 Min: 4 Act: 9 Avg: 8 Max: 87 ^^ µs로 떨어짐87µs로 떨어졌습니다. 좋아졌지만 EtherCAT 250µs 사이클에는 아직 빠듯합니다. 다음 단계가 CPU isolation입니다.
#CPU isolation — RT 코어를 격리
multicore 시스템에서 몇 개 코어만 RT 작업에 전용으로 두면 latency가 한 자릿수 µs까지 떨어집니다. 세 가지 옵션을 함께 씁니다.
| 부트 파라미터 | 효과 |
|---|---|
isolcpus=2,3 | 스케줄러가 이 코어에 일반 태스크를 배치하지 않음 |
nohz_full=2,3 | 이 코어의 timer tick을 끔 (CPU 휴식 시 인터럽트 0) |
rcu_nocbs=2,3 | RCU callback을 이 코어에서 호출하지 않음 |
/etc/default/grub:
GRUB_CMDLINE_LINUX_DEFAULT="quiet isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3 \ irqaffinity=0,1 mitigations=off intel_pstate=disable processor.max_cstate=1 \ idle=poll"옵션 해설:
irqaffinity=0,1— 모든 IRQ를 housekeeping 코어(0,1)에 보냄. RT 코어로 IRQ가 못 들어오게.mitigations=off— Spectre/Meltdown 완화 끄기 (보안 vs 결정성 trade-off, isolated 시스템에서만).intel_pstate=disable— Intel P-state governor 비활성. 주파수 변동 방지.processor.max_cstate=1— C1 이상의 deep sleep 금지. wakeup latency 0.idle=poll— idle 시 busy loop. wakeup latency를 거의 0으로. 전력 비용 큼.
grub-mkconfig -o /boot/grub/grub.cfg 후 재부팅합니다.
RT 애플리케이션을 cpu 2에 pin:
sudo taskset -c 2 chrt -f 80 ./ec_master_app또는 코드 안에서:
#include <sched.h>#include <pthread.h>
void pin_to_cpu(int cpu) { cpu_set_t set; CPU_ZERO(&set); CPU_SET(cpu, &set); sched_setaffinity(0, sizeof(set), &set);
struct sched_param param = { .sched_priority = 80 }; sched_setscheduler(0, SCHED_FIFO, ¶m);
// Lock memory mlockall(MCL_CURRENT | MCL_FUTURE);}mlockall이 page fault를 막습니다. 산업 RT에서는 반드시 호출합니다.
#cyclictest로 측정
isolation 후 측정:
# RT 코어 (cpu 2)에 200,000 사이클, 250µs 간격으로sudo taskset -c 2 cyclictest -p99 -t 1 -i 250 -n -D 1h전형적 결과:
T: 0 ( 5678) P:99 I:250 C:14400000 Min: 2 Act: 5 Avg: 4 Max: 18 ^^ μs18µs max latency까지 떨어집니다. 250µs 사이클에 충분하고 100µs 사이클도 마진 있게 들어갑니다.
장시간 측정이 중요합니다. 1시간 이상 돌려야 real-world worst case가 보입니다. 짧게 돌리면 page fault·hardware error 같은 드문 사건을 놓칩니다.
hackbench를 백그라운드로 돌리며 측정하면 부하 중 worst case를 봅니다.
# Stress in backgroundhackbench -g 10 -l 10000 &
# Measure on RT coresudo taskset -c 2 cyclictest -p99 -t 1 -i 250 -n -D 30m이 조건에서 Max가 50µs 이하면 production-ready로 간주합니다.
#NIC 튜닝 — Intel I210 / I225 + NXP
NIC 자체의 jitter도 줄여야 합니다. 표준 NIC의 default 설정은 throughput 최적화이므로 latency를 깨트립니다.
#IRQ affinity
NIC IRQ를 RT 코어가 아닌 housekeeping 코어로 보냅니다.
# Find NIC IRQ$ cat /proc/interrupts | grep eth0 29: 1234567 IO-APIC 29-edge eth0 30: 890 IO-APIC 30-edge eth0-rx-0 31: 912 IO-APIC 31-edge eth0-tx-0
# Pin to cpu 0$ echo 1 | sudo tee /proc/irq/29/smp_affinity$ echo 1 | sudo tee /proc/irq/30/smp_affinity$ echo 1 | sudo tee /proc/irq/31/smp_affinity또는 irqbalance를 끄고 tuna로 영구 설정합니다.
sudo systemctl disable --now irqbalancesudo tuna -q eth0 -c 0 -m#Offload 끄기
generic-segmentation-offload, TCP-segmentation-offload 등은 NIC가 큰 프레임을 쪼개는 기능입니다. 결정성을 깨므로 끕니다.
sudo ethtool -K eth0 gso off tso off gro off lro off ufo offsudo ethtool -K eth0 rx-checksumming off tx-checksumming offsudo ip link set eth0 txqueuelen 0#Ring size 줄이기
NIC ring buffer가 크면 프레임이 큐에서 대기합니다. RT 트래픽은 큐에 쌓이면 안 됩니다.
sudo ethtool -G eth0 rx 64 tx 64기본은 보통 256~512이고 고처리량 최적화입니다. RT에서는 프레임이 즉시 송신되어야 하므로 64까지 줄입니다.
#Interrupt coalescing 끄기
NIC가 여러 패킷을 모아 IRQ를 한 번에 거는 coalescing은 latency를 만듭니다.
sudo ethtool -C eth0 rx-usecs 0 tx-usecs 0 rx-frames 1 tx-frames 1 \ adaptive-rx off adaptive-tx offrx-usecs 0 / rx-frames 1이 프레임마다 즉시 IRQ입니다.
#Busy poll (선택)
SO_BUSY_POLL이나 napi_busy_read로 NIC를 busy-loop polling하면 IRQ 자체를 없앨 수 있습니다. CPU 비용이 크지만 jitter 최소입니다.
int busy_poll = 50; // µssetsockopt(fd, SOL_SOCKET, SO_BUSY_POLL, &busy_poll, sizeof(busy_poll));EtherCAT 마스터 같은 전용 thread에 적합합니다.
#Intel I210 / I225
I210은 PTP timestamping + Qav AVB까지. TSN(Qbv/Qbu)는 I225부터입니다.
I225는 LaunchTime이라는 NIC 내장 송신 스케줄러를 가집니다. 송신 시각을 NIC에 미리 알리면 그 시각에 정확히 송신합니다. tc-taprio의 hw offload가 이걸 활용합니다.
NXP LS1021A·LS1028A는 Cortex-A + 내장 TSN switch입니다. enetc·felix 드라이버가 PTP·Qbv·Qbu를 노출합니다.
# LS1028A: 내장 스위치 포트 SWP0~SWP3$ ls /sys/class/net/eno0 eno1 enp0s0 swp0 swp1 swp2 swp3
# Bridge로 노출$ ip link add name br0 type bridge$ ip link set swp0 master br0swp0~3이 완전한 TSN 포트로 동작합니다.
#EtherCAT IgH — Linux 마스터의 표준
EtherCAT 마스터를 Linux로 구현하는 de facto 표준은 IgH EtherLab Master입니다. GPL이고 active maintenance입니다.
git clone https://gitlab.com/etherlab.org/ethercat.gitcd ethercat./bootstrap./configure \ --prefix=/opt/etherlab \ --enable-generic \ --enable-igb \ --with-linux-dir=/lib/modules/$(uname -r)/buildmake -j && sudo make modules_install installsudo depmod/etc/ethercat.conf:
MASTER0_DEVICE="00:1B:21:AA:BB:CC" # 마스터로 쓸 NIC의 MACMASTER0_BACKUP="" # redundancy NIC (없으면 빈 문자열)DEVICE_MODULES="generic" # 또는 "igb" (Intel I210/I225 전용)UTIL_DIR="/opt/etherlab"generic 모듈은 어떤 NIC에서도 동작합니다. 추가 패치된 igb는 더 빠른 send-receive turnaround를 제공하지만, 커널 버전마다 패치 적용 작업이 필요합니다.
서비스 기동:
sudo systemctl enable --now ethercatsudo ethercat slaves연결된 슬레이브 목록이 나옵니다.
$ sudo ethercat slaves0 0:0 PREOP + EK1100 EtherCAT Coupler (2A E-Bus)1 0:1 PREOP + EL2008 8K. Dig. Ausgang 24V, 0.5A2 0:2 PREOP + EL1018 8K. Dig. Eingang 24V, 3ms마스터 application은 userspace에서 ec_master_open API로 cyclic process data를 주고받습니다.
#include <ecrt.h>
ec_master_t *master;ec_domain_t *domain;ec_slave_config_t *sc;
int main(void) { pin_to_cpu(2);
master = ecrt_request_master(0); domain = ecrt_master_create_domain(master);
sc = ecrt_master_slave_config(master, 0, 1, 0x00000002, 0x07d83052); // ... PDO mapping ...
ecrt_master_activate(master);
// Cyclic loop struct timespec next; clock_gettime(CLOCK_MONOTONIC, &next); while (running) { ecrt_master_receive(master); ecrt_domain_process(domain);
// application logic, update PDOs
ecrt_domain_queue(domain); ecrt_master_send(master);
next.tv_nsec += 1000000; // 1 ms cycle if (next.tv_nsec >= 1000000000) { next.tv_sec++; next.tv_nsec -= 1000000000; } clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL); }}clock_nanosleep을 TIMER_ABSTIME으로 호출하는 게 핵심입니다. 매 사이클이 절대 시각에 깨므로 drift가 누적되지 않습니다.
#softPLC 생태계
같은 Linux + PREEMPT_RT 위에 softPLC가 올라가는 흐름이 커지고 있습니다.
| 제품 | 마스터 | 비고 |
|---|---|---|
| CODESYS Runtime | EtherCAT, PROFINET, Modbus | IEC 61131-3 라이선스 |
| Bosch ctrlX AUTOMATION | EtherCAT, OPC UA | Ubuntu 기반, snap 모듈 |
| B&R APROL | POWERLINK | 공정 제어 SCADA + softPLC |
| Phoenix Contact PLCnext | PROFINET | Linux ARM, C++/IEC 61131 |
| Beckhoff TwinCAT/BSD | EtherCAT | FreeBSD 기반, RT는 자체 커널 |
ctrlX·PLCnext가 오픈 ecosystem 측면에서 가장 활발합니다. Docker 컨테이너로 제어 application을 배포하는 IT-style 운영이 가능해집니다.
#자주 하는 실수
#“PREEMPT_RT를 켰는데 latency가 그대로”
CONFIG_PREEMPT_RT=y만 켜고 user-space에서 RT 우선순위를 안 준 경우입니다. chrt -f 80 또는 sched_setscheduler(SCHED_FIFO)를 반드시 호출합니다. 기본 우선순위는 SCHED_OTHER(0)라 다른 일반 태스크와 같이 경쟁합니다.
#“isolcpus를 했는데 RT 코어에 다른 프로세스가 보인다”
isolcpus는 스케줄러 default placement만 막습니다. 명시적으로 affinity를 그 코어로 지정한 프로세스는 들어옵니다. systemd-cgroup이나 cgroupv2 cpuset으로 system slice 전체를 housekeeping 코어로 묶는 게 더 확실합니다.
#“EtherCAT 마스터가 cyclic 안에서 sleep 누락”
clock_nanosleep이 EINTR로 깨면 signal로 인터럽트된 것입니다. while (ret == EINTR) 루프로 재시도하거나, RT 스레드에서 signal mask를 전부 막아 둡니다.
sigset_t set;sigfillset(&set);pthread_sigmask(SIG_BLOCK, &set, NULL);#“I225에 LaunchTime을 쓰는데 가끔 미스”
SO_TXTIME socket option의 시계를 CLOCK_TAI로 잡았는지 확인합니다. CLOCK_REALTIME / CLOCK_MONOTONIC과 섞이면 수십 µs 어긋남이 누적됩니다. ptp4l + phc2sys가 TAI로 system 시계를 맞춰야 합니다.
#“cyclictest는 좋은데 실제 app은 jitter가 크다”
cyclictest가 안 측정하는 application-specific 원인이 있습니다.
- Memory allocation 중에 page fault —
mlockall후 부팅 시 메모리를 미리 touch합니다. - I/O syscall (
printf,fprintf) 안에 sleep — RT thread에서 printf 금지. 별도 thread로 logging. - Mutex contention with non-RT thread — priority inheritance mutex만 사용.
#“softPLC가 잘 돌다가 한 시간에 한 번 spike”
거의 항상 firmware/microcode update나 power management 이벤트입니다. dmesg에서 그 시점을 확인합니다. *SMI(System Management Interrupt)*가 의심되면 BIOS에서 SMI 출처들(USB legacy emulation, power button polling 등)을 끕니다.
#정리
- vanilla Linux의 worst-case latency는 수 ms입니다. 모든 단계에서 결정성을 짜야 µs 단위로 들어옵니다.
- PREEMPT_RT는 Linux 6.12부터 mainline 통합입니다. Xenomai 4 / EVL은 더 극단적 결정성용입니다.
- isolcpus + nohz_full + rcu_nocbs가 함께 적용되어야 RT 코어가 완전 격리됩니다.
- IRQ affinity를 housekeeping 코어로 보내고, cpuidle·cpufreq를 끄고, mlockall로 page fault를 막습니다.
- cyclictest는 1시간 이상 hackbench 부하 중에 측정해야 의미가 있습니다. 50µs 이하면 production-ready입니다.
- NIC 튜닝 — gso/tso off, ring 64, coalescing 0, txqueuelen 0. Intel I225는 LaunchTime으로 TSN 지원.
- IgH EtherCAT이 Linux 마스터의 표준입니다. userspace에서
clock_nanosleep(TIMER_ABSTIME)이 cyclic의 핵심. - softPLC 생태계 — CODESYS·ctrlX·B&R APROL·Phoenix PLCnext가 모두 PREEMPT_RT 기반.
다음 편이자 시리즈 마지막은 Ch 12: 비교 분석 — 프로토콜 선택 가이드입니다.
#관련 항목
Industrial Ethernet 심화 · 11 of 12
- 1 산업용 이더넷 분석 — 일반 이더넷과 결정성 요구의 차이
- 2 산업용 통신 실시간 요구사항 — Determinism·Jitter·Cycle Time
- 3 EtherCAT 아키텍처 분해 — Processing on the Fly 메커니즘
- 4 EtherCAT 프레임 구조 분석 — Datagram·WKC·Address 모드
- 5 EtherCAT Master 구현 비교 — SOEM·IgH·TwinCAT 분석
- 6 PROFINET 개요 분석 — RT·IRT 클래스와 실시간 등급
- 7 PROFINET IO 모델 — Controller·Device·Supervisor 역할 추적
- 8 TSN 표준 분석 — IEEE 802.1 Time-Sensitive Networking 개요
- 9 TSN 스케줄링 메커니즘 — Qbv·Qbu·gPTP 동기화 분석
- 10 POWERLINK과 OpenSAFETY 분석 — 산업 안전 통신 프로토콜
- 11 리눅스 실시간 산업 통신 — PREEMPT_RT·EtherCAT Master 운영
- 12 산업용 이더넷 프로토콜 비교 — EtherCAT·PROFINET·POWERLINK·TSN 선택
관련 글
산업용 이더넷 프로토콜 비교 — EtherCAT·PROFINET·POWERLINK·TSN 선택
EtherCAT·PROFINET·EtherNet/IP·POWERLINK·SERCOS III·TSN — 시나리오별 선택 가이드와 한국 산업 현장 적용 사례.
같은 시리즈에서 이어 읽기
POWERLINK과 OpenSAFETY 분석 — 산업 안전 통신 프로토콜
B&R발 오픈소스 표준 산업 이더넷·통합 safety layer — slot polling으로 1ms 결정성을 만드는 법.
같은 시리즈에서 이어 읽기
PREEMPT_RT Linux — Mainline 6.12·Xenomai 4·EVL
2024년 9월 Linux 6.12 mainline에 합류한 PREEMPT_RT의 핵심 변경을 정리하고, Xenomai 4·EVL과 함께 RTOS와의 선택 기준을 비교합니다. threaded IRQ·sleeping spinlock·cyclictest까지 한 지도에 모읍니다.
공통 태그 기반 추천