首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Linux频率调整影响计时器精度

Linux频率调整影响计时器精度
EN

Stack Overflow用户
提问于 2018-02-08 10:56:52
回答 1查看 630关注 0票数 3

在一台基于Linux驱动的单核嵌入式Cortex-A8机器上,我遇到了timerfd遇到的问题:我需要每隔几毫秒触发一些IO,到目前为止,我创建的计时器一切都很好:

代码语言:javascript
复制
int _timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
int _flags = 0;
itimerspec _new_timer;
_new_timer.it_interval.tv_sec = interval / 1000000;
_new_timer.it_interval.tv_nsec = (interval % 1000000) * 1000;
_new_timer.it_value.tv_sec = _new_timer.it_interval.tv_sec;
_new_timer.it_value.tv_nsec = _new_timer.it_interval.tv_nsec;
timerfd_settime(_timer_fd, _flags, &_new_timer, NULL);

。。以及文件描述符上的select()

CPU在默认情况下运行在800 The,并且可以缩小到300 The。即使在最低频率下,即使在高系统负载和重IO的情况下,定时器也会定期触发。

现在有个问题:当我将CPU频率调控器设置为ondemand时,计时器会在切换频率时忽略的几秒唤醒(高达2800 is )。

我所说的IO包括上传大型文件(网络IO、提取/CPU、写入闪存)。仅仅创建/提取一个大型存档似乎并不是一个问题。

我修改了这个方便的小Python脚本,它使用timerfd来打印每100 to的CPU频率和时间差,我可以重现这个问题!运行test.py并启动上传(繁重的IO)将为我提供以下输出:

代码语言:javascript
复制
f=300000 t=0.100021, count=01 *
f=600000 t=0.099609, count=01 *                    <== switch, but no problem
f=600000 t=0.099989, count=01 *
f=300000 t=0.100388, count=01 *                    <== switch, but no problem
f=300000 t=0.099874, count=01 *
f=300000 t=0.099944, count=01 *
f=300000 t=0.100000, count=01 *
f=600000 t=0.099615, count=01 *                    <== switch, but no problem
f=600000 t=0.100033, count=01 *
f=600000 t=0.099958, count=01 *
f=600000 t=0.100003, count=01 *                    <== IO starts
f=600000 t=0.100062, count=01 *
f=600000 t=0.100318, count=01 *
f=800000 t=0.418505, count=04 ****                 <== 3 misses
f=800000 t=0.081735, count=01 *
f=800000 t=0.100019, count=01 *
f=800000 t=0.099284, count=01 *
f=800000 t=0.100584, count=01 *
f=800000 t=0.100089, count=01 *
f=800000 t=0.099623, count=01 *
f=720000 t=1.854099, count=18 ******************   <== 17 misses
f=720000 t=0.046591, count=01 *
f=720000 t=0.099038, count=01 *
f=720000 t=0.100744, count=01 *
f=720000 t=0.099240, count=01 *
f=720000 t=0.100029, count=01 *
f=720000 t=0.099985, count=01 *
f=720000 t=0.100007, count=01 *
f=800000 t=2.715434, count=27 ***************************  <== 26 misses
f=800000 t=0.085148, count=01 *
f=800000 t=0.099992, count=01 *
f=800000 t=0.099648, count=01 *
f=800000 t=0.100367, count=01 *
f=800000 t=0.099406, count=01 *
f=800000 t=0.099984, count=01 *
f=720000 t=2.446585, count=24 ************************  <== 23 misses
f=720000 t=0.054219, count=01 *
f=720000 t=0.099947, count=01 *
f=720000 t=0.099284, count=01 *
f=720000 t=0.100721, count=01 *
f=720000 t=0.099975, count=01 *
f=720000 t=0.100089, count=01 *
f=800000 t=2.391552, count=23 ***********************  <== 22 misses
f=800000 t=0.015058, count=01 *
f=800000 t=0.092592, count=01 *
f=800000 t=0.100651, count=01 *
f=800000 t=0.099982, count=01 *
f=800000 t=0.099967, count=01 *

我尝试了答案,它建议设置我的过程的优先级,但没有效果。

以下是我目前的结论:

  • 这个问题不是由我的C程序引起的,因为我可以用一个Python脚本来再现它。
  • CPU的性能不是问题,因为将频率固定到300 the可以很好地工作。
  • 产生重负荷的过程必须满足某些要求(见下文)--仅仅执行网络IO或CPU密集操作就无法工作。
  • 计时器间隙似乎只有当gpg进程得到某些数据时才会出现。

