Cấu hình Kernel Real-time (PREEMPT_RT) trên Linux cho Các Tác vụ Độ trễ Cực thấp

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Khi Kernel Tiêu chuẩn Cản trở Bạn

Vài năm trước, tôi tham gia thiết lập một pipeline xử lý âm thanh độ trễ thấp trên server Linux. Ứng dụng liên tục bị drop frame — không phải vì CPU quá tải, mà vì kernel scheduler đôi khi trì hoãn tác vụ vài millisecond. Đó là lần đầu tiên tôi gặp PREEMPT_RT.

Kernel Linux tiêu chuẩn được tối ưu cho thông lượng, không phải tính xác định. Các tác vụ kernel có thể giữ lock trong thời gian dài. Interrupt handler có thể làm trễ ứng dụng của bạn theo những cách hoàn toàn không thể đoán trước. Với hầu hết workload — web server, database, containerized app — điều đó hoàn toàn ổn. Nhưng với các ứng dụng real-time như:

  • Hệ thống điều khiển công nghiệp (thay thế PLC, điều khiển chuyển động)
  • Sản xuất âm thanh/video độ trễ thấp
  • Hệ thống giao dịch tài chính yêu cầu độ chính xác microsecond
  • Robot và các nền tảng Linux nhúng
  • Viễn thông (5G RAN, xử lý gói tin dựa trên DPDK)

…kernel mặc định đơn giản là không đáp ứng được. Độ trễ lập lịch trong trường hợp tệ nhất trên một hệ thống chưa được tinh chỉnh có thể tăng vọt lên 5–20 millisecond. Trong thuật ngữ real-time, đó là một khoảng thời gian vô tận.

PREEMPT_RT Thực sự Thay đổi Điều gì

PREEMPT_RT tái cấu trúc toàn bộ mô hình locking của kernel. Hầu hết spinlock trở thành preemptable mutex. Interrupt handler chạy như các preemptable kernel thread thay vì trong hard interrupt context. Gần như mọi code path trong kernel đều có thể bị gián đoạn giữa chừng. Kết quả: các tác vụ ưu tiên cao của bạn có thể preempt ngay cả code nội bộ của kernel, giảm độ trễ trường hợp tệ nhất từ millisecond xuống còn đơn vị microsecond trên phần cứng được tinh chỉnh đúng cách.

Cài đặt Kernel PREEMPT_RT

Tùy chọn 1: Gói Dựng sẵn (Ubuntu/Debian — Khuyến nghị để Bắt đầu)

Ubuntu cung cấp các gói kernel real-time thông qua Ubuntu Pro. Ngay cả ở gói miễn phí, kernel low-latency cũng mang lại cải thiện đáng kể:

# Kiểm tra các kernel RT có sẵn
apt search linux-image-rt

# Kernel low-latency (PREEMPT, không phải RT đầy đủ — điểm khởi đầu tốt)
sudo apt install linux-image-lowlatency linux-headers-lowlatency

# PREEMPT_RT đầy đủ trên Ubuntu Pro (miễn phí cho tối đa 5 máy)
sudo pro attach <your-token>
sudo pro enable realtime-kernel

Với hệ thống Debian, kernel RT có trong kho lưu trữ tiêu chuẩn:

sudo apt install linux-image-rt-amd64 linux-headers-rt-amd64

Tùy chọn 2: Biên dịch từ Source

Khi bạn cần một phiên bản kernel cụ thể, hỗ trợ driver tùy chỉnh, hoặc toàn quyền kiểm soát cấu hình build, biên dịch từ source là lựa chọn đúng đắn. Dành 30–60 phút trên máy tính để bàn hiện đại, hoặc gần 2 tiếng trên VPS 2 lõi. Một quy tắc đã cứu tôi nhiều lần: luôn thử nghiệm trên máy staging trước. Build kernel RT là đúng loại thay đổi có thể gây bất ngờ trên production nếu bạn bỏ qua bước đó.

# Cài đặt các gói phụ thuộc để build
sudo apt install build-essential libncurses-dev bison flex \
  libssl-dev libelf-dev bc dwarves

# Tải source kernel và RT patch tương ứng
KERNEL_VERSION=6.6.21
RT_PATCH=patch-6.6.21-rt26

wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${KERNEL_VERSION}.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/6.6/${RT_PATCH}.patch.xz

