我需要对1 us水平进行精确计时,以便改变pwm波的占空比。
背景
根据/ proc / cpuinfo,我正在使用Gumstix Over Water COM(https://www.gumstix.com/store/app.php/products/265/),它有一个运行在499.92 BogoMIPS的单核ARM Cortex-A8处理器(Gumstix页面声称高达1Ghz,推荐800Mhz) 。操作系统是基于内核版本2.6.34的Linux的Angstrom Image版本,它在Gumstix Water COM上有库存。
问题
我已经对Linux中的精确计时做了大量的阅读(并且已经尝试了大部分计划)并且共识似乎是使用clock_gettime()和引用CLOCK_MONOTONIC是最好的方法。 (我本来希望使用RDTSC寄存器进行定时,因为我有一个具有最小省电能力的内核但这不是Intel处理器。)所以这里是奇数部分,而clock_getres()返回1,表明分辨率为1 ns实际的时序测试表明,最小分辨率为30517ns或(它不能重合)恰好是32.768KHz时钟周期之间的时间。这就是我的意思:
// Stackoverflow example
#include <stdio.h>
#include <time.h>
#define SEC2NANOSEC 1000000000
int main( int argc, const char* argv[] )
{
// //////////////// Min resolution test //////////////////////
struct timespec resStart, resEnd, ts;
ts.tv_sec = 0; // s
ts.tv_nsec = 1; // ns
int iters = 100;
double resTime,sum = 0;
int i;
for (i = 0; i<iters; i++)
{
clock_gettime(CLOCK_MONOTONIC, &resStart); // start timer
// clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts);
clock_gettime(CLOCK_MONOTONIC, &resEnd); // end timer
resTime = ((double)resEnd.tv_sec*SEC2NANOSEC + (double)resEnd.tv_nsec
- ((double)resStart.tv_sec*SEC2NANOSEC + (double)resStart.tv_nsec);
sum = sum + resTime;
printf("resTime = %f\n",resTime);
}
printf("Average = %f\n",sum/(double)iters);
}
(不要担心双重投射,时间t中的tv_sec和tv_nsec很长。)
编译:
gcc soExample.c -o runSOExample -lrt
使用以下命令运行:
./runSOExample
如图所示,nanosleep被注释掉,结果为0ns或30517ns,大部分为0ns。这使我相信CLOCK_MONOTONIC在32.768kHz处更新,并且大多数时间在第二次clock_gettime()调用之前尚未更新时钟,并且在结果为30517ns的情况下,时钟已在呼叫之间更新。
当我在我的开发计算机上执行相同操作(AMD FX(tm)-6100六核处理器,运行频率为1.4 GHz)时,最小延迟为149-151ns,没有零。
所以,让我们将这些结果与CPU速度进行比较。对于Gumstix,30517ns(32.768kHz)相当于499.93MHz cpu的15298个周期。对于我的开发计算机,150ns相当于1.4Ghz CPU的210个周期。
使用clock_nanosleep()调用取消注释,平均结果如下: Gumstix:平均值= 213623,结果以最小分辨率30517ns的倍数上下变化 开发电脑:57710-68065 ns,没有明显的趋势。在开发计算机的情况下,我希望分辨率实际上在1 ns级别,并且测量的~150ns确实是两次clock_gettime()调用之间经过的时间。
所以,我的问题是这些: 是什么决定了最低分辨率? 当处理器运行速度提高约2.6倍时,为什么开发计算机30000X的分辨率优于Gumstix? 有没有办法改变CLOCK_MONOTONIC的更新频率和位置?在内核中?
谢谢!如果您需要更多信息或澄清,请询问。
答案 0 :(得分:7)
据我了解,两种环境(Gumstix和您的开发计算机)之间的差异可能是他们正在使用的基础计时器。
评论nanosleep()案例:
您正在使用clock_gettime()两次。为了让您大致了解这个clock_gettime()最终将映射到哪个(在内核中):
clock_gettime - &gt; clock_get() - &gt; posix_ktime_get_ts - &gt; ktime_get_ts() - &gt; timekeeping_get_ns() - &gt; clock-&gt; read()
clock-&gt; read()基本上读取由底层计时器驱动程序提供的计数器的值和相应的h / w。与过去和当前计数器值的计数器存储值的简单差异,然后纳秒转换数学将产生经过的纳秒,并将更新内核中的计时数据结构。
例如,如果您有一个HPET定时器,它为您提供10 MHz时钟,则h / w计数器将以100 ns的时间间隔更新。
让我们说,在第一个clock-&gt; read()上,你得到一个X的计数器值。
Linux时间保持数据结构将读取X的这个值,与一些旧的存储计数器值相比得到“D”的差异。做一些反差“D”到纳秒'n'转换数学,更新数据 - 结构'n' 将此新时间值产生到用户空间。
当发出第二个clock-&gt; read()时,它将再次读取计数器并更新时间。 现在,对于HPET计时器,此计数器每100ns更新一次,因此,您将看到向用户空间报告此差异。
现在,让我们用一个32.768 KHz的慢时钟代替这个HPET定时器。现在,clock-&gt; read()的计数器只会在30517 ns秒之后更新,所以,如果你第二次调用clock_gettime()就在这段时间之前,你将得到0(这是大多数情况)并且在某些情况下例如,在计数器递增1之后,将放置第二个函数调用,即已经过了30517 ns。因此,有时值为30517 ns。
未注释的Nanosleep()案例 让我们追踪单调时钟的clock_nanosleep():
clock_nanosleep() - &gt; nsleep - &gt; common_nsleep() - &gt; hrtimer_nanosleep() - &gt; do_nanosleep()
do_nanosleep()将简单地将当前任务置于INTERRUPTIBLE状态,等待定时器到期(即1 ns),然后再次将当前任务设置为RUNNING状态。你看,现在涉及很多因素,主要是当你的内核线程(以及用户空间进程)将被再次安排时。根据您的操作系统,当您进行上下文切换时,您将始终面临一些延迟,这是我们使用平均值观察到的。
现在你的问题:
什么决定了最低分辨率?
我认为系统的分辨率/精度将取决于所使用的基础计时器硬件(假设您的操作系统能够为用户空间进程提供该精度)。
*当处理器运行速度提高约2.6倍时,为什么开发计算机30000X的分辨率优于Gumstix? * < / EM> 强>
对不起,我在这里想念你。 它是如何快30000倍?对我来说,它看起来像200倍速(30714 ns / 150 ns~200X?)。但无论如何,据我所知,CPU速度可能与也可能不相关计时器分辨率/精度。因此,这种假设可能适用于某些体系结构(当您使用TSC H / W时),但在其他体系结构中可能会失败(使用HPET,PIT等)。
有没有办法改变CLOCK_MONOTONIC的更新频率和位置?在内核中?
你可以随时查看内核代码以获取详细信息(这就是我对它的看法)。 在linux内核代码中,查找这些源文件和文档:
答案 1 :(得分:1)
我手边没有gumstix,但看起来你的clockource很慢。 跑:
$ dmesg | grep clocksource
如果你回来了
[ 0.560455] Switching to clocksource 32k_counter
这可以解释为什么你的时钟太慢了。
在最近的内核中有一个目录/sys/devices/system/clocksource/clocksource0
,其中包含两个文件:available_clocksource
和current_clocksource
。如果您有此目录,请尝试通过将其名称回显到第二个文件来切换到其他来源。