,所以我的问题是:我需要一个精确的计时器,间隔大约10 ms (几毫秒抖动就可以了)。我能用timerfd实现这一点吗?我有什么选择?

使用的内核版本为4.4.19 (OpenEmbedded/Yocto)

复制

目前,我不知道有其他方法来再现所描述的行为,只知道以下几点:

  • 在具有网络接入的嵌入式设备上,有nginx安装了proxy_passing端口80到其他端口,例如8081
  • 在设备上运行receive.py,该设备将侦听POST请求,接收一个大文件并将其输送到GnuPG
  • 在设备上运行test.py以观察CPU频率和计时器精度。
  • 将cpu调控器设置为ondemandecho ondemand > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
  • 使用另一台机器上的upload.py向嵌入式系统发送一个10M文件,文件内容随机
  • 上传数据的内容似乎很重要!upload.py <ip/hostname> 10000000将生成一个随机字节流,并将其存储到一个名为data-out的文件中,然后再对其进行POST操作--在大多数情况下,您将不会看到计时器间隙--如果您能够观察到它们,您可以保存该文件并在以后重用它。
  • 从嵌入式设备上运行upload.py (没有网络)或遗漏nginx将无法工作!

文件

这是test.py的修改版本,它生成上面的输出 导入异步核心,时间,timerfd.async类TestDispatcher(timerfd.async.dispatcher):def __init__(self,*args):.__init__(*args) self._last_t = time.time() def handle_expire(self,*args)):t= time.time() f=time.time print("f=%s t=%.6f,count=%0.2d %s“%)% (f,t- self._last_t,count,self._last_t =t dispatcher = TestDispatcher(timerfd.CLOCK_MONOTONIC) dispatcher.settime(0,timerfd.itimerspec(0.1,1)) asyncore.loop() receive.py 导入子进程,http.server,socketserver类http.server def do_POST(self):gpg_process = subprocess.Popen( 'gpg',‘-homedir’,‘/home/root/..gnupg’,'-u','Name','-d',stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE) tar_process = subprocess.Popen( 'tar','-C','.','-xzf','-',stdin=gpg_process.stdout,stderr=subprocess.PIPE) content_length =int(Sel.Header‘content_length’)而content_length > 0: content_length -= gpg_process.stdin.write( self.rfile.read(最小1000,(Content_length)) gpg_process.stdin.close() self.send_response(201) self.end_headers() socketserver.TCPServer.allow_reuse_address = True socketserver.TCPServer(‘’,8081),InstallationHandler).serve_forever() upload.py -提供要上载的文件名或生成的字节数。 导入http.client,sys,os if os.path.exists( sys.argv2):print('read. %r‘%sys.argv2)b= open(sys.argv2,'rb').read() .read()sys.argv2:print(“生成随机数据.”)B= os.urandom(int(sys.argv2))打开(‘data-out’,'wb').write(b) b=字节( b)打印(‘size=%d’% len(b)) h= http.client.HTTPConnection(sys.argv1) h.request('POST',‘/上载/校准_data’,b)打印(h.getresponse().read()

EN

回答 1

Stack Overflow用户

发布于 2018-02-10 05:37:55

初步答复。让我们假设您不希望禁用cpufreq或进行任何其他可能导致功耗更改的侵入性内核配置更改。

让我假设抖动不是来自cpu时钟和计时器时钟之间的某种奇怪的交互,这是很难消除的。

让我们也假设你愿意破门而入。那样的话..。使用你自己的硬件计时器!

ARM SoC通常有许多硬件定时器,而Linux通常只消耗其中的两个:一个用于给定时器(即timerfd和其他计时器接口)提供数据,另一个用于计时。这意味着您通常有许多空闲和可用的硬件定时器。

不幸的是,Linux没有提供任何框架或接口来使用它们,所以您必须做自己的事情。例如,这里,有一个MIPS SoC AR9331的例子。

为ARM SoC做这件事仅仅是读取数据表、检查寄存器,或者修改这个示例,或者想出自己的解决方案。

抖动要小得多,因为它是一个硬件定时器,产生中断,因此不受常规负载的影响。

如果你想要更少的抖动,你可以尝试快速中断(FIQ)。Bootlin (前自由电子)在他们的博客上解释了这个可怕的技巧。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48683823

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档