tar xf linux-${KERNEL_VERSION}.tar.xz
cd linux-${KERNEL_VERSION}
xzcat ../${RT_PATCH}.patch.xz | patch -p1
# Bắt đầu từ config kernel đang chạy hiện tại
cp /boot/config-$(uname -r) .config
make olddefconfig

# Bật PREEMPT_RT đầy đủ
scripts/config --enable PREEMPT_RT
scripts/config --disable PREEMPT_VOLUNTARY
scripts/config --disable PREEMPT

# Build dưới dạng gói .deb (dễ cài/gỡ hơn)
make -j$(nproc) deb-pkg LOCALVERSION=-rt

# Cài đặt
sudo dpkg -i ../linux-image-*.deb ../linux-headers-*.deb

# Khởi động lại và chọn kernel RT trong GRUB
sudo reboot

Sau khi khởi động lại, xác nhận bạn đang dùng kernel RT:

uname -a
# Output mong đợi chứa: PREEMPT_RT
# Ví dụ: Linux myhost 6.6.21-rt26 #1 SMP PREEMPT_RT Sat Apr 5 ...

Cấu hình Hệ thống cho Hiệu năng Real-Time

Cài đặt kernel RT là phần dễ dàng. Công việc thực sự là thuyết phục phần còn lại của OS để yên cho tác vụ RT của bạn.

Cô lập CPU

Dành riêng các lõi CPU cụ thể cho workload RT của bạn — kernel scheduler sẽ không đặt bất kỳ công việc nào khác lên chúng sau khi đã cô lập:

sudo nano /etc/default/grub

# Thêm vào GRUB_CMDLINE_LINUX_DEFAULT — ở đây chúng ta cô lập lõi 2 và 3:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3"

sudo update-grub
sudo reboot

nohz_full tắt timer tick trên các lõi đó (giảm jitter do interrupt), và rcu_nocbs chuyển RCU callback ra khỏi chúng. Ba tham số này hoạt động cùng nhau — hãy dùng cả ba.

IRQ Affinity

Chuyển các hardware interrupt ra khỏi các lõi đã cô lập. Cách đơn giản nhất là cấu hình irqbalance để tránh chúng:

sudo apt install irqbalance
sudo nano /etc/default/irqbalance

# Bitmask — lõi 2 và 3 = nhị phân 1100 = hex 0xC
IRQBALANCE_BANNED_CPUS="0x0c"

sudo systemctl restart irqbalance

Với các IRQ cụ thể mà irqbalance không xử lý (như MSI interrupt của card mạng), gán thủ công:

# Xem tất cả IRQ đang hoạt động
cat /proc/interrupts

# Ghim IRQ 30 chỉ vào CPU 0 (smp_affinity là bitmask CPU)
echo 1 | sudo tee /proc/irq/30/smp_affinity

Giới hạn Tài nguyên cho RT Process

RT process cần quyền lock bộ nhớ và đặt mức ưu tiên lập lịch cao. Thêm vào /etc/security/limits.conf:

sudo nano /etc/security/limits.conf

# Thay 'rtuser' bằng tên người dùng thực của bạn, hoặc dùng @audio cho nhóm audio
rtuser    -    rtprio     99
rtuser    -    memlock    unlimited
rtuser    -    nice       -20

Tham số Tinh chỉnh Kernel

sudo nano /etc/sysctl.d/99-realtime.conf
# Tắt kernel watchdog trên các lõi RT (chỉ lõi 0,1 — hex 0x3 = nhị phân 0011)
kernel.watchdog_cpumask = 0x3

# Cho phép RT task tiêu thụ 100% CPU khi cần
# CẢNH BÁO: đặt lại về 950000 nếu RT task của bạn spin không mong muốn
kernel.sched_rt_runtime_us = -1

# Giảm thiểu áp lực swap
vm.swappiness = 0
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
sudo sysctl -p /etc/sysctl.d/99-realtime.conf

CPU Frequency Governor

Frequency scaling tạo ra biến thiên độ trễ. Khóa các lõi RT ở tần số tối đa:

sudo apt install cpufrequtils

# Đặt performance governor trên các lõi đã cô lập
sudo cpufreq-set -g performance -c 2
sudo cpufreq-set -g performance -c 3

# Kiểm tra
cat /sys/devices/system/cpu/cpu2/cpufreq/scaling_governor
# performance

Khởi chạy Ứng dụng với RT Scheduling

Ghim ứng dụng của bạn vào các lõi đã cô lập và cấu hình SCHED_FIFO scheduling:

