C 为什么nanosleep()会增加一个恒定的延迟,我如何避免这种情况?

C 为什么nanosleep()会增加一个恒定的延迟,我如何避免这种情况?,c,raspberry-pi,real-time,C,Raspberry Pi,Real Time,我想用我的树莓3驱动步进电机。我需要每秒产生大约10000个脉冲,这意味着我需要以大约100us的间隔产生脉冲。我知道Raspberry可以生成PWM,但事实并非如此,因为我需要精确控制脉冲数以及加速/减速,所以我更喜欢显式循环 虽然忙循环提供了非常精确的计时,但它显然消耗了100%的CPU时间。同时,nanosleep()在时间精度和CPU负载之间提供了很好的平衡——我可以用大约10%的CPU负载驱动电机 但是。我写了一小段代码来测量nanosleep()延迟 无符号长迭代次数=5000; 对

我想用我的树莓3驱动步进电机。我需要每秒产生大约10000个脉冲,这意味着我需要以大约100us的间隔产生脉冲。我知道Raspberry可以生成PWM,但事实并非如此,因为我需要精确控制脉冲数以及加速/减速,所以我更喜欢显式循环

虽然忙循环提供了非常精确的计时,但它显然消耗了100%的CPU时间。同时,nanosleep()在时间精度和CPU负载之间提供了很好的平衡——我可以用大约10%的CPU负载驱动电机

但是。我写了一小段代码来测量nanosleep()延迟

无符号长迭代次数=5000;
对于(int d=10;d
1) 为什么我期望的睡眠时间和实际的睡眠时间有差异

Linux可能会等到所需的时间过去,然后花一定的时间切换回您的任务并从内核返回

3) 如果CPU负载增加,为什么这种差异会变小(我的预期正好相反)

当CPU无事可做(例如,因为你在“纳米延迟”)时,Linux可能会将CPU置于某种睡眠状态以降低功耗;当延迟到期时,需要额外的时间来唤醒CPU。当然,如果CPU还有其他工作要做,它就不会进入睡眠状态(也不会节省电源),也不需要被唤醒


请注意,使用TIMER\u ABSTIME来确保额外的延迟是恒定的(并且不会增加-例如,这次延迟65微秒,下次延迟130微秒,之后延迟196微秒,…)可能会更有趣。

您的进程是从用户空间运行的。为了睡眠和获得时间,您需要切换到内核空间。这会产生间接费用。此外,任何睡眠功能都只能保证在睡眠计时器过期后的某个时间唤醒

总之,为了获得所需的精度,您可能需要以设备驱动程序的形式编写代码。这使您可以直接访问所需的高分辨率计时器

1)为什么期望睡眠时间与实际睡眠时间之间存在差异?

:

虽然通常有最短时间段的保证,但没有严格保证线程会立即或很快运行,甚至在指定的时间过去后根本不会运行。这取决于调度程序的判断,并取决于线程优先级和实现细节,如休眠线程再次运行时的计时器分辨率

这也适用于Linux。若您的流程是关键的,您可以使用命令提高其优先级,这将影响流程调度,并可能给您带来更好的结果

2)这种差异非常稳定,不依赖于请求的延迟。有人对此有解释吗?

除了上面提到的,另一个因素是。摘自维基百科:

在计算中,上下文切换是存储进程或线程状态的过程,以便以后可以从同一点恢复进程或线程状态并恢复执行。这允许多个进程共享一个CPU,这是多任务操作系统的一个基本特性

显然,上下文切换需要时间,这是测试中应该考虑的

3)如果CPU负载增加,为什么这种差异会变小(我的预期正好相反)?

有,默认为
ondemand
。摘自内核文档:

CPUfreq调控器“ondemand”根据CPU频率设置 当前系统负载

这意味着如果有更大的CPU负载,您的系统可能会响应更快

对于测试,您可能希望将调控器设置为
performance
userspace
,以获得不受CPU速度动态变化影响的结果:

echo performance > /sys/bus/cpu/devices/cpu0/cpufreq/scaling_governor
echo 1000000 > /sys/bus/cpu/devices/cpu0/cpufreq/scaling_max_freq

计时器延迟(即期望睡眠时间和实际睡眠时间之间的差异)似乎是由名为timer_slop的内核参数引起的。它的默认值是50 us,这意味着您的计时器将延迟最多50 us。定时器斜率的设计是为了通过分批处理定时器中断来提高系统性能,从而减少定时器中断的数量

您可以通过文件/proc/[pid]/timerslack\n更改进程的计时器斜率。
例如,命令
echo 10000>/proc/[pid]/timerslack\u ns
将计时器斜率更改为10 us。

是的,我正在考虑编写内核模块,但这需要一些时间来学习如何做到这一点。谢谢,切换到“性能”模式使延迟更加稳定。它不再依赖于CPU负载,但仍然是大约60us。增加了1和2的答案。
10      80      70
15      85      70
20      89      69
25      95      70
30      99      69
35      105     70
40      110     70
45      114     69
50      123     70
55      124     69
60      129     69
65      134     69
70      130     60    <---- here I run CPU-heavy process
75      135     60
80      140     60
85      145     60
90      150     60
95      158     62
100     161     61
105     166     61
110     172     62
115     177     62
120     181     61
125     186     61
130     191     61
135     205     70    <---- here it finished
140     210     70
145     215     70
150     220     70
155     225     70
160     230     70
165     235     70

echo performance > /sys/bus/cpu/devices/cpu0/cpufreq/scaling_governor
echo 1000000 > /sys/bus/cpu/devices/cpu0/cpufreq/scaling_max_freq