Solaris 使用基于计时单元的 CPU 记帐(其中系统时钟中断会按固定时间间隔触发)并将 CPU 的使用情况归结到计时单元时间运行的线程和进程。以下示例说明如何使用 tick 探测器来观察这一归结情况:
# dtrace -n sched:::tick'{@[stringof(args[1]->pr_fname)] = count()}' ^C arch 1 sh 1 sed 1 echo 1 ls 1 FvwmAuto 1 pwd 1 awk 2 basename 2 expr 2 resize 2 tput 2 uname 2 fsflush 2 dirname 4 vim 9 fvwm2 10 ksh 19 xterm 21 Xsun 93 MozillaFirebird 260 |
系统时钟频率随操作系统不同而变化,但通常会在 25 赫兹到 1024 赫兹之间。Solaris 系统时钟频率是可调整的,但缺省值为 100 赫兹。
仅当系统时钟检测到可运行线程时,tick 探测器才会触发。要使用 tick 探测器来观察系统时钟的频率,必须具有始终可运行的线程。在一个窗口中创建循环 shell,如下例所示:
$ while true ; do let i=0 ; done |
在另一个窗口中运行以下脚本:
uint64_t last[int]; sched:::tick /last[cpu]/ { @[cpu] = min(timestamp - last[cpu]); } sched:::tick { last[cpu] = timestamp; }
# dtrace -s ./ticktime.d dtrace: script './ticktime.d' matched 2 probes ^C 0 9883789 |
最短时间间隔为 9.8 毫秒,它指示缺省时钟周期频率为 10 毫秒(100 赫兹)。因为抖动,所以观察到的最短时间间隔略小于 10 毫秒。
基于计时单元的记帐的一个缺点是,执行记帐的系统时钟通常还负责分派任何与时间有关的调度活动。因此,如果线程要在每个时钟周期(即每 10 毫秒)执行一定量的工作,则根据记帐是在与时间有关的分派调度活动之前还是之后完成,系统会对线程的工作量记帐过多或过少。在 Solaris 中,记帐是在与时间有关的分派之前完成的。因此,系统会对定期运行的线程工作量记帐过少。如果这类线程运行的时间小于时钟周期的时间间隔,它们可以有效地“隐藏”在时钟周期中。以下示例显示系统中这类线程的情况:
sched:::tick, sched:::enqueue { @[probename] = lquantize((timestamp / 1000000) % 10, 0, 10); }
示例脚本的输出在 10 毫秒时间间隔内分为两个毫秒偏移部分,一个对应于 tick 探测器,另一个对应于 enqueue:
# dtrace -s ./tick.d dtrace: script './tick.d' matched 4 probes ^C tick value -------------- Distribution ------------ count 6 | 0 7 |@ 3 8 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 79 9 | 0 enqueue value -------------- Distribution ------------ count < 0 | 0 0 |@@ 267 1 |@@ 300 2 |@@ 259 3 |@@ 291 4 |@@@ 360 5 |@@ 305 6 |@@ 295 7 |@@@@ 522 8 |@@@@@@@@@@@@ 1315 9 |@@@ 337 |
名为 tick 的输出直方图显示时钟周期在 8 毫秒偏移处触发。如果调度与时钟周期完全无关,enqueue 的输出将平均分布在 10 毫秒时间间隔内。但是,输出显示同一 8 毫秒偏移处有一个峰值,指示系统中至少有一些线程是根据时间调度的。