# SCHED_FIFO ưu tiên 80, ghim vào lõi 2
sudo chrt -f 80 taskset -c 2 ./your-rt-application

# Kiểm tra chính sách lập lịch mà một process đang chạy đang sử dụng
chrt -p <PID>

Xác minh và Giám sát

Đừng bao giờ giả định rằng việc tinh chỉnh đã hoạt động — hãy đo lường. Benchmark độ trễ RT tiêu chuẩn là cyclictest, đo chênh lệch giữa khi timer đáng lẽ kích hoạt và khi nó thực sự kích hoạt.

Chạy cyclictest

sudo apt install rt-tests

# Chạy trên lõi đã cô lập 2, RT priority 80, trong 60 giây
sudo cyclictest \
  --mlockall \
  --priority=80 \
  --interval=200 \
  --distance=0 \
  --affinity=2 \
  --duration=60s \
  --histogram=400 \
  --histfile=latency.hist

Ba con số quan trọng:

  • Min: độ trễ phần cứng cơ sở — thường 1–10 µs trên x86 hiện đại
  • Avg: độ trễ hoạt động bình thường dưới tải ổn định
  • Max: jitter trường hợp tệ nhất — đây là con số mà ứng dụng của bạn thực sự phải chịu đựng được

Trên một hệ thống PREEMPT_RT được tinh chỉnh tốt, Max nên dưới 100 µs. Các triển khai nghiêm túc — điều khiển công nghiệp, audio chuyên nghiệp — thường xuyên đạt 20–50 µs trên phần cứng server tốt. Chạy cùng bài test trên kernel mặc định dưới tải và các spike 5–20 ms là hoàn toàn bình thường.

Kiểm tra dưới Tải Thực tế

Các con số độ trễ khi nhàn rỗi hầu như vô dụng. Bạn cần biết điều gì xảy ra khi hệ thống thực sự chịu tải:

# Terminal 1: Đo độ trễ
sudo cyclictest --mlockall --priority=80 --interval=200 --duration=120s --affinity=2

# Terminal 2: Tải CPU và bộ nhớ
sudo apt install stress-ng
stress-ng --cpu 2 --vm 1 --vm-bytes 1G --io 2

# Terminal 3: Tải mạng
iperf3 -s &
iperf3 -c localhost -t 120 -P 4

Chạy cả ba đồng thời và theo dõi Max. Nếu nó vẫn nằm trong deadline của ứng dụng với tất cả stressor đang hoạt động, việc tinh chỉnh của bạn đủ vững chắc để xác thực production.

Truy vết Độ trễ Đột biến với ftrace

Khi cyclictest hiển thị các outlier không giải thích được, ftrace chỉ ra chính xác code path của kernel gây ra:

# Bật tracer độ trễ preempt/IRQ-off
echo preemptirqsoff | sudo tee /sys/kernel/debug/tracing/current_tracer
echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_on

# Chạy workload vài giây, sau đó lấy trace
sudo cat /sys/kernel/debug/tracing/trace | head -80

# Tắt tracing
echo 0 | sudo tee /sys/kernel/debug/tracing/tracing_on

Script Kiểm tra Sức khỏe RT

Khởi động lại không phải lúc nào cũng khôi phục mọi cài đặt RT. IRQ affinity là thủ phạm tệ nhất — nó tự reset sau khi cập nhật kernel mà không có cảnh báo. Script này phát hiện vấn đề trước khi workload RT của bạn gặp phải:

#!/bin/bash
# rt-check.sh — kiểm tra cấu hình RT sau khi khởi động lại

echo "=== Kernel ==="
uname -r | grep -q PREEMPT_RT && echo "OK: PREEMPT_RT đang hoạt động" || echo "CẢNH BÁO: Kernel tiêu chuẩn"

echo ""
echo "=== CPU Governor ==="
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
  echo "  $(basename $(dirname $(dirname $cpu))): $(cat $cpu)"
done

echo ""
echo "=== CPU Đã Cô lập ==="
echo "  $(cat /sys/devices/system/cpu/isolated 2>/dev/null || echo 'không có')"

echo ""
echo "=== Swappiness ==="
echo "  $(sysctl vm.swappiness)"
chmod +x rt-check.sh
./rt-check.sh

Chạy script này sau mỗi lần cập nhật kernel hoặc khởi động lại, trước khi đưa RT workload của bạn trở lại hoạt động. Script đã phát hiện nhiều lần các cấu hình sai âm thầm — đặc biệt sau các lần nâng cấp tự động.

Share: