树莓派上实时系统#
Linux 实时系统测试主要关注系统的实时性能和确定性,以确保任务能够在严格的时间约束内完成。以下是进行Linux实时系统测试的一些关键步骤和工具:
1. 确定系统需求#
首先,需要明确实时系统的需求,包括延迟要求、抖动容忍度以及系统的实时任务类型(硬实时、软实时)。
2. 配置实时内核#
确保Linux系统使用的是实时内核(PREEMPT_RT补丁)。可以从Kernel.org下载并编译实时内核。
3. 测试工具#
使用适当的工具来测试系统的实时性能:
a. cyclictest#
cyclictest 是一个广泛使用的工具,用于测量系统的中断延迟和周期性任务的抖动。它可以帮助评估系统的确定性。
sudo cyclictest -l10000 -m -Sp90 -i200 -h400 -q > cyclictest.log
# 5个线程 , 优先级为80
sudo cyclictest -t 5 -p 80
sudo cyclictest -l100000 -m -Sp90 -i200 -h400 -q > cyclictest.log 是一个命令,用于运行 cyclictest 工具来测量Linux实时系统的中断延迟和周期性任务的抖动。以下是对每个选项的详细解释:
sudo: 以超级用户(root)权限运行命令,因为cyclictest需要访问系统级资源。cyclictest: 测量系统中断延迟和周期性任务抖动的工具。-l100000: 运行100000次循环。这意味着cyclictest将执行100000个周期性任务来测量延迟。-m: 锁定内存,防止内存分页。这有助于减少由于内存分页导致的延迟抖动。-Sp90: 使用优先级为90的SCHED_FIFO调度策略。S表示使用实时调度策略,p90表示优先级为90。-i200: 设置周期性任务的间隔时间为200微秒。cyclictest每隔200微秒唤醒一次,测量调度延迟。-h400: 设置最大延迟的阈值为400微秒。如果延迟超过400微秒,cyclictest将记录一个警告。-q: 安静模式,减少输出信息,只记录必要的数据。> cyclictest.log: 将输出重定向到文件cyclictest.log,方便后续分析。
命令的具体作用#
这个命令启动了一个实时任务,优先级设置为90,使用SCHED_FIFO调度策略。任务每200微秒运行一次,共运行100000次。通过锁定内存和设置最大延迟阈值,该测试可以帮助确定系统在不同负载条件下的实时性能。
分析日志文件#
运行完 cyclictest 后,可以通过分析 cyclictest.log 文件来获取系统的实时性能数据。可以查看每个任务的延迟时间,评估系统的实时性是否满足要求。
例子#
假设你运行了这个命令,生成了 cyclictest.log 文件,文件内容如下:
# /dev/cpu_dma_latency set to 0us
# policy: fifo: loadavg: 0.23 0.06 0.02 1/163 1322
# Parameters: a:1 n:1 policy: fifo priority:90 interval:2000 duration:0
# _----=> CPU use histogram
# CPU | cycles | min | avg | max | stdev | count |
# --- |------------|---------------------|---------------------|---------------------|---------------------|---------------------|
0 | 0 | 1.00 | 5.20 | 30.00 | 3.00 | 100000 |
这个示例输出显示,在100000个周期内,最小延迟为1微秒,平均延迟为5.20微秒,最大延迟为30微秒,标准差为3微秒。
通过这样的分析,可以评估系统是否满足实时性能要求,并据此进行进一步的调优。
测试rt-test#
- 无实时系统补丁
# Total: 000100000 000100000 000099970 000099912
# Min Latencies: 00005 00006 00006 00005
# Avg Latencies: 00010 00010 00010 00010
# Max Latencies: 00065 00139 00054 00027
- 无实时系统补丁 锁核 + 锁频
b. stress-ng#
stress-ng 用于生成系统负载,测试在高负载情况下的实时性能。
c. rt-tests#
rt-tests 提供了一系列测试工具,如 cyclictest、signaltest 等,用于评估实时系统的性能。
4. 分析和调优#
使用上述工具生成的数据,分析系统的响应时间和抖动情况。如果不满足需求,可以进行以下调优:
- 中断处理:优化中断处理程序,确保中断能及时处理。
- CPU隔离:将实时任务运行在专用的CPU上,避免与其他任务竞争资源。
- 优先级调度:使用适当的调度策略(如FIFO或RR)和优先级设置,确保关键任务能按时执行。
5. 持续监控#
部署后,持续监控系统的实时性能,使用类似 collectl 和 sar 的工具,确保系统在长时间运行下仍然满足实时性要求。
以下是一个简单的测试和分析流程示例:
# 安装必要工具
sudo apt-get install rt-tests stress-ng
# 配置并启动实时内核(假设已经编译并安装)
sudo grub-set-default "Real-Time Kernel"
sudo reboot
# 运行cyclictest
sudo cyclictest -l100000 -m -Sp90 -i200 -h400 -q > cyclictest.log
# 生成负载
sudo stress-ng --cpu 8 --io 4 --vm 2 --vm-bytes 1G --timeout 60s
# 分析结果
cat cyclictest.log | grep "T: 0" | awk '{print $2,$3,$4,$5,$6}' | sort -n -k2
这些步骤和工具可以帮助评估和改进Linux实时系统的性能。如果你有特定的实时系统应用或更详细的需求,可以进一步细化测试方案和调优措施。
查看优先级和亲核性#
example:
admin@raspberrypi:~/t/security $ chrt -p 2895
pid 2895's current scheduling policy: SCHED_OTHER
pid 2895's current scheduling priority: 0
admin@raspberrypi:~/t/security $ chrt -p 3173
pid 3173's current scheduling policy: SCHED_RR
pid 3173's current scheduling priority: 1
admin@raspberrypi:~/t/security $ taskset -p 3173
\pid 3173's current affinity mask: c
admin@raspberrypi:~/t/security $ taskset -cp 3173
pid 3173's current affinity list: 2,3
设置优先级,调度策略,亲核性#
renice -n 5 -p 1234
sudo chrt -f -p 99 1234
taskset -cp 0,2 1234
# 启动高性能实时进程(需要root权限)
sudo chrt -f 99 taskset -c 0 nice -n -20 ./critical_realtime_app
# 分解说明:
# chrt -f 99 : 设置SCHED_FIFO实时调度,最高优先级99
# taskset -c 0 : 绑定到CPU核心0
# nice -n -20 : 设置最高nice值(最低数字优先级)
使用 nice 命令(启动新进程时设置)#
# 以较低的优先级启动进程(较高的nice值)
nice -n 10 /path/to/command
# 以较高的优先级启动进程(较低的nice值,需要root权限)
sudo nice -n -10 /path/to/command
# 示例:以nice值15运行一个耗时的计算
nice -n 15 ./cpu_intensive_program
使用 renice 命令(修改运行中进程的优先级)#
# 修改指定PID进程的nice值
renice -n 优先级值 -p PID
# 示例:将进程1234的nice值设置为5
renice -n 5 -p 1234
# 示例:将进程1234设置为较高优先级(需要root)
sudo renice -n -5 -p 1234
# 修改属于特定用户的所有进程
renice -n 10 -u username
# 修改进程组的所有进程
renice -n 10 -g pgid
2. 设置进程调度策略#
使用 chrt 命令#
调度策略选项:
-f:SCHED_FIFO(实时先进先出)-r:SCHED_RR(实时轮转)-o:SCHED_OTHER(默认分时调度)-b:SCHED_BATCH(批处理)-i:SCHED_IDLE(空闲调度)
示例:
# 以SCHED_FIFO策略,优先级50启动进程(需要root)
sudo chrt -f 50 ./realtime_program
# 以SCHED_RR策略,优先级10启动进程
sudo chrt -r 10 ./realtime_program
# 将运行中进程1234改为SCHED_FIFO,优先级99
sudo chrt -f -p 99 1234
# 将进程改回普通调度
sudo chrt -o -p 0 1234
3. 设置CPU亲和性(绑定到特定核心)#
使用 taskset 命令#
示例:
# 将进程绑定到CPU 0和1上运行
taskset -c 0,1 ./cpu_intensive_program
# 将进程绑定到CPU 0-3(4个核心)
taskset -c 0-3 ./program
# 修改运行中进程1234的CPU亲和性,绑定到CPU 2
taskset -cp 2 1234
# 绑定到CPU 0和2(跳过1)
taskset -cp 0,2 1234
# 查看当前亲和性设置
taskset -cp 1234
4. 综合设置示例#
同时设置优先级、调度策略和CPU亲和性#
# 启动高性能实时进程(需要root权限)
sudo chrt -f 99 taskset -c 0 nice -n -20 ./critical_realtime_app
# 分解说明:
# chrt -f 99 : 设置SCHED_FIFO实时调度,最高优先级99
# taskset -c 0 : 绑定到CPU核心0
# nice -n -20 : 设置最高nice值(最低数字优先级)
批量设置脚本示例#
#!/bin/bash
# 文件名: optimize_process.sh
PID=$1
CPU_CORES=$2
SCHED_PRIORITY=$3
if [ -z "$PID" ] || [ -z "$CPU_CORES" ]; then
echo "用法: $0 <PID> <CPU核心> [调度优先级]"
echo "示例: $0 1234 0-1 50"
exit 1
fi
echo "优化进程 $PID 的调度设置..."
# 设置CPU亲和性
echo "设置CPU亲和性为: $CPU_CORES"
taskset -cp $CPU_CORES $PID
# 如果指定了调度优先级,设置实时调度
if [ ! -z "$SCHED_PRIORITY" ]; then
echo "设置实时调度策略,优先级: $SCHED_PRIORITY"
chrt -f -p $SCHED_PRIORITY $PID
fi
# 显示结果
echo -e "\n优化完成,当前设置:"
echo "CPU亲和性: $(taskset -cp $PID | cut -d: -f2)"
echo "调度策略: $(chrt -p $PID 2>/dev/null | grep policy | cut -d: -f2 || echo 'N/A')"
5. 实用的组合命令#
监控并自动优化高CPU使用率进程#
#!/bin/bash
# 自动将高CPU进程绑定到特定核心
# 查找CPU使用率超过50%的进程
HIGH_CPU_PIDS=$(ps -eo pid,%cpu,comm --sort=-%cpu | awk '$2>50 && $1!=1 {print $1}' | head -5)
for PID in $HIGH_CPU_PIDS; do
if [ -d "/proc/$PID" ]; then
echo "优化高CPU进程 $PID ($(ps -p $PID -o comm=))"
# 绑定到CPU核心2-3(避免干扰系统核心)
taskset -cp 2-3 $PID
# 设置为较低优先级
renice -n 10 -p $PID
fi
done
系统服务优化脚本#
#!/bin/bash
# 关键系统服务优化
# 优化关键服务到专用CPU核心
declare -A SERVICES=(
["网络服务"]="0x1" # CPU 0
["存储服务"]="0x2" # CPU 1
["数据库服务"]="0x4" # CPU 2
)
optimize_service() {
local service_name=$1
local cpu_mask=$2
# 查找服务进程
local pids=$(pgrep -f "$service_name")
for pid in $pids; do
echo "优化 $service_name (PID: $pid)"
taskset -p $cpu_mask $pid
chrt -o -p 0 $pid # 设置为普通调度
renice -n -5 -p $pid
done
}
# 应用优化
for service in "${!SERVICES[@]}"; do
optimize_service "$service" "${SERVICES[$service]}"
done