Linux核心add_timer在一个jiffy的分辨率的可靠性?

Linux核心add_timer在一个jiffy的分辨率的可靠性?,第1张

概述Linux核心add_timer在一个jiffy的分辨率可靠性

在下面给出的代码中,有一个简单的linux内核模块(驱动程序),它使用分辨率为1 jiffy的add_timer (也就是计划在jiffIEs + 1激发计时器)重复调用函数10次。 使用bash脚本rerun.sh ,然后从syslog打印输出中获得时间戳,并使用gnuplot可视化它们。

在大多数情况下,我得到这样的syslog输出:

[ 7103.055787] Init testjiffy: 0 ; HZ: 250 ; 1/HZ (ms): 4 [ 7103.056044] testjiffy_timer_function: runcount 1 [ 7103.060045] testjiffy_timer_function: runcount 2 [ 7103.064052] testjiffy_timer_function: runcount 3 [ 7103.068050] testjiffy_timer_function: runcount 4 [ 7103.072053] testjiffy_timer_function: runcount 5 [ 7103.076036] testjiffy_timer_function: runcount 6 [ 7103.080044] testjiffy_timer_function: runcount 7 [ 7103.084044] testjiffy_timer_function: runcount 8 [ 7103.088060] testjiffy_timer_function: runcount 9 [ 7103.092059] testjiffy_timer_function: runcount 10 [ 7104.095429] Exit testjiffy

…与时间序列和Delta直方图的结果如下所示:

在windows上安装定时器/时钟ISR – 在单线程环境中进行asynchronous调用

networking协议中的定时器

HR定时器精确度研究案例

高精度的事件计时器

定时器C在beagleboard-xm上进行armtrong编程

这基本上就是我期望的代码质量。

然而 – 每过一段时间,我都会得到如下的捕捉:

[ 7121.377507] Init testjiffy: 0 ; HZ: 250 ; 1/HZ (ms): 4 [ 7121.380049] testjiffy_timer_function: runcount 1 [ 7121.384062] testjiffy_timer_function: runcount 2 [ 7121.392053] testjiffy_timer_function: runcount 3 [ 7121.396055] testjiffy_timer_function: runcount 4 [ 7121.400068] testjiffy_timer_function: runcount 5 [ 7121.404085] testjiffy_timer_function: runcount 6 [ 7121.408084] testjiffy_timer_function: runcount 7 [ 7121.412072] testjiffy_timer_function: runcount 8 [ 7121.416083] testjiffy_timer_function: runcount 9 [ 7121.420066] testjiffy_timer_function: runcount 10 [ 7122.417325] Exit testjiffy

…这样的渲染结果如下:

…我就像:“WHOOOOOAAAAAA …等一下…” – 是不是有一个从序列中删除的脉冲? 这add_timer 错过了一个插槽,然后在下一个4 ms插槽中启动该函数?

有趣的是,在运行这些testing时,除了terminal,网页浏览器和文本编辑器外,我没有别的东西,所以我不能看到任何运行的东西,这可能会影响OS /内核。 所以我真的不明白为什么内核会这么大的一个错过(整个瞬间)。 当我读到关于linux内核时序的信息时,例如“ 所有定时器中最简单和最不准确的定时器API ”,我把“最不精确”读为:“不要期待精确到 4毫秒”(按照这个例如) – 我没有,我很好,(第一)直方图中显示的方差; 但我不指望整个时期会被遗漏!?

所以我的问题是:

这个分辨率是否是add_timer预期行为(偶尔会错过一段时间)?

如果是这样的话,有没有办法“强制” add_timer在每个4ms插槽触发该函数,就像这个平台上的jiffy指定的那样?

是否有可能得到一个“错误”的时间戳 – 例如时间戳,反映实际的“打印”到系统日志发生的时间,而不是实际上被激发的function?

请注意,我没有find一个时间分辨率低于jiffy(在这种情况下,4ms); 当代码正常工作时,我也不希望减less增量差异。 所以就我所见,我没有“高分辨率计时器”的要求,也没有“硬实时”的要求 – 我只是想让add_timer可靠地启动。 在这个平台上可以做到这一点,而不需要内核的特殊“实时”configuration?

奖金的问题:在rerun.sh下面,你会注意到两个标有MUSTHAVE sleep ;如果其中任何一个被忽略/评论,OS /内核冻结 ,并且需要硬重启。而我不明白为什么 – 在bash的insmod之后运行rmmod是否真的有可能如此之快 ,以至于与正常的模块加载/卸载过程相冲突?

平台信息:

$ cat /proc/cpuinfo | grep "processor|model name|MHz|cores" processor : 0 # (same for 1) model name : Intel(R) Atom(TM) cpu N450 @ 1.66GHz cpu MHz : 1000.000 cpu cores : 1 $ echo $(cat /etc/issue ; uname -a) Ubuntu 11.04 n l linux mypc 2.6.38-16-generic #67-Ubuntu SMP Thu Sep 6 18:00:43 UTC 2012 i686 i686 i386 GNU/linux $ echo $(lsb_release -a 2>/dev/null | tr 'n' ' ') distributor ID: Ubuntu Description: Ubuntu 11.04 Release: 11.04 Codename: natty

码:

$ cd /tmp/testjiffy $ ls Makefile rerun.sh testjiffy.c

Makefile :

obj-m += testjiffy.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

testjiffy.c :

/* * [http://www.tldp.org/LDP/lkmpg/2.6/HTML/lkmpg.HTML#AEN189 The linux Kernel Module Programming GuIDe] */ #include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_INFO */ #include <linux/init.h> /* Needed for the macros */ #include <linux/jiffIEs.h> #include <linux/time.h> #define MAXRUNS 10 static volatile int runcount = 0; static struct timer_List my_timer; static voID testjiffy_timer_function(unsigned long data) { int tdelay = 100; runcount++; if (runcount == 5) { while (tdelay > 0) { tdelay--; } // small delay } printk(KERN_INFO " %s: runcount %d n",__func__,runcount); if (runcount < MAXRUNS) { my_timer.expires = jiffIEs + 1; add_timer(&my_timer); } } static int __init testjiffy_init(voID) { printk(KERN_INFO "Init testjiffy: %d ; HZ: %d ; 1/HZ (ms): %dn",runcount,HZ,1000/HZ); init_timer(&my_timer); my_timer.function = testjiffy_timer_function; //my_timer.data = (unsigned long) runcount; my_timer.expires = jiffIEs + 1; add_timer(&my_timer); return 0; } static voID __exit testjiffy_exit(voID) { printk(KERN_INFO "Exit testjiffyn"); } module_init(testjiffy_init); module_exit(testjiffy_exit); MODulE_liCENSE("GPL");

rerun.sh :

#!/usr/bin/env bash set -x make clean make # blank syslog first sudo bash -c 'echo "0" > /var/log/syslog' sleep 1 # MUSTHAVE 01! # reload kernel module/driver sudo insmod ./testjiffy.ko sleep 1 # MUSTHAVE 02! sudo rmmod testjiffy set +x # copy & process syslog max=0; for ix in _testjiffy_*.syslog; do aa=${ix#_testjiffy_}; ab=${aa%.syslog} ; case $ab in *[!0-9]*) ab=0;; # reset if non-digit obtained; else *) ab=$(echo $ab | bc);; # remove leading zeroes (else octal) esac if (( $ab > $max )) ; then max=$((ab)); fi; done; newm=$( printf "%05d" $(($max+1)) ); PLPROC='chomp $_; if (!$p) {$p=0;}; if (!$f) {$f=$_;} else { $a=$_-$f; $d=$a-$p; print "$a $dn" ; $p=$a; };' set -x grep "testjiffy" /var/log/syslog | cut -d' ' -f7- > _testjiffy_${newm}.syslog grep "testjiffy_timer_function" _testjiffy_${newm}.syslog | sed 's/[(.*)].*/1/' | perl -ne "$PLPROC" > _testjiffy_${newm}.dat set +x cat > _testjiffy_${newm}.gp <<EOF set terminal pngcairo Font 'Arial,10' size 900,500 set output '_testjiffy_${newm}.png' set style line 1 linetype 1 linewidth 3 pointtype 3 linecolor rgb "red" set multiplot layout 1,2 Title "_testjiffy_${newm}.syslog" set xtics rotate by -45 set Title "Time positions" set yrange [0:1.5] set offsets graph 50e-3,1e-3,0 plot '_testjiffy_${newm}.dat' using 1:(1.0):xtic(gprintf("%.3se%s",$1)) noTitle with points ls 1,'_testjiffy_${newm}.dat' using 1:(1.0) with impulses ls 1 binwIDth=0.05e-3 set BoxwIDth binwIDth bin(x,wIDth)=wIDth*floor(x/wIDth) + wIDth/2.0 set Title "Delta diff histogram" set style fill solID 0.5 set autoscale xy set offsets graph 0.1e-3,0.1e-3,0.1,0.1 plot '_testjiffy_${newm}.dat' using (bin($2,binwIDth)):(1.0) smooth freq with Boxes ls 1 unset multiplot EOF set -x; gnuplot _testjiffy_${newm}.gp ; set +x

编辑: 由@granquet这个评论的动机 ,我试图从/proc/schedstat和/proc/sched_deBUG ,通过使用dd通过call_usermodehelper获得调度统计; 请注意,大部分时间都是“跳过”(即,由于函数的第7次,第6次或第X次运行而丢失的文件)。 但我设法获得了两个完整的运行,并将其发布到https://gist.github.com/anonymous/5709699 (因为我注意到在SO上可能优先考虑pastebin),因为输出有点大。 *_11*文件logging一个正确的运行, *_17*文件用“drop”logging一个运行。

注意我也切换到模块中的mod_timer_pinned ,并没有什么帮助(使用这个函数的模块获得了主要日志)。 这些是testjiffy.c中的变化:

#include <linux/kmod.h> // usermode-helper API ... char fcmd[] = "of=/tmp/testjiffy_sched00"; char *dd1argv[] = { "/bin/dd","if=/proc/schedstat","oflag=append","conv=notrunc",&fcmd[0],NulL }; char *dd2argv[] = { "/bin/dd","if=/proc/sched_deBUG",NulL }; static char *envp[] = { "HOME=/","TERM=linux","PATH=/sbin:/bin:/usr/sbin:/usr/bin",NulL }; static voID testjiffy_timer_function(unsigned long data) { int tdelay = 100; unsigned long tjNow; runcount++; if (runcount == 5) { while (tdelay > 0) { tdelay--; } // small delay } printk(KERN_INFO " %s: runcount %d n",runcount); if (runcount < MAXRUNS) { mod_timer_pinned(&my_timer,jiffIEs + 1); tjNow = jiffIEs; printk(KERN_INFO " testjiffy expires: %lu - jiffIEs %lu => %lu / %lun",my_timer.expires,tjNow,my_timer.expires-tjNow,jiffIEs); sprintf(fcmd,"of=/tmp/testjiffy_sched%02d",runcount); call_usermodehelper( dd1argv[0],dd1argv,envp,UMH_NO_WAIT ); call_usermodehelper( dd2argv[0],dd2argv,UMH_NO_WAIT ); } }

…在rerun.sh :

... set +x for ix in /tmp/testjiffy_sched*; do echo $ix | tee -a _testjiffy_${newm}.sched cat $ix >> _testjiffy_${newm}.sched done set -x ; sudo rm /tmp/testjiffy_sched* ; set +x cat > _testjiffy_${newm}.gp <<EOF ...

我会用这个post进行详细的回复。

@CL。 :非常感谢答案。 很好,它确认这是“可能您的计时器function被称为稍后jiffy”; 通过loggingjiffIEs,我也意识到定时器函数在稍后被调用 – 除此之外,它本身没有任何“错误”。

很高兴知道时间戳; 我想知道是否有可能:定时器的function在正确的时间,但内核抢占内核日志服务(我相信这是klogd ),所以我得到一个延迟的时间戳? 然而,我试图创build一个“循环”(或更确切地说,定期的)定时器函数来写硬件,我首先注意到这个“下降”,实现了PC不以一定的时间间隔在USB总线上写数据; 并考虑到时间戳确认行为,这可能不是这里的问题(我猜)。

我已经修改了定时器函数,所以它相对于最后一个定时器( my_timer.expires )的预定时间触发 – 再次通过mod_timer_pinned而不是add_timer :

static voID testjiffy_timer_function(unsigned long data) { int tdelay = 100; unsigned long tjlast; unsigned long tjNow; runcount++; if (runcount == 5) { while (tdelay > 0) { tdelay--; } // small delay } printk(KERN_INFO " %s: runcount %d n",runcount); if (runcount < MAXRUNS) { tjlast = my_timer.expires; mod_timer_pinned(&my_timer,tjlast + 1); tjNow = jiffIEs; printk(KERN_INFO " testjiffy expires: %lu - jiffIEs %lu => %lu / %lu last: %lun",jiffIEs,tjlast); } }

…和前几个尝试,它无可挑剔的作品 – 但是,最终,我得到这个:

[13389.775508] Init testjiffy: 0 ; HZ: 250 ; 1/HZ (ms): 4 [13389.776051] testjiffy_timer_function: runcount 1 [13389.776063] testjiffy expires: 3272445 - jiffIEs 3272444 => 1 / 3272444 last: 3272444 [13389.780053] testjiffy_timer_function: runcount 2 [13389.780068] testjiffy expires: 3272446 - jiffIEs 3272445 => 1 / 3272445 last: 3272445 [13389.788054] testjiffy_timer_function: runcount 3 [13389.788073] testjiffy expires: 3272447 - jiffIEs 3272447 => 0 / 3272447 last: 3272446 [13389.788090] testjiffy_timer_function: runcount 4 [13389.788096] testjiffy expires: 3272448 - jiffIEs 3272447 => 1 / 3272447 last: 3272447 [13389.792070] testjiffy_timer_function: runcount 5 [13389.792091] testjiffy expires: 3272449 - jiffIEs 3272448 => 1 / 3272448 last: 3272448 [13389.796044] testjiffy_timer_function: runcount 6 [13389.796062] testjiffy expires: 3272450 - jiffIEs 3272449 => 1 / 3272449 last: 3272449 [13389.800053] testjiffy_timer_function: runcount 7 [13389.800063] testjiffy expires: 3272451 - jiffIEs 3272450 => 1 / 3272450 last: 3272450 [13389.804056] testjiffy_timer_function: runcount 8 [13389.804072] testjiffy expires: 3272452 - jiffIEs 3272451 => 1 / 3272451 last: 3272451 [13389.808045] testjiffy_timer_function: runcount 9 [13389.808057] testjiffy expires: 3272453 - jiffIEs 3272452 => 1 / 3272452 last: 3272452 [13389.812054] testjiffy_timer_function: runcount 10 [13390.815415] Exit testjiffy

…这样呈现:

…所以,基本上我有一个延迟/“下降”在+ 8ms插槽(应该是@ 3272446 jiffIEs),然后有两个函数运行在+ 12ms插槽(这将是@ 3272447 jiffIEs); 您甚至可以将情节上的标签看成“更加粗体”。 这样更好一些,就是现在“drop”序列和一个正确的非drop序列是同步的(正如你所说的:“为了避免一个晚的定时器函数改变所有跟随的定时器的调用”),然而,我仍然错过一个节拍; 因为我必须在每个节拍上写硬件字节,所以我保持一个持续的,恒定的传输速率,这不幸的是没有多大的帮助。

至于另一个build议,“使用十个定时器” – 因为我的终极目标(使用定期的lo-res定时器function写硬件); 我首先认为它不适用 – 但是如果没有别的可能(除了做一些特殊的实时内核准备),那么我一定会尝试一个计划,我有10(或N)个计时器(可能存储在一个数组)。

编辑:只join剩余的相关评论:

USB传输既可以预先安排(等时),也可以没有时间保证(asynchronous)。 如果你的设备没有使用同步传输,那么这是错误的devise。 – CL。 6月5日10:47

感谢评论@CL。 – “…提前预定(等时)…”清除了我的困惑。 我(最终)针对FT232,只有BulK模式 – 只要每个计时器的字节数低,我实际上可以“骗”我的方式通过add_timer“stream”数据; 但是,当我将接近消耗带宽的字节数转移时,这些“失火”开始变得明显,如下降。 所以我有兴趣testing的限制,为此我需要一个可靠的重复“定时器”function – 有什么我可以尝试有一个可靠的“计时器”? – sdaau 6月5日12:27

批量传输不适合stream式传输。 通过使用另一种软件定时器,您无法修复硬件协议中的缺陷。 – CL。 6月5日13:50

…作为我对@CL的回应。 :我知道我不能解决缺点; 我对观察这些缺点更感兴趣 – 比方说,如果内核函数定期进行USB写入,我可以观察示波器/分析器上的信号,并希望看到大容量模式不适合的情况。 但首先,我不得不相信函数可以(至less有些)以一个定期的速度可靠地重复(即“生成”一个时钟/滴答) – 直到现在,我还不知道,我不能真正相信在jiffIEs分辨率的add_timer (因为它是相对容易跳过一整段时间)。 然而,从这个意义上说,转向linux的高分辨率定时器( hrtimer )确实给了我一个可靠的周期性函数 – 所以我想这解决了我的问题(发表在我的答案下面 )。

我如何得到无滴答的内核工作? nohz_full,rcu_nocbs,isolcpus还有什么?

什么时候使用gethrvtime()代替gethrtime()更合适?

C Unix毫秒计时器返回0的差异

带有timerfd的epoll

在windows上运行高分辨率计时器(毫秒精度)

非常感谢所有的意见和答案; 他们都指出了必须考虑的事情 – 但是,由于我是一个永远的小白菜,我还是需要多做一些阅读,才能获得一些理解(我希望是正确的)。 另外,我也找不到定期“滴答”功能的具体内容 – 所以我会在这里发布一个更详细的答案。

简而言之 – 对于jiffy分辨率的可靠的周期性linux内核函数,不要使用add_timer ( <linux/time.h> ),因为它可能会“丢弃”整个周期; 请使用高分辨率计时器( <linux/hrtimer.h> )。 更详细地说:

是否有可能得到一个“错误”的时间戳 – …?

@CL。 :日志中的时间戳是将该字符串打印到日志中的时间。

所以,也许这是可能的 – 但事实证明,这不是问题在这里:

这个分辨率是否是add_timer的预期行为(偶尔会错过一段时间)?

我想,事实证明 – 是的:

如果是这样的话,有没有办法“强制”add_timer在每个4ms插槽触发该函数,就像这个平台上的jiffy指定的那样?

…和(我想再次),事实证明 – 不。

现在,其原因有些微妙 – 我希望如果我没有把它们弄对,有人会纠正我。 首先,我有第一个误解,就是“时钟只是一个时钟”(即:即使它是以计算机代码的形式实现的) – 但这不是很正确。 内核基本上必须在某处“排队”一个“事件”,每次使用add_timer东西; 并且这个请求可能来自任何事情:来自任何(所有)排序的驱动程序,甚至可能的用户空间。

问题在于这种“排队”的成本 – 因为除了内核必须处理(相当于)遍历和插入(以及删除)数组中的项目之外,还必须处理跨越几个数量级的定时器延迟说毫秒到10秒); 事实上,一些驱动程序(显然是网络协议)明显地排队了许多计时器事件,这些事件通常在运行之前被取消 – 而其他类型的驱动器可能需要一个完全不同的行为,你预计大部分时间,这个事件通常不会被取消;而且你也一个接一个地排列事件) 。 最重要的是,内核需要处理这个单处理器与SMP与多处理器平台。 因此,在内核中实现定时器处理涉及到一个成本 – 收益折衷。

事实证明,围绕jiffIEs / add_timer的架构是为了处理最常见的设备而设计的 – 对于他们来说,精确的解析速度并不是问题; 但是这也意味着用这种方法不能期望有一个可靠的定时器来解析单个jiffy。 内核通过对待这些“事件队列”(有点像中断服务请求(IRQ))来处理这一事实,这也加剧了这一点。 并且在内核的IRQ处理中有几个优先级,其中较高优先级的例程可以抢先一个较低优先级的例程(即:即使正在执行的中断和挂起较低优先级的例程,允许更高优先级的例行公事)。 或者,如前所述:

@granquet :计时器在软irq上下文中运行,这意味着它们具有最高的优先级,并且它们抢占了在cpu上运行/运行的所有内容…但是在服务一个软irq时未被禁用的硬件中断。 所以你可能(最有可能的解释)在这里和那里得到一个硬件中断抢占你的计时器…因此你得到一个中断,在正确的时间没有服务。

@CL。 :确实有可能你的定时器函数被调用的时间晚于什么设置。 可能的原因是调度延迟,中断时间太长的其他驱动程序(图形和WLAN驱动程序通常是罪魁祸首),或一些蹩脚的BIOS执行SMI代码。

我现在也这样想 – 我想这可能是发生什么的一个例证:

