目录
背景
LitmusRT是一个基于Linux的实时调度平台,其提供了一些调度插件,可以帮助我们实时调度我们的任务进程或线程,我们也可以基于其提供的API编写实时任务线程或者自己的调度插件。
本文记录LitmusRT的使用过程,需要大家根据官网事先安装好LitmusRT、liblitmus和feature,如果编译安装Litmus时遇到了问题,可以参考我的文章编译安装LitmusRT遇到的问题
调度器的查看与使用
我们需要进入liblitmus目录,以便使用命令来进行调度器的查看与设置
root@ubuntu:/home/szc# cd litmus/liblitmus/
查看当前调度器
root@ubuntu:/home/szc/litmus/liblitmus# ./showsched
Linux
litmusRT默认使用Linux自带的调度器,我们可以看一下它支持哪些调度器
root@ubuntu:/home/szc/litmus/liblitmus# cat /proc/litmus/plugins/loaded
P-RES
PFAIR
C-EDF
P-FP
PSN-EDF
GSN-EDF
Linux
设置新调度器
root@ubuntu:/home/szc/litmus/liblitmus# ./setsched GSN-EDF
root@ubuntu:/home/szc/litmus/liblitmus# ./showsched
GSN-EDF
使能新的调度器后,Linux调度器继续共存,以运行非实时的任务
下面就从这种全局的最早截止时间调度器开始进行学习
GSN-EDF
我们要启动实时进程来进行测试。启动方法有三,如下所示
启动命令 | 简介 |
rt_launch | 把任何进程作为实时进程 |
rtspin | 用来做实验的伪高速任务 |
liblitmus-based | 运行自定义任务,这些任务得用liblitmus库的api写 |
rt_launch
以下是使用rt_launch的例子,得先用apt-get安装lighttpd
root@ubuntu:/home/szc/litmus/liblitmus# ./rt_launch 50 100 -- /usr/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf
50表示任务执行时间的预算(超时任务就取消),100表示任务执行周期(每多久执行一次任务,继续上次的执行),单位都是ms。/usr/sbin/lighttpd是要执行的进程,后面为进程的参数。--表示分隔符,用来分隔rt_launch的参数与待执行文件及其参数
如果使用固定优先级的调度器,那么就要使用-q指定优先级
rtspin
rtspin用来运行前台任务以进行测试,下面是一个例子,其中-v表示输出每个任务的详情,10表示任务的执行预算,1000表示执行间隔,5表示rtspin的执行时间,前两者单位为ms,最后的参数单位为s。也就是说让rtspin每个1s运行一个任务,每个任务运行预算为10ms,并输出每个任务的详细信息,任务最大总用时为5s。
root@ubuntu:/home/szc/litmus/liblitmus# ./rtspin -v 10 1000 5
rtspin/23834:1 @ 0.0072ms
release: 17262720832387ns (=17262.72s)
deadline: 17263720832387ns (=17263.72s)
cur time: 17262720878014ns (=17262.72s)
time until deadline: 999.95ms
target exec. time: 9.50ms (95.00% of WCET)
sleep_next_period() until 17263720832387ns (=17263.72s)
rtspin/23834:2 @ 1000.3781ms
release: 17263720832387ns (=17263.72s)
deadline: 17264720832387ns (=17264.72s)
cur time: 17263721223474ns (=17263.72s)
time until deadline: 999.61ms
target exec. time: 9.50ms (95.00% of WCET)
sleep_next_period() until 17264720832387ns (=17264.72s)
rtspin/23834:3 @ 2000.5350ms
release: 17264720832387ns (=17264.72s)
deadline: 17265720832387ns (=17265.72s)
cur time: 17264721380697ns (=17264.72s)
time until deadline: 999.45ms
target exec. time: 9.50ms (95.00% of WCET)
sleep_next_period() until 17265720832387ns (=17265.72s)
rtspin/23834:4 @ 3000.6142ms
release: 17265720832387ns (=17265.72s)
deadline: 17266720832387ns (=17266.72s)
cur time: 17265721460250ns (=17265.72s)
time until deadline: 999.37ms
target exec. time: 9.50ms (95.00% of WCET)
sleep_next_period() until 17266720832387ns (=17266.72s)
rtspin/23834:5 @ 4000.2530ms
release: 17266720832387ns (=17266.72s)
deadline: 17267720832387ns (=17267.72s)
cur time: 17266721097362ns (=17266.72s)
time until deadline: 999.74ms
target exec. time: 9.50ms (95.00% of WCET)
sleep_next_period() until 17267720832387ns (=17267.72s)
查看某个任务的输出信息,可看到它的格式如下
rtspin/pid:任务索引 @ 任务启动时间
release: 任务开始时间
deadline: 任务截止时间(人物开始时间 + 1周期)
cur time: 当前时间
time until deadline: 任务启动后的剩余时间
target exec. time: 任务执行时间 (占预算的百分比 of WCET)
sleep_next_period() until 休眠阻塞到下一周期的时间
P-FP
这是一种分区的、固定优先级的调度器,先使能之
root@ubuntu:/home/szc/litmus/liblitmus# ./setsched P-FP
使用分区固定优先级调度器调度进程时,需要额外的参数指定分区,分区值为[0, 处理器数 - 1]
root@ubuntu:/home/szc/litmus/liblitmus# ./rtspin -v -p 3 5 1000 2
rtspin/23898:1 @ 0.0050ms
release: 18402779742670ns (=18402.78s)
deadline: 18403779742670ns (=18403.78s)
cur time: 18402779763054ns (=18402.78s)
time until deadline: 999.98ms
target exec. time: 4.75ms (95.00% of WCET)
sleep_next_period() until 18403779742670ns (=18403.78s)
rtspin/23898:2 @ 1000.4060ms
release: 18403779742670ns (=18403.78s)
deadline: 18404779742670ns (=18404.78s)
cur time: 18403780162072ns (=18403.78s)
time until deadline: 999.58ms
target exec. time: 4.75ms (95.00% of WCET)
sleep_next_period() until 18404779742670ns (=18404.78s)
这里-p值为3,是因为我这虚拟机总共就4个CPU核
我们也可以通过-q指定优先级,因为这毕竟是固定优先级的调度器,优先级值域为[1, 511],值越大,优先级越低,默认选取最低的优先级
root@ubuntu:/home/szc/litmus/liblitmus# ./rtspin -v -p 3 -q 3 5 1000 2
rtspin/23906:1 @ 0.0050ms
release: 18582148655138ns (=18582.15s)
deadline: 18583148655138ns (=18583.15s)
cur time: 18582148676177ns (=18582.15s)
time until deadline: 999.98ms
target exec. time: 4.75ms (95.00% of WCET)
sleep_next_period() until 18583148655138ns (=18583.15s)
rtspin/23906:2 @ 1000.1960ms
release: 18583148655138ns (=18583.15s)
deadline: 18584148655138ns (=18584.15s)
cur time: 18583148866165ns (=18583.15s)
time until deadline: 999.79ms
target exec. time: 4.75ms (95.00% of WCET)
sleep_next_period() until 18584148655138ns (=18584.15s)
如果我们需要执行同步任务,就是让多个任务同时进行,可以使用-w参数,让任务等待同步信号
root@ubuntu:/home/szc/litmus/liblitmus# ./rtspin -w -v -p 3 -q 1 5 1000 2 &
[1] 23916
root@ubuntu:/home/szc/litmus/liblitmus# ./rtspin -w -v -p 3 -q 2 5 1000 2 &
[2] 23917
后面的&表示任务在后台执行,然后我们可以查看一下就绪的任务状态
root@ubuntu:/home/szc/litmus/liblitmus# cat /proc/litmus/stats
real-time tasks = 2
ready for release = 2
而后我们发出同步信号,看看执行效果
root@ubuntu:/home/szc/litmus/liblitmus# ./release_ts
Synchronous release at time 18956000.00ms.
Released 2 real-time tasks.
root@ubuntu:/home/szc/litmus/liblitmus# rtspin/23916:3 @ 0.0012ms
release: 18956000000000ns (=18956.00s)
deadline: 18957000000000ns (=18957.00s)
cur time: 18956000211697ns (=18956.00s)
time until deadline: 999.79ms
target exec. time: 4.75ms (95.00% of WCET)
sleep_next_period() until 18957000000000ns (=18957.00s)
rtspin/23917:3 @ 0.0012ms
release: 18956000000000ns (=18956.00s)
deadline: 18957000000000ns (=18957.00s)
cur time: 18956004984996ns (=18956.00s)
time until deadline: 995.02ms
target exec. time: 4.75ms (95.00% of WCET)
sleep_next_period() until 18957000000000ns (=18957.00s)
rtspin/23916:4 @ 1000.4890ms
release: 18957000000000ns (=18957.00s)
deadline: 18958000000000ns (=18958.00s)
cur time: 18957000693040ns (=18957.00s)
time until deadline: 999.31ms
target exec. time: 4.75ms (95.00% of WCET)
sleep_next_period() until 18958000000000ns (=18958.00s)
rtspin/23917:4 @ 1000.4902ms
release: 18957000000000ns (=18957.00s)
deadline: 18958000000000ns (=18958.00s)
cur time: 18957005472014ns (=18957.01s)
time until deadline: 994.53ms
target exec. time: 4.75ms (95.00% of WCET)
sleep_next_period() until 18958000000000ns (=18958.00s)
rtspin/23917:5 @ 1995.4231ms
release: 18958000000000ns (=18958.00s)
deadline: 18959000000000ns (=18959.00s)
cur time: 18958000416045ns (=18958.00s)
time until deadline: 999.58ms
target exec. time: 4.75ms (95.00% of WCET)
sleep_next_period() until 18959000000000ns (=18959.00s)
^C
[1]- Done ./rtspin -w -v -p 3 -q 1 5 1000 2
[2]+ Done ./rtspin -w -v -p 3 -q 2 5 1000 2
P-RES
P-RES是一种基于预留的调度插件,支持一系列的分区单处理器预留策略:周期性轮询、不定时轮询和表驱动预留,同时也支持EDF和FP
基于预留的基本工作流程为:在某个核上创建预留区,然后启动一个绑定到此预留区的实时任务。每个预留区在P-RES中有一个预留区ID(RID),这个id在核内唯一,且必须在创建预留区时明确分配。除此之外,在创建预留区和把进程绑定到预留区上时,都要明确指定CPU核
不定时轮询
以不定时轮询为例,创建预留区的命令如下,其中-n指定RID、-t指定调度策略(此处为不定时轮询),-c指定处理器索引
root@ubuntu:/home/szc# resctl -n 123 -t polling-sporadic -c 1
默认情况下,预留区的执行预算为10ms,补给周期为100ms,我们可以通过参数-b和-p分别指定,单位都是ms
root@ubuntu:/home/szc# resctl -n 234 -t polling-sporadic -c 1 -b 25 -p 50
如果要向预留区分配任务,可以使用rtspin的-r参数,后面跟上RID
root@ubuntu:/home/szc# rtspin -v -p 1 -r 234 10 1000 5
任务执行预算不能高于预留区执行预算,任务周期也不能低于预留区的周期,否则均会很可能任务延迟
root@ubuntu:/home/szc# rtspin -v -p 1 -r 234 30 50 0.6
rtspin/10284:1 @ 0.0079ms
release: 2075898649893ns (=2075.90s)
deadline: 2075948649893ns (=2075.95s)
cur time: 2075898673257ns (=2075.90s)
time until deadline: 49.98ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2075948649893ns (=2075.95s)
rtspin/10284:2 @ 53.7610ms
release: 2075948649893ns (=2075.95s)
deadline: 2075998649893ns (=2076.00s)
cur time: 2075952408637ns (=2075.95s)
time until deadline: 46.24ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2075998649893ns (=2076.00s)
rtspin/10284:3 @ 107.0869ms
release: 2075998649893ns (=2076.00s)
deadline: 2076048649893ns (=2076.05s)
cur time: 2076005738009ns (=2076.01s)
time until deadline: 42.91ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2076048649893ns (=2076.05s)
rtspin/10284:4 @ 160.3320ms
release: 2076048649893ns (=2076.05s)
deadline: 2076098649893ns (=2076.10s)
cur time: 2076058979821ns (=2076.06s)
time until deadline: 39.67ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2076098649893ns (=2076.10s)
rtspin/10284:5 @ 212.8220ms
release: 2076098649893ns (=2076.10s)
deadline: 2076148649893ns (=2076.15s)
cur time: 2076111470329ns (=2076.11s)
time until deadline: 37.18ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2076148649893ns (=2076.15s)
rtspin/10284:6 @ 266.5539ms
release: 2076148649893ns (=2076.15s)
deadline: 2076198649893ns (=2076.20s)
cur time: 2076165202267ns (=2076.17s)
time until deadline: 33.45ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2076198649893ns (=2076.20s)
rtspin/10284:7 @ 320.0889ms
release: 2076198649893ns (=2076.20s)
deadline: 2076248649893ns (=2076.25s)
cur time: 2076218736375ns (=2076.22s)
time until deadline: 29.91ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2076248649893ns (=2076.25s)
rtspin/10284:8 @ 373.1570ms
release: 2076248649893ns (=2076.25s)
deadline: 2076298649893ns (=2076.30s)
cur time: 2076271805100ns (=2076.27s)
time until deadline: 26.84ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2076298649893ns (=2076.30s)
rtspin/10284:9 @ 452.1320ms
release: 2076298649893ns (=2076.30s)
deadline: 2076348649893ns (=2076.35s)
cur time: 2076350779961ns (=2076.35s)
time until deadline: -2.13ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2076348649893ns (=2076.35s)
rtspin/10284:10 @ 505.1789ms
release: 2076348649893ns (=2076.35s)
deadline: 2076398649893ns (=2076.40s)
cur time: 2076403826255ns (=2076.40s)
time until deadline: -5.18ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2076398649893ns (=2076.40s)
rtspin/10284:11 @ 558.6228ms
release: 2076398649893ns (=2076.40s)
deadline: 2076448649893ns (=2076.45s)
cur time: 2076457271697ns (=2076.46s)
time until deadline: -8.62ms
target exec. time: 28.50ms (95.00% of WCET)
sleep_next_period() until 2076448649893ns (=2076.45s)
从time until deadline逐渐下降,到最后甚至成了负数,可以发现预留区已经越来越不能应付这一任务了,就是因为任务执行预算大于了预留区的执行预算
再试一下让任务周期小于预留区周期
root@ubuntu:/home/szc# rtspin -v -p 1 -r 234 25 40 5
......
rtspin/10303:17 @ 754.9450ms
release: 2419484294166ns (=2419.48s)
deadline: 2419524294166ns (=2419.52s)
cur time: 2419599238117ns (=2419.60s)
time until deadline: -74.94ms
target exec. time: 23.75ms (95.00% of WCET)
sleep_next_period() until 2419524294166ns (=2419.52s)
rtspin/10303:18 @ 803.4141ms
release: 2419524294166ns (=2419.52s)
deadline: 2419564294166ns (=2419.56s)
cur time: 2419647706968ns (=2419.65s)
time until deadline: -83.41ms
target exec. time: 23.75ms (95.00% of WCET)
sleep_next_period() until 2419564294166ns (=2419.56s)
rtspin/10303:19 @ 852.2091ms
release: 2419564294166ns (=2419.56s)
deadline: 2419604294166ns (=2419.60s)
cur time: 2419696501640ns (=2419.70s)
time until deadline: -92.21ms
target exec. time: 23.75ms (95.00% of WCET)
sleep_next_period() until 2419604294166ns (=2419.60s)
rtspin/10303:20 @ 900.8570ms
release: 2419604294166ns (=2419.60s)
deadline: 2419644294166ns (=2419.64s)
cur time: 2419745150007ns (=2419.75s)
time until deadline: -100.86ms
target exec. time: 23.75ms (95.00% of WCET)
sleep_next_period() until 2419644294166ns (=2419.64s)
rtspin/10303:21 @ 924.6321ms
release: 2419644294166ns (=2419.64s)
deadline: 2419684294166ns (=2419.68s)
cur time: 2419768924507ns (=2419.77s)
time until deadline: -84.63ms
target exec. time: 23.75ms (95.00% of WCET)
sleep_next_period() until 2419684294166ns (=2419.68s)
rtspin/10303:22 @ 973.2361ms
release: 2419684294166ns (=2419.68s)
deadline: 2419724294166ns (=2419.72s)
cur time: 2419817531076ns (=2419.82s)
time until deadline: -93.24ms
target exec. time: 23.75ms (95.00% of WCET)
sleep_next_period() until 2419724294166ns (=2419.72s)
rtspin/10303:23 @ 1021.2801ms
release: 2419724294166ns (=2419.72s)
deadline: 2419764294166ns (=2419.76s)
cur time: 2419865573168ns (=2419.87s)
time until deadline: -101.28ms
target exec. time: 23.75ms (95.00% of WCET)
sleep_next_period() until 2419764294166ns (=2419.76s)
rtspin/10303:24 @ 1069.7651ms
release: 2419764294166ns (=2419.76s)
deadline: 2419804294166ns (=2419.80s)
cur time: 2419914058314ns (=2419.91s)
time until deadline: -109.76ms
target exec. time: 23.75ms (95.00% of WCET)
sleep_next_period() until 2419804294166ns (=2419.80s)
rtspin/10303:25 @ 1118.3450ms
release: 2419804294166ns (=2419.80s)
deadline: 2419844294166ns (=2419.84s)
cur time: 2419962637516ns (=2419.96s)
time until deadline: -118.34ms
target exec. time: 23.75ms (95.00% of WCET)
....
可见任务调度得太快,预留区也承受不住
现在没有用来删除单独分区的命令,我们可以通过改变调度器的方式来删除所有分区
root@ubuntu:/home/szc# setsched Linux
root@ubuntu:/home/szc# setsched P-RES
表驱动预留
在P-RES下,预留区可以被一张周期性、静态定义的调度表调度(一个la ARINC 653时间分区调度算法)。表驱动预留的基本工作流和其他预留区的类似:创建表驱动预留(只是此时要指定一个静态调度算法),给每个表驱动预留绑定任务。
静态调度器有两个参数:主循环周期(M)和执行槽,前者表示调度每多少毫秒进行一次,后者则是一个个不重叠的区间。主循环周期控制预留区多久被调度一次,执行槽则指定在每个调度周期内,预留区的执行区间
下面是创建表驱动的预留区的例子,注意如果单个预留区的执行槽相互重叠,命令行直接报错
root@ubuntu:/home/szc# resctl -n 100 -c 1 -t table-driven -m 200 '[0, 50)' '[100, 150)'
这时一个调度周期内执行槽的分布如下所示
我们可以创建两个表驱动预留分区,调度周期都是200ms,但执行槽的分布不一样
root@ubuntu:/home/szc# resctl -n 100 -c 1 -t table-driven -m 200 '[0, 50)' '[100, 150)'
root@ubuntu:/home/szc# resctl -n 101 -c 1 -t table-driven -m 200 '[50, 100)' '[150, 200)'
此时一个调度周期内,执行槽的分布如下图所示,其中slot1表示预留区1的执行槽,slot2表示预留区2的执行槽
注意,如果多个预留区的执行槽相互重叠,resctl不会报错
另外,多个预留区的调度周期不用一样,但一定要确保调度槽是相互不重叠的。下面是一个预留区调度周期不一样的例子
root@ubuntu:/home/szc# resctl -n 100 -c 1 -t table-driven -m 200 '[0, 50)' '[100, 150)'
root@ubuntu:/home/szc# resctl -n 101 -c 1 -t table-driven -m 100 '[50, 100)'
这个例子的效果和上面第二个例子的小果实一样的,执行槽的分布也如下图所示
但是第三个例子会占用更少的内存空间
当我们要为表驱动预留区绑定任务时,会遵循如下原则:
- 每个预留区都可以绑定多个任务
- 预留区调度任务时,它根据轮询原则从就绪队列中选择下一个任务来执行
- 当没有就绪任务、且有后台任务要执行时,此预留区会让出处理器
我们先在处理器1上创建三个表驱动预留区,调度周期都是200ms,执行槽区间分别是[0, 50) U [100, 150)、[50, 100)和[150, 200)
root@ubuntu:/home/szc# resctl -n 100 -c 1 -t table-driven -m 200 '[0, 50)' '[100, 150)'
root@ubuntu:/home/szc# resctl -n 101 -c 1 -t table-driven -m 200 '[50, 100)'
root@ubuntu:/home/szc# resctl -n 102 -c 1 -t table-driven -m 200 '[150, 200)'
然后创建任务,把任务的pid绑定到100预留区上
root@ubuntu:/home/szc# yes > /dev/null &
[1] 10575
root@ubuntu:/home/szc# resctl -a 10575 -r 100 -c 1
使用top命令查看系统内进程情况,可以看到yes命令占用了一半儿cpu
倘若换个预留区绑定,cpu占用率就变了
协同任务唤醒机制
此机制用来确保被分配给一个表驱动预留区的周期性任务总是在每个执行槽的开始被唤醒。
LitmusRT核用CLOCK_MONOTONIC来表示时间。CLOCK_MONOTONIC意为系统开机时间,也就是系统重启到现在的时间,更改系统时间对它没有影响,系统一旦开机,这个时间显然是单调递增的。可以用linux系统函数clock_nanosleep()来设置任务的休眠时间,以让任务到执行槽的开始时刻被准确唤醒
任务跟踪及可视化
前期准备
我们可以使用feather-trace-tools里的st-trace-schedule来进行任务调度的跟踪及可视化(我已经把feather-trace-tools等添加到环境变量PATH里),不过需要做以下前期工作::
修改源文件
修改默认的feather-trace-tools/sched_trace/draw.py文件,这个文件有bug,要加入一些非None判断。修改后的文件如下所示
from __future__ import division
import sys
import cairo
from math import ceil
from .format import EVENTS
from . import event_time, event_pid, event_cpu, event_job_id
ARROW_LINE_WIDTH = 2 # (pts)
ARROW_WIDTH = 12 # (pts)
ARROW_HEIGHT = 0.8 # (relative)
ALLOC_HEIGHT = 0.5 # (relative)
COLORS = [
(0.0000, 0.1667, 1.0000),
(1.0000, 0.0833, 0.0000),
(0.0417, 1.0000, 0.0000),
(0.9583, 0.0000, 1.0000),
(0.9792, 1.0000, 0.0000),
(0.0000, 1.0000, 0.8958),
(0.3958, 0.0000, 1.0000),
(1.0000, 0.0000, 0.4792),
(0.6042, 1.0000, 0.0000),
(0.0000, 0.7292, 1.0000),
(0.0000, 1.0000, 0.3333),
(1.0000, 0.4583, 0.0000),
(0.0208, 0.0000, 1.0000),
(1.0000, 0.8333, 0.0000),
(0.2083, 0.0000, 1.0000),
(0.7917, 1.0000, 0.0000),
(0.5833, 0.0000, 1.0000),
(0.4167, 1.0000, 0.0000),
(0.7708, 0.0000, 1.0000),
(0.2292, 1.0000, 0.0000),
(1.0000, 0.0000, 0.8542),
(0.0000, 1.0000, 0.1458),
(1.0000, 0.0000, 0.6667),
(0.0000, 1.0000, 0.5208),
(1.0000, 0.0000, 0.2917),
(0.0000, 1.0000, 0.7083),
(1.0000, 0.0000, 0.1042),
(0.0000, 0.9167, 1.0000),
(1.0000, 0.2708, 0.0000),
(0.0000, 0.5417, 1.0000),
(1.0000, 0.6458, 0.0000),
(0.0000, 0.3542, 1.0000)
]
def cpu_color(cpu):
return COLORS[cpu % len(COLORS)]
MAJOR_TICK_FONT_SIZE = 24
TASK_LABEL_FONT_SIZE = 18
TASK_LABEL_COLOR = (0, 0, 0)
TAG_FONT_SIZE = 12
TAG_COLOR = (0, 0, 0)
JOB_ID_FONT_SIZE = 10
GRID_WIDTH = 2
MAJOR_TICK_WIDTH = 2
MINOR_TICK_WIDTH = 1
MAJOR_TICK_COLOR = (0.7, 0.7, 0.7)
MINOR_TICK_COLOR = (0.8, 0.8, 0.8)
GRID_COLOR = (0.7, 0.7, 0.7)
JOB_EVENT_COLOR = (0, 0, 0)
YRES = 100
XLABEL_MARGIN = 72
YLABEL_MARGIN = 200
def center_text(c, x, y, msg):
x_bear, y_bear, width, height, x_adv, y_adv = c.text_extents(msg)
c.move_to(x, y)
c.rel_move_to(-width / 2, height)
c.show_text(msg)
def center_text_above(c, x, y, msg):
x_bear, y_bear, width, height, x_adv, y_adv = c.text_extents(msg)
c.move_to(x, y)
c.rel_move_to(-width / 2, 0)
c.show_text(msg)
def text_left_align_below(c, x, y, msg):
x_bear, y_bear, width, height, x_adv, y_adv = c.text_extents(msg)
c.move_to(x, y)
c.rel_move_to(0, height)
c.show_text(msg)
def text_left_align_above(c, x, y, msg):
c.move_to(x, y)
c.show_text(msg)
def vcenter_right_align_text(c, x, y, msg):
x_bear, y_bear, width, height, x_adv, y_adv = c.text_extents(msg)
c.move_to(x, y)
c.rel_move_to(-width, height/2)
c.show_text(msg)
return y_adv
def render(opts, trace):
if opts.verbose:
print '[II] Identifying relevant tasks and CPUs...',
sys.stdout.flush()
tasks, cores = trace.active_in_interval(opts.start, opts.end)
if opts.verbose:
print '%d tasks, %d cores.' % (len(tasks), len(cores))
# scale is given in points/ms, we need points/ns
xscale = opts.xscale/1E6
yscale = opts.yscale/YRES
if opts.end is not None and opts.start is not None:
width = ceil(opts.margin * 2 + (opts.end - opts.start) * xscale + YLABEL_MARGIN)
else:
width = ceil(opts.margin * 2 + YLABEL_MARGIN)
height = ceil(opts.margin * 2 + (len(tasks) * YRES) * yscale + XLABEL_MARGIN)
if opts.verbose:
print '[II] Canvas size: %dpt x %dpt' % (width, height)
pdf = cairo.PDFSurface(opts.output, width, height)
task_idx = {}
for i, t in enumerate(sorted(tasks)):
task_idx[t] = i
c = cairo.Context(pdf)
c.translate(opts.margin + YLABEL_MARGIN, opts.margin)
# draw box
# c.rectangle(0, 0, width - opts.margin * 2, height - opts.margin * 2)
# c.stroke()
def xpos(x):
if x is not None and opts.start is not None:
return (x - opts.start) * xscale
else:
return 0
def ypos(y):
return (y * YRES) * yscale
# c.scale(xscale, yscale)
if opts.verbose:
print '[II] Drawing grid...',
sys.stdout.flush()
# draw minor tick lines
if opts.minor_ticks:
c.set_source_rgb(*MINOR_TICK_COLOR)
c.set_line_width(MINOR_TICK_WIDTH)
time = 0
if opts.start is not None:
time = opts.start
while time <= opts.end:
x = xpos(time)
y = ypos(len(tasks))
c.new_path()
c.move_to(x, y)
c.line_to(x, 0)
c.stroke()
time += opts.minor_ticks * 1E6
# draw major tick lines
if opts.major_ticks:
c.set_source_rgb(*MAJOR_TICK_COLOR)
c.set_line_width(MAJOR_TICK_WIDTH)
c.set_font_size(MAJOR_TICK_FONT_SIZE)
time = 0
start = 0
if opts.start is not None:
time = opts.start
start = opts.start
while time <= opts.end:
x = xpos(time)
y = ypos(len(tasks) + 0.2)
c.new_path()
c.move_to(x, y)
c.line_to(x, 0)
c.stroke()
y = ypos(len(tasks) + 0.3)
center_text(c, x, y, "%dms" % ((time - start) / 1E6))
time += opts.major_ticks * 1E6
# draw task labels
c.set_font_size(TASK_LABEL_FONT_SIZE)
c.set_source_rgb(*TASK_LABEL_COLOR)
for pid in tasks:
x = -24
y = ypos(task_idx[pid] + 0.25)
vcenter_right_align_text(c, x, y, "%s/%d" % (trace.task_names[pid], pid))
y = ypos(task_idx[pid] + 0.75)
vcenter_right_align_text(c, x, y,
"(%.2fms, %.2fms)" % (trace.task_wcets[pid] / 1E6,
trace.task_periods[pid] / 1E6))
if opts.verbose:
print 'done.'
print '[II] Drawing CPU allocations...',
sys.stdout.flush()
# raw allocations
box_height = ALLOC_HEIGHT * YRES * yscale
c.set_font_size(TAG_FONT_SIZE)
for (to, away) in trace.scheduling_intervals_in_range(opts.start, opts.end):
delta = (event_time(away) - event_time(to)) * xscale
pid = event_pid(to)
y = ypos(task_idx[pid] + (1 - ALLOC_HEIGHT))
x = xpos(event_time(to))
c.new_path()
c.rectangle(x, y, delta, box_height)
c.set_source_rgb(*cpu_color(event_cpu(to)))
c.fill()
c.set_source_rgb(*TAG_COLOR)
text_left_align_below(c, x + 2, y + 2, '%d' % event_cpu(to))
# draw task base lines
c.set_source_rgb(*GRID_COLOR)
c.set_line_width(GRID_WIDTH)
for t in tasks:
c.new_path()
y = ypos(task_idx[t] + 1)
x = xpos(opts.start)
c.move_to(x, y)
x = xpos(opts.end)
c.line_to(x, y)
c.stroke()
if opts.verbose:
print 'done.'
print '[II] Drawing releases and deadlines...',
sys.stdout.flush()
# draw releases and deadlines
c.set_source_rgb(*JOB_EVENT_COLOR)
c.set_line_width(ARROW_LINE_WIDTH)
# c.set_line_cap(cairo.LINE_JOIN_ROUND)
c.set_line_join(cairo.LINE_JOIN_ROUND)
c.set_font_size(JOB_ID_FONT_SIZE)
arrow_width = ARROW_WIDTH / 2
arrow_height = ARROW_HEIGHT * YRES * yscale
for rec in trace.events_in_range_of_type(opts.start, opts.end, 'ST_RELEASE'):
pid = event_pid(rec)
y = ypos(task_idx[pid] + 1)
rel = xpos(event_time(rec))
c.new_path()
c.move_to(rel, y - (GRID_WIDTH - 1))
c.rel_line_to(0, -arrow_height)
c.rel_line_to(-arrow_width, arrow_width)
c.rel_move_to(arrow_width, -arrow_width)
c.rel_line_to(arrow_width, arrow_width)
c.stroke()
if opts.show_job_ids:
text_left_align_above(c, rel + 4, y - arrow_height - 4,
"%d" % event_job_id(rec))
dl = xpos(rec[-1])
c.new_path()
c.move_to(dl, y - arrow_height - (GRID_WIDTH - 1))
c.rel_line_to(0, arrow_height)
c.rel_line_to(-arrow_width, -arrow_width)
c.rel_move_to(arrow_width, arrow_width)
c.rel_line_to(arrow_width, -arrow_width)
c.stroke()
if opts.verbose:
print 'done.'
print '[II] Drawing job completions...',
sys.stdout.flush()
# draw job completions
for rec in trace.events_in_range_of_type(opts.start, opts.end, 'ST_COMPLETION'):
pid = event_pid(rec)
y = ypos(task_idx[pid] + 1)
x = xpos(event_time(rec))
c.new_path()
c.move_to(x, y - (GRID_WIDTH - 1))
c.rel_line_to(0, -arrow_height)
c.rel_move_to(-arrow_width, 0)
c.rel_line_to(2 * arrow_width, 0)
c.stroke()
if opts.show_job_ids:
text_left_align_above(c, x + 4, y - arrow_height - 4,
"%d" % event_job_id(rec))
if opts.verbose:
print 'done.'
print '[II] Drawing job suspensions...',
sys.stdout.flush()
# draw job suspensions
c.set_line_width(1)
for rec in trace.events_in_range_of_type(opts.start, opts.end, 'ST_BLOCK'):
pid = event_pid(rec)
y = ypos(task_idx[pid] + (1 - ALLOC_HEIGHT) + 0.5 * ALLOC_HEIGHT)
y1 = ypos(task_idx[pid] + (1 - ALLOC_HEIGHT) + 0.4 * ALLOC_HEIGHT)
y2 = ypos(task_idx[pid] + (1 - ALLOC_HEIGHT) + 0.6 * ALLOC_HEIGHT)
x = xpos(event_time(rec))
c.new_path()
c.move_to(x, y1)
c.line_to(x, y2)
c.line_to(x - arrow_width, y)
c.line_to(x, y1)
c.close_path()
c.stroke()
if opts.verbose:
print 'done.'
print '[II] Drawing job wake-ups...',
sys.stdout.flush()
# draw job suspensions
c.set_line_width(1)
for rec in trace.events_in_range_of_type(opts.start, opts.end, 'ST_RESUME'):
pid = event_pid(rec)
y = ypos(task_idx[pid] + (1 - ALLOC_HEIGHT) + 0.5 * ALLOC_HEIGHT)
y1 = ypos(task_idx[pid] + (1 - ALLOC_HEIGHT) + 0.4 * ALLOC_HEIGHT)
y2 = ypos(task_idx[pid] + (1 - ALLOC_HEIGHT) + 0.6 * ALLOC_HEIGHT)
x = xpos(event_time(rec))
c.new_path()
c.move_to(x, y1)
c.line_to(x, y2)
c.line_to(x + arrow_width, y)
c.line_to(x, y1)
c.close_path()
c.stroke()
if opts.verbose:
print 'done.'
print '[II] Finishing PDF...',
sys.stdout.flush()
pdf.finish()
if opts.verbose:
print 'done.'
if opts.verbose:
print '[II] Flushing PDF...',
sys.stdout.flush()
pdf.flush()
if opts.verbose:
print 'done.'
del pdf
安装库
安装python-cairo
root@ubuntu:/home/szc# apt-get install python-cairo
这样就可以使用st-trace-schedule了
使用
root@ubuntu:/home/szc# st-trace-schedule my-trace
CPU 0: 26466 > schedule_host=ubuntu_scheduler=P-FP_trace=my-trace_cpu=0.bin [0]
CPU 1: 26468 > schedule_host=ubuntu_scheduler=P-FP_trace=my-trace_cpu=1.bin [0]
CPU 2: 26470 > schedule_host=ubuntu_scheduler=P-FP_trace=my-trace_cpu=2.bin [0]
CPU 3: 26472 > schedule_host=ubuntu_scheduler=P-FP_trace=my-trace_cpu=3.bin [0]
Press Enter to end tracing...
然后另开一个终端,设置一些并发任务,然后启动
root@ubuntu:/home/szc/litmus# rtspin -w -v -p 3 -q 2 10 100 0.1 &
[1] 26484
root@ubuntu:/home/szc/litmus# rtspin -w -v -p 3 -q 3 10 100 0.1 &
[2] 26485
root@ubuntu:/home/szc/litmus# release_ts && wait
Synchronous release at time 34711000.00ms.
Released 2 real-time tasks.
rtspin/26484:3 @ 0.0010ms
release: 34711000000000ns (=34711.00s)
deadline: 34711100000000ns (=34711.10s)
cur time: 34711000205496ns (=34711.00s)
time until deadline: 99.79ms
target exec. time: 9.50ms (95.00% of WCET)
sleep_next_period() until 34711100000000ns (=34711.10s)
rtspin/26485:3 @ 0.0000ms
release: 34711000000000ns (=34711.00s)
deadline: 34711100000000ns (=34711.10s)
cur time: 34711009764630ns (=34711.01s)
time until deadline: 90.24ms
target exec. time: 9.50ms (95.00% of WCET)
sleep_next_period() until 34711100000000ns (=34711.10s)
rtspin/26485:4 @ 90.8608ms
release: 34711100000000ns (=34711.10s)
deadline: 34711200000000ns (=34711.20s)
cur time: 34711100642502ns (=34711.10s)
time until deadline: 99.36ms
target exec. time: 9.50ms (95.00% of WCET)
sleep_next_period() until 34711200000000ns (=34711.20s)
[1]- Done rtspin -w -v -p 3 -q 2 10 100 0.1
[2]+ Done rtspin -w -v -p 3 -q 3 10 100 0.1
任务结束后,回到st-trace-schedule终端,按下enter键停止跟踪
会看到当前目录下四个bin文件已经有两个有内容了,运行命令生成PDF文件
root@ubuntu:/home/szc# st-draw *.bin
会看到当前目录下出现了个pdf
下载到windows下打开它即可
下面是一个全局优先级固定的例子
root@ubuntu:/home/szc/litmus# rtspin -w 10 100 5 &
[1] 26543
root@ubuntu:/home/szc/litmus# rtspin -w 20 50 5 &
[2] 26546
root@ubuntu:/home/szc/litmus# rtspin -w 5 30 5 &
[3] 26547
root@ubuntu:/home/szc/litmus# rtspin -w 5 20 5 &
[4] 26548
root@ubuntu:/home/szc/litmus# release_ts && wait
生成的pdf如下图所示(部分)
我们可以使用另一个工具st-job-stats来查看记录文件里的内容
root@ubuntu:/home/szc# st-job-stats *GSN-EDF*.bin | head
# Task, Job, Period, Response, DL Miss?, Lateness, Tardiness, Forced?, ACET, Preemptions, Migrations
# task NAME=rtspin PID=27122 COST=10000000 PERIOD=100000000 CPU=0
27122, 2, 100000000, 2310, 0, -99997690, 0, 0, 1120, 0, 0
27122, 3, 100000000, 10200168, 0, -89799832, 0, 0, 9503666, 0, 0
27122, 4, 100000000, 9786927, 0, -90213073, 0, 0, 9505061, 0, 0
27122, 5, 100000000, 9741232, 0, -90258768, 0, 0, 9508143, 0, 0
27122, 6, 100000000, 9994662, 0, -90005338, 0, 0, 9506501, 0, 0
27122, 7, 100000000, 10034041, 0, -89965959, 0, 0, 9503288, 0, 0
27122, 8, 100000000, 9777269, 0, -90222731, 0, 0, 9501092, 0, 0
27122, 9, 100000000, 9840609, 0, -90159391, 0, 0, 9507407, 0, 0
各列字段含义如下表所示
字段 | 含义 |
Task | 任务的pid |
Job | 任务的执行次数(索引) |
Period | 任务周期,精确到纳秒 |
Response | 响应时间,当前任务完成时间,精确到纳秒 |
DL Miss | 是否错过截止时间,0为否 |
Lateness | 超过截止的时间,精确到纳秒 |
Tardiness | max(0, Lateness), 延迟时间 |
Forced | 是否使用预算执行机制。此标志只有在基于进程的调度器中才可能为非0,超过预算时间,就强行结束任务 |
ACET | 任务执行时间,精确到纳秒 |
Preemptions | 未知 |
Migrations | 未知 |
相关时间由于精确度太高,linux又是伪实时系统,所以会产生毫秒级别的误差
liblitmus
liblitmus提供了用来和litmusRT交互的C语言api,以构造自定义的实时任务,这些api被包含在头文件litmus.h中,因此如果要使用liblitmus的API,则必须包含头文件
#include <litmus.h>
以下是两个例子:
周期任务
litmusRT调用可能在运行时失败,因此强烈推荐进行错误检查,如下所示
#define CALL(exp) do { \
int ret; \
ret = exp; \
if (ret != 0) { \
fprintf(stderr, "%s failed.\n",); \
} else { \
fprintf(stderr, "%s ok.\n", #exp); \
} \
} while(0)
下面就是我们的周期性任务,很简单,就是累加一个全局计数器的值,直到计数器>=10
int i = 0;
int job() {
i++;
if (i >= 10) {
return 1;
}
return 0;
}
在main函数中,rt_task结构体类型变量param会保存这个内核任务的所有信息
int main() {
int do_exit;
struct rt_task params;
然后,我们必须先调用init_litmus()函数来初始化liblitmus库
CALL(init_litmus());
然后,我们给params变量填充一些任务参数,例如调度周期、每个周期内的截止时间、任务执行预算
#define PERIOD ms2ns(1000)
#define DEADLINE ms2ns(1000)
#define EXEC_COST ms2ns(50)
...
init_rt_task_param(¶ms);
params.exec_cost = EXEC_COST;
params.period = PERIOD;
params.relative_deadline = DEADLINE;
而后把这些参数提交给内核
CALL(set_rt_task_param(gettid(), ¶ms));
任务默认是litmusRT中的一个后台进程,我们需要通过使用task_mode()函数把它们转换成实时任务
CALL(task_mode(LITMUS_RT_TASK)); // 传入1也可以
我们可能还希望等待一个开始信号,这一点可以通过调用wait_for_ts_release()函数实现
CALL(wait_for_ts_release());
然后我们就可以写主循环了
do {
sleep_next_period();
do_exit = job();
} while(!do_exit);
其中的sleep_next_period()函数负责确保job函数在每个调度周期内只被调用一次,而后的job()函数则是业务逻辑,返回值不同表示执行情况不同。这里,返回值为1表示计数器加到了10,要退出了。
然后,再调用一次task_mode来把任务切回到后台任务,最后退出程序即可
CALL(task_mode(BACKGROUND_TASK)); // 传入0也可以
return 0;
}
事件任务
事件驱动任务的代码和周期任务类似,只是有三点区别:
其一,在主循环里,不用调sleep_next_period()了,因为既然不是周期任务,就不存在每个周期只执行一次的情况
do {
do_exit = job();
} while(!do_exit);
其二,业务逻辑也变了,我们这里要执行的逻辑是从标准输入读取字符
int job() {
int ret;
char buffer[80];
ret = read(STDIN_FILENO, buffer, sizeof(buffer));
buffer[ret] = '\0';
if (buffer[ret - 1] == '\n') {
buffer[ret - 1] = '\0';
}
if (strcmp(buffer, "exit")) {
return 1;
} else {
printf("%s\n", buffer);
return 0;
}
}
其三,要加入头文件
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
编译
编译文件MakeFile,要把收尾的makefile路径改成自己的
-include make.conf
LIBLITMUS ?= /home/szc/litmus/liblitmus
include ${LIBLITMUS}/inc/config.makefile
vpath %.c src/
.PHONY: all clean
all: ${all}
clean:
rm -f ${all} *.o *.d
obj-example_periodic = example_periodic.o
period_task: ${obj-example_periodic}
obj-example_event = example_event.o
event_task: ${obj-example_event}
include ${LIBLITMUS}/inc/depend.makefile
然后编译
root@ubuntu:/home/szc/real_task_test# make period_task -f MakeFile
root@ubuntu:/home/szc/real_task_test# make event_task -f MakeFile
运行
运行前,把调度器设置成GSN-EDF(不能是Linux、P-FP或者P-RES)
root@ubuntu:/home/szc# setsched GSN-EDF
再运行例子,先尝试period_task
再开一个terminal,释放同步信号
此刻,周期任务也开始运行了,结果如下
再测试一下事件任务(注意,此任务在xshell远程工具下不能正常运行,必须到虚拟机内部才可以!),创建一个fifo文件,再用tail往里面追加内容,得到的输出送入事件任务中
另起一个终端,往input中输入两个字符串后,发送同步信号,就会看到我们的输入
最后再输入两次exit,退出任务。第一次退出job()函数,第二次是等待输入事件触发任务,使其继续运行main函数,终止自己
Feature-Trace
Feature-Trace可以用来跟踪处理各种各样的系统负载,包括调度负载、调度后的处理负载、上下文切换负载、任务启动延迟、同步负载、重调度IPI负载。
以下是一个例子,包括采样、分析、对比实验结果。
采样
我们使用ft-trace-overheads来追踪一些负载情况
root@ubuntu:/home/szc/litmus/ft-demo# ft-trace-overheads my-trace
[II] Recording /dev/litmus/ft_cpu_trace0 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin
[II] Recording /dev/litmus/ft_cpu_trace1 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=1.bin
[II] Recording /dev/litmus/ft_cpu_trace2 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=2.bin
[II] Recording /dev/litmus/ft_cpu_trace3 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=3.bin
[II] Recording /dev/litmus/ft_msg_trace0 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=0.bin
[II] Recording /dev/litmus/ft_msg_trace1 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=1.bin
[II] Recording /dev/litmus/ft_msg_trace2 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=2.bin
[II] Recording /dev/litmus/ft_msg_trace3 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=3.bin
Press Enter to end tracing...
而后另起一个终端,设置调度器为全局的EDF,设置四个任务,而后启动之
root@ubuntu:/home/szc# setsched GSN-EDF
root@ubuntu:/home/szc# rtspin -w 10 100 5 &
[1] 12638
root@ubuntu:/home/szc# rtspin -w 10 100 5 &
[2] 12639
root@ubuntu:/home/szc# rtspin -w 10 100 5 &
[3] 12640
root@ubuntu:/home/szc# rtspin -w 10 100 5 &
[4] 12641
root@ubuntu:/home/szc# release_ts
Synchronous release at time 31404000.00ms.
Released 4 real-time tasks.
等大概四五秒钟,回到ft-trace-overheads终端,按下回车停止追踪
root@ubuntu:/home/szc/litmus/ft-demo# ft-trace-overheads my-trace
[II] Recording /dev/litmus/ft_cpu_trace0 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin
[II] Recording /dev/litmus/ft_cpu_trace1 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=1.bin
[II] Recording /dev/litmus/ft_cpu_trace2 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=2.bin
[II] Recording /dev/litmus/ft_cpu_trace3 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=3.bin
[II] Recording /dev/litmus/ft_msg_trace0 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=0.bin
[II] Recording /dev/litmus/ft_msg_trace1 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=1.bin
[II] Recording /dev/litmus/ft_msg_trace2 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=2.bin
[II] Recording /dev/litmus/ft_msg_trace3 -> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=3.bin
Press Enter to end tracing...
Ending Trace...
# 下面是新出现的日志
Disabling 4 events.
Disabling 18 events.
Disabling 4 events.
Disabling 18 events.
Disabling 4 events.
Disabling 18 events.
Disabling 18 events.
Disabling 4 events.
/dev/litmus/ft_cpu_trace0: 4582880 bytes read.
/dev/litmus/ft_cpu_trace3: 3499648 bytes read.
/dev/litmus/ft_msg_trace0: 1120 bytes read.
/dev/litmus/ft_msg_trace2: 1248 bytes read.
/dev/litmus/ft_msg_trace3: 1216 bytes read.
/dev/litmus/ft_cpu_trace2: 5651136 bytes read.
/dev/litmus/ft_cpu_trace1: 4959072 bytes read.
/dev/litmus/ft_msg_trace1: 1280 bytes read.
并查看输出文件
root@ubuntu:/home/szc/litmus/ft-demo# ls *.bin
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=1.bin
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=2.bin
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=3.bin
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=0.bin
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=1.bin
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=2.bin
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=3.bin
样本采样已经完成,接下来就要对它们进行分析了,比如排序、提取、统计
分析
排序
使用的工具是ft-sort-traces,示例命令如下:
其中2>&1表示把标准错误重定向到标准输出,然后tee -a表示从标准输入追加内容到后面的lod文件,|表示管道,前面的输出自然就成了后面的输入。此命令输出如下:
root@ubuntu:/home/szc/litmus/ft-demo# ft-sort-traces overheads_*.bin 2>&1 | tee -a overhead-processing.log
[1/8] Sorting overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin
Total : 286430
Holes : 0
Reordered : 0
Non-monotonic : 0
Seq. constraint : 0
Implausible : 0
Size : 4.37 Mb
Time : 0.00 s
Throughput : 3237.07 Mb/s
[2/8] Sorting overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=1.bin
Total : 309942
Holes : 0
Reordered : 0
Non-monotonic : 0
Seq. constraint : 0
Implausible : 0
Size : 4.73 Mb
Time : 0.01 s
Throughput : 614.53 Mb/s
[3/8] Sorting overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=2.bin
Total : 353196
Holes : 0
Reordered : 0
Non-monotonic : 0
Seq. constraint : 0
Implausible : 0
Size : 5.39 Mb
Time : 0.00 s
Throughput : 3292.24 Mb/s
[4/8] Sorting overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=3.bin
Total : 218728
Holes : 0
Reordered : 0
Non-monotonic : 0
Seq. constraint : 0
Implausible : 0
Size : 3.34 Mb
Time : 0.00 s
Throughput : 3084.75 Mb/s
[5/8] Sorting overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=0.bin
Total : 70
Holes : 0
Reordered : 0
Non-monotonic : 0
Seq. constraint : 0
Implausible : 0
Size : 0.00 Mb
Time : 0.00 s
Throughput : 65.88 Mb/s
[6/8] Sorting overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=1.bin
Total : 80
Holes : 0
Reordered : 0
Non-monotonic : 0
Seq. constraint : 0
Implausible : 0
Size : 0.00 Mb
Time : 0.00 s
Throughput : 86.78 Mb/s
[7/8] Sorting overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=2.bin
Total : 78
Holes : 0
Reordered : 0
Non-monotonic : 0
Seq. constraint : 0
Implausible : 0
Size : 0.00 Mb
Time : 0.00 s
Throughput : 92.44 Mb/s
[8/8] Sorting overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_msg=3.bin
Total : 76
Holes : 0
Reordered : 0
Non-monotonic : 0
Seq. constraint : 0
Implausible : 0
Size : 0.00 Mb
Time : 0.00 s
Throughput : 88.44 Mb/s
排序依据是:按照任务的定义顺序(索引)进行输出,先输出全部的cpu负载,再输出全部的trace负载
提取
使用ft-extract-samples工具,示例命令如下
root@ubuntu:/home/szc/litmus/ft-demo# ft-extract-samples overheads_*.bin 2>&1 | tee -a overhead-processing_sample.log
[1/8] Extracting samples from overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin -> CXS RELEASE RELEASE_LATENCY SCHED SCHED2
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin CXS >> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0_overhead=CXS.float32
Generating binary, NumPy-compatible output.
Total : 286430
Skipped : 3
Avoided : 0
Complete : 51
Incomplete : 21
Non RT : 46482
Interleaved : 0
Interrupted : 113
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin RELEASE >> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0_overhead=RELEASE.float32
Generating binary, NumPy-compatible output.
Total : 286430
Skipped : 79
Avoided : 0
Complete : 2922
Incomplete : 0
Non RT : 0
Interleaved : 0
Interrupted : 0
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin RELEASE_LATENCY >> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0_overhead=RELEASE-LATENCY.float32
Generating binary, NumPy-compatible output.
Total : 286430
Skipped : 0
Avoided : 0
Complete : 15
Incomplete : 0
Non RT : 0
Interleaved : 0
Interrupted : 0
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin SCHED >> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0_overhead=SCHED.float32
Generating binary, NumPy-compatible output.
Total : 286430
Skipped : 1
Avoided : 0
Complete : 52
Incomplete : 0
Non RT : 46716
Interleaved : 0
Interrupted : 40
overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0.bin SCHED2 >> overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_cpu=0_overhead=SCHED2.float32
Generating binary, NumPy-compatible output.
Total : 286430
Skipped : 5
Avoided : 0
Complete : 52
Incomplete : 0
Non RT : 46686
Interleaved : 0
Interrupted : 49
..................
会为每个任务输出.float32格式的文件,这种文件兼容numpy,因此可以通过内存映射来达到比csv文件更快的数据处理效果。输出的数据类型有执行时间、启动时间、启动延迟、调度、调度后等,以便下一步进行统计
统计
统计使用的是ft-compute-stats命令,但要先安装python-numpy
root@ubuntu:/home/szc/litmus/ft-demo# apt-get install python-numpy
然后进行统计,输出到某个csv文件中
root@ubuntu:/home/szc/litmus/ft-demo# ft-compute-stats overheads_*.float32 > stats.csv
文件内容如下所示:
这些列包括:调度插件、cpu核、负载类型、负载计算单元、单位、任务、样本数、最大值、九成九九的值、九成九的值、九成五的值、均值、中值、最小值、标准差、方差、float32源文件
比较多个实验结果
如果要比较多个实验的结果,我们可以进行这些操作:合并数据文件、统计每种类型记录的事件数、混洗和剪裁所有的样本文件
合并数据文件
我们可以使用ft-combine-samples对基于键值命名的文件进行合并
root@ubuntu:/home/szc/litmus/ft-demo# ft-combine-samples --std overheads_*.float32 2>&1 | tee -a combined.log
其中std参数表示把所有的任务序列号和cpu索引进行合并,仅保留指标类型,因此以上命令输出如下
root@ubuntu:/home/szc/litmus/ft-demo# ll combine*
-rw-r--r-- 1 root root 5037 Sep 25 17:17 combined.log
-rw-r--r-- 1 root root 0 Sep 25 17:17 combined-overheads_.float32
-rw-r--r-- 1 root root 836 Sep 25 17:17 combined-overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_overhead=CXS.float32
-rw-r--r-- 1 root root 32140 Sep 25 17:17 combined-overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_overhead=RELEASE.float32
-rw-r--r-- 1 root root 208 Sep 25 17:17 combined-overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_overhead=RELEASE-LATENCY.float32
-rw-r--r-- 1 root root 864 Sep 25 17:17 combined-overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_overhead=SCHED2.float32
-rw-r--r-- 1 root root 864 Sep 25 17:17 combined-overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_overhead=SCHED.float32
-rw-r--r-- 1 root root 580 Sep 25 17:17 combined-overheads_host=ubuntu_scheduler=P-RES_trace=my-trace_overhead=SEND-RESCHED.float32
把float32文件根据指标类型(CXS、RELEASE等)进行分类,生成新的float32文件
统计每种类型记录的事件数
我们可以使用ft-count-samples工具对合并好的文件进行统计,然后生成counts.csv
root@ubuntu:/home/szc/litmus/ft-demo# ft-count-samples combined-overheads_*.float32 > counts.csv
输出的counts.csv文件内容如下
记录了每种事件的发生数量
混洗和剪裁所有的样本文件
我们可以使用ft-select-samples工具对所有的trace数据随机选择等量的样本
root@ubuntu:/home/szc/litmus/ft-demo# ft-select-samples counts.csv combined-overheads_*.float32 2>&1 | tee -a select_samples.log
得到的select_samples.log内容如下
root@rtlab-computer:/home/rtlab/szc/litmus/ft-demo# cat select_samples.log
SCHED2 -> 216
[1/1] combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=SCHED2.float32Not shuffling combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=SCHED2.float32.Storing combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=SCHED2.sf32.
RELEASE -> 22041
[1/1] combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=RELEASE.float32Not shuffling combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=RELEASE.float32.Storing combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=RELEASE.sf32.
CXS -> 212
[1/1] combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=CXS.float32Not shuffling combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=CXS.float32.Storing combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=CXS.sf32.
RELEASE-LATENCY -> 52
[1/1] combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=RELEASE-LATENCY.float32Not shuffling combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=RELEASE-LATENCY.float32.Storing combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=RELEASE-LATENCY.sf32.
SCHED -> 216
[1/1] combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=SCHED.float32Not shuffling combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=SCHED.float32.Storing combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=SCHED.sf32.
SEND-RESCHED -> 150
[1/1] combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=SEND-RESCHED.float32Not shuffling combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=SEND-RESCHED.float32.Storing combined-overheads_host=rtlab-computer_scheduler=Linux_trace=my-trace_overhead=SEND-RESCHED.sf32.
结语
参考文章:官方实验手册《LITMUSRT: A Hands-On Primer》