jiffIEs改变,比如10000(== 40000 ms @ 250 Hz)

假设定时器函数(由add_timer排队)即将开始运行 – 但尚未开始运行

我们这里说,网卡产生(不管什么原因)一个硬件中断

具有较高优先级的硬件中断触发内核预占(停止和暂停)定时器功能(可能现在开始,只有几条指令);

这意味着内核现在必须重新计划定时器函数,稍后才能运行 – 由于只能在内核中使用整数运算,并且此类事件的时间分辨率在jiffIEs中 – 最好能够重新计划它为jiffIEs + 1(10001 == 40004 ms @ 250 Hz)

现在内核将上下文切换到网卡驱动程序的IRQ服务例程,并开始其业务

假设IRQ服务程序在200微秒内完成 – 这意味着现在我们(以“绝对”的方式)在40000.2毫秒 – 但是,我们也仍然在10000微秒

如果内核现在将上下文切换回定时器功能,那么应该已经完成​​ – 没有必要注意到延迟;

…但是,这不会发生,因为定时器功能是为下一个jiffy!

所以内核会在接下来的大约3.8毫秒时间内处理业务(可能正在休眠)

jiffIEs更改为10001(== 40004 ms @ 250 Hz)

(先前重新安排的)定时器功能运行 – 这一次完成没有中断

我还没有真正做过详细的分析,看看事件的顺序是否完全如上所述; 但我相当相信这是一个很接近的东西 – 换句话说,解决问题 – 特别是因为高分辨率定时器方法似乎没有表现出这种行为。 获得一个调度程序日志, 确切地知道发生了什么事情会引起先发制人是很棒的 – 但是我怀疑用户空间的往返,我试图在OP编辑中回应@granquet的评论,正确的事情。

无论如何,回到这个:

请注意,我没有找到一个时间分辨率低于jiffy(在这种情况下,4ms); 当代码正常工作时,我也不希望减少增量差异。 所以就我所见,我没有“高分辨率定时器”的要求,也没有“硬实时”的要求…

这是我犯的一个错误 – 正如上面的分析所显示的,我确实有“高分辨率”的要求! 而且,如果我早些意识到,我可能会早点发现相关的阅读材料。 无论如何,一些相关的文档 – 即使他们没有具体讨论定期功能 – 对我来说,是:

LDD3:5.3。 信号量和互斥量 (在描述一个具有不同要求的驱动程序时): “ 不能从中断处理程序或其他异步上下文访问,没有特殊的延迟(响应时间)要求; 应用程序员理解I / O请求通常不会马上满足 “

documentation / timers / hrtimers.txt – “ timers.c代码是非常”紧密地编码“在jiffIEs和32位的假设,并已经磨练和微型优化的相对狭窄的用例(jiffIEs在一个相对狭窄的HZ范围)多年 – 因此,即使是小的扩展,它很容易打破轮概念 “

T. Gleixner,D. NIEhaus Hrtimers and Beyond:转换linux时间子系统(pdf) – (最详细的,另见图内) “ 1997年实现的级联定时轮(CTW)链表来解决链表的O(N)插入时间的可伸缩性问题…目前linux中定时器管理的方法在满足极其广泛的需求方面做得很好,但是它不能提供所需的服务质量在某些情况下,恰恰是因为它必须满足如此广泛的要求…超时相关的定时器保存在现有的定时器轮和一个新的子系统优化(高分辨率)定时器要求hrtimers实施。hrtimers完全基于人时间(单位:纳秒)…它们保存在按cpu列表排序的时间中,并以红黑树的形式实现。

高分辨率计时器API [LWN.net] – “ 在其核心,hrtimer机制保持不变,而不是使用”计时轮“数据结构,时间表生活在一个时间排序的链表上,下一个计时器到期在列表的头部,另外一个单独的红/黑树也可以在不扫描列表的情况下插入和移除定时器事件,但是在核心保持不变的情况下,至少其他所有东西都改变了,至少表面上“。

软件中断和实时 处理 – “ softirq机制意味着处理与处理硬件中断几乎一样重要的处理,但Softirq运行在高优先级(尽管有一个有趣的例外,描述下面),但是硬件中断被启用,因此它们通常会抢占除了对“真正的”硬件中断的响应之外的任何工作…然而,从3.0实时补丁集开始,该能力消失了……作为响应, 3.6.1-rt1,softirqs的处理已经改变了。 “

高分辨率超时(但不是太高)[LWN.net] – “_poll()和epoll_wait()以毫秒为整数; select()以微秒分辨率以结构timeval,ppoll()和pselect )采用纳秒分辨率的结构timespec,但它们都是相同的,因为它们把这个超时值转换成jiffIEs,最大分辨率在1到10毫秒之间,程序员可以编程一个10纳秒的pselect()调用超时,但是即使在没有争用cpu的情况下,调用也许不会返回到10毫秒之后……这是一个非常有用的功能,但是这是以一些重要的API变化为代价的。

从引用中可以清楚地看出,内核中的高分辨率定时设备仍然处于积极的发展状态(API发生了变化),我担心,也许我不得不安装一个特殊的“实时补丁”内核。 值得庆幸的是,我的2.6.38-16 SMP内核中的高分辨率定时器看起来是可用的(并且正在工作),没有任何特殊的改变。 下面是修改后的testjiffIEs.c内核模块的列表,该模块现在使用高分辨率定时器,但是保持与jiffIEs所确定的相同的周期。 为了测试,我做了200次循环(而不是10次)。 并运行rerun.sh脚本20-30次,这是我得到的最糟糕的结果:

现在的时间序列显然是不可读的,但直方图仍然可以告诉我们:对于最大偏差,取0.00435-0.004(= 0.004-0.00365)= 350微秒,它仅表示100 *(350/4000)= 8.75%预期期间 我当然没有问题。 此外,我从来没有下降(或相应的,整个2 *周期= 8毫秒的延迟),或0毫秒的延迟 – 我得到的捕获,否则质量显示在OP中的第一个图像。 现在,我当然可以进行更长时间的测试,更准确地看到它的可靠性 – 但这是我期望/需要看到的这个简单情况下的所有可靠性; 相比之下,在OP中,我只能在10个循环中下降,抛掷硬币的概率 – 每rerun.sh脚本的第二次或第三次运行,我会得到一个下降 – 即使在低OS资源使用情况!

最后,请注意,下面的代码应该有问题,由@CL发现。 :“ 你的模块是越野车:你必须确保计时器在模块卸载之前没有挂起 ”,固定的(在hrtimer的上下文中)。 这似乎回答了我的奖金问题,因为它避免了在rerun.sh脚本中的任何一个“MUSTHAVE” sleep s的rerun.sh 。 但是,请注意,200循环@ 4毫秒需要0.8秒 – 如果我们想要完整的200刻度捕捉(否则,在我的机器上,我只能捕获约7个刻度),需要insmod和rmmod之间的sleep 。

那么,希望我现在得到这个(至少大部分如果) – 如果没有,更正欢迎:)

testjiffy(-hr).c

#include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_INFO */ #include <linux/init.h> /* Needed for the macros */ #include <linux/jiffIEs.h> #include <linux/time.h> #define MAXRUNS 200 #include <linux/hrtimer.h> static volatile int runcount = 0; //~ static struct timer_List my_timer; static unsigned long period_ms; static unsigned long period_ns; static ktime_t ktime_period_ns; static struct hrtimer my_hrtimer; //~ static voID testjiffy_timer_function(unsigned long data) static enum hrtimer_restart testjiffy_timer_function(struct hrtimer *timer) { int tdelay = 100; unsigned long tjNow; ktime_t kt_Now; int ret_overrun; runcount++; if (runcount == 5) { while (tdelay > 0) { tdelay--; } // small delay } printk(KERN_INFO " %s: runcount %d n",runcount); if (runcount < MAXRUNS) { tjNow = jiffIEs; kt_Now = hrtimer_cb_get_time(&my_hrtimer); ret_overrun = hrtimer_forward(&my_hrtimer,kt_Now,ktime_period_ns); printk(KERN_INFO " testjiffy jiffIEs %lu ; ret: %d ; ktnsec: %lld n",ret_overrun,ktime_to_ns(kt_Now)); return HRTIMER_RESTART; } else return HRTIMER_norESTART; } static int __init testjiffy_init(voID) { struct timespec tp_hr_res; period_ms = 1000/HZ; hrtimer_get_res(CLOCK_MONOTONIC,&tp_hr_res); printk(KERN_INFO "Init testjiffy: %d ; HZ: %d ; 1/HZ (ms): %ld ; hrres: %lld.%.9ldn",period_ms,(long long)tp_hr_res.tv_sec,tp_hr_res.tv_nsec ); hrtimer_init(&my_hrtimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL); my_hrtimer.function = &testjiffy_timer_function; period_ns = period_ms*( (unsigned long)1E6L ); ktime_period_ns = ktime_set(0,period_ns); hrtimer_start(&my_hrtimer,ktime_period_ns,HRTIMER_MODE_REL); return 0; } static voID __exit testjiffy_exit(voID) { int ret_cancel = 0; while( hrtimer_callback_running(&my_hrtimer) ) { ret_canceL++; } if (ret_cancel != 0) { printk(KERN_INFO " testjiffy Waited for hrtimer callback to finish (%d)n",ret_cancel); } if (hrtimer_active(&my_hrtimer) != 0) { ret_cancel = hrtimer_cancel(&my_hrtimer); printk(KERN_INFO " testjiffy active hrtimer cancelled: %d (%d)n",ret_cancel,runcount); } if (hrtimer_is_queued(&my_hrtimer) != 0) { ret_cancel = hrtimer_cancel(&my_hrtimer); printk(KERN_INFO " testjiffy queued hrtimer cancelled: %d (%d)n",runcount); } printk(KERN_INFO "Exit testjiffyn"); } module_init(testjiffy_init); module_exit(testjiffy_exit); MODulE_liCENSE("GPL");

It is indeed possible that your timer function gets called at a later jiffy than what expires what set to. Possible reasons are scheduling delays,other drivers that disable interrupts for too long (graphics and WLAN drivers are usual culprits),or some crappy BIOS executing SMI code.

If you want to avoID that one late timer function shifts all following timer calls,you have to schedule the respective next timer not relative to the current time ( jiffIEs ),but relative to the scheduled time of the last timer ( my_timer.expires ). Alternatively,use ten timers that you all schedule at the beginning at jiffIEs + 1,2,3,…

The timestamp in the log is the time when that string was printed to the log.

Your module is BUGgy: you must ensure that the timer is not pending before the module is unloaded.

总结

以上是内存溢出为你收集整理的Linux核心add_timer在一个jiffy的分辨率的可靠性?全部内容,希望文章能够帮你解决Linux核心add_timer在一个jiffy的分辨率的可靠性?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1159471.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-01
下一篇 2022-06-01

发表评论

登录后才能评论

评论列表(0条)

保存