如何睡几微秒
考虑以下代码:
#include <stdio.h>
#include <time.h>
#include <math.h>
// Compile with gcc -lrt -lm -o test_clock test_clock.c
#define CLOCK CLOCK_MONOTONIC
int main(int argc, char** argv) {
double temp, elapsed;
int j;
struct timespec requestStart, requestEnd, req;
// Pseudo-sleep
clock_gettime(CLOCK, &requestStart);
temp = 0;
for(j=0; j < 40; j++)
temp += sin(j);
clock_gettime(CLOCK, &requestEnd);
elapsed = ( requestEnd.tv_sec - requestStart.tv_sec ) / 1e-6
+ ( requestEnd.tv_nsec - requestStart.tv_nsec ) / 1e3;
printf("Elapsed: %lf us\n", elapsed);
// Nanosleep
clock_gettime(CLOCK, &requestStart);
req.tv_nsec = 5000;
req.tv_sec = 0;
clock_nanosleep(CLOCK, 0, &req, NULL);
clock_gettime(CLOCK, &requestEnd);
elapsed = ( requestEnd.tv_sec - requestStart.tv_sec ) / 1e-6
+ ( requestEnd.tv_nsec - requestStart.tv_nsec ) / 1e3;
printf("Elapsed: %lf us\n", elapsed);
}
在我的 2.6.32 系统上,结果是
Elapsed: 5.308000 us
Elapsed: 69.142000 us
我同意这很可能是因为 nanosleep() 要求内核重新安排进程。我怎样才能避免这种情况?我想保留 CPU 的所有权并闲置一段精确的时间。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
如果您希望您的应用程序能够尽可能精确地“睡眠”,请首先将您的应用程序置于实时条件下,
SCHED_FIFO
或SCHED_RR
请手动 busywait 查看 http://www.drdobbs.com/184402031
还有另一个问题:nanosleep CPU 使用率高?
If you want your application to be able to "sleep" as precisely as possible, first put your application in realtime conditions
SCHED_FIFO
orSCHED_RR
Have a look at http://www.drdobbs.com/184402031
And this other question: nanosleep high cpu usage?
操作系统调度程序不会执行类似“哦,将此线程从处理器上移出正好 86 个时钟周期,然后将其重新打开”之类的操作。
你放弃了处理器,你就放弃了处理器。当您需要时,操作系统会让您重新启动。很可能您必须等到其他正在运行的程序放弃处理器才能重新启动。
The OS scheduler is not going to do anything like "oh, take this thread off the processor for exactly 86 clock cycles then put it back on".
You give up the processor, you've given up the processor. The OS will put you back on when it feels like it. Chances are you'll have to wait until whatever else is running gives up the processor before you can sneak back on.
如何配置 Linux
SCHED_RR
软实时循环调度程序,以便将clock_nanosleep()
的睡眠分辨率从约 55 秒降低到最低约 4 秒us 最小值,具体取决于您的硬件问题摘要
OP 正确使用了
clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_time, NULL)
的等效项来尝试休眠请求的时钟时间恰好为 5000 ns,或 5 us。请参阅: https://man7.org/linux/man-pages/ man2/clock_nanosleep.2.html 和问题。 然而,尽管被命令睡眠5 秒,但实际睡眠时间为69 秒。为什么?我遇到了同样类型的问题。我命令
clock_nanosleep()
休眠 1 ns(0.001 us),它平均休眠 55 us 。这似乎是可能的最小睡眠时间间隔。为什么?我们可以做得更好吗?答案摘要
SCHED_OTHER
/SCHED_NORMAL
“默认 Linux 分时”调度程序时出现的情况。SCHED_RR
循环软实时调度程序(推荐) 、或SCHED_FIFO
先进/先出软实时调度程序。这是我的
sleep_nanosleep_minimum_time_interval.c 的测试结果
程序:SCHED_OTHER
/SCHED_NORMAL
“默认 Linux 分时”调度程序:SCHED_RR
循环软实时调度程序,最低优先级为1:- 可能的最短纳睡眠时间:~4000 ns(~4 us)
SCHED_RR
循环软实时调度程序,最高优先级为99:- 可能的最短纳睡眠时间:~4000 ns(~4 us)(同上)
SCHED_FIFO
先进/先出软实时调度程序1
的最低优先级:SCHED_FIFO
先进/先出软实时调度程序99
的最高优先级:如您所见,您会立即获得巨大的延迟时间(55000/4000 = 13.75x) )只需从
SCHED_OTHER
/SCHED_NORMAL
非实时 Linux 调度程序切换到软实时 Linux 调度程序即可立即得到改进,例如SCHED_FIFO
或SCHED_RR
,具有任意优先级。根据我的测试,这两个调度程序中的哪一个以及您选择哪个优先级并不那么重要。您必须知道您想要哪个线程获得什么优先级以及何时获得,以便调整您自己的线程。作为初学者,设置具有非常短的睡眠时间的线程,通常 1~10 ms:SCHED_RR
软实时循环调度程序,以及:作为初学者,请对所有实时线程使用最低优先级
1
的SCHED_RR
,然后根据需要调整他们的优先级。您不想想立即选择一个非常高的优先级并不必要地阻止高优先级任务。仅根据需要提高线程或进程的优先级。如何设置 Linux“策略”(调度程序)和“优先级”
@Yann Droneaud 的回答 很有帮助,但它没有显示如何使用实时调度程序。然而,它链接到的文章解释了更多内容(但有一些错误和疏忽),Ask Ubuntu Q&A 也是如此。我研究了这两个来源以了解下面提供的选项:
SCHED_RR
运行程序命令行中的策略?以下是如何设置 Linux 调度程序“策略”(调度程序)和“优先级”:
chrt
“更改实时”命令调用您的程序:要查看给定调度程序可用的
优先级
值,请运行chrt --max
。如果在具有chrt
BusyBox 实现的嵌入式 Linux 系统上,请改用chrt -m
。以下是我的 x86-64 Linux Ubuntu 机器上的运行和输出:如您所见,对于
SCHED_FIFO
和SCHED_RR
,1/99
显示最低优先级为1
,并且最高优先级是99
。另请参阅
SCHED_RR
实时循环调度程序和打开绝对计时标志的clock_nanosleep()
来执行 10 kHz 固定周期循环。nanosleep() 输出 创建
sleep_us()
/ElectricRCAircraftGuy/eRCaGuy_hello_world/blob/master/c/sleep_nanosleep_minimum_time_interval.c" rel="nofollow noreferrer">sleep_nanosleep_minimum_time_interval.c
与SCHED_RR
循环实时时间调度程序:请注意,错误平均约为 4 us(向右滚动一点):
How to configure the Linux
SCHED_RR
soft real-time round-robin scheduler so thatclock_nanosleep()
can have improved sleep resolution as low as ~4 us minimum, down from ~55 us minimum, depending on your hardwareSummary of the question
The OP correctly used the equivlanet of
clock_nanosleep(CLOCK_MONOTONIC, 0, &requested_time, NULL)
to try to sleep a requested time of exactly 5000 ns, or 5 us. See: https://man7.org/linux/man-pages/man2/clock_nanosleep.2.html and the question. However, the actual time slept was 69 us despite being commanded to sleep 5 us. Why?I ran into this same type of issue. I was commanding
clock_nanosleep()
to sleep 1 ns (0.001 us), and it slept on average 55 us. That seems to be the smallest sleep time interval possible. Why? Can we make this any better?Answer summary
SCHED_OTHER
/SCHED_NORMAL
"Default Linux time-sharing" scheduler, however.SCHED_RR
round-robin soft real-time scheduler (recommended), or theSCHED_FIFO
first-in/first-out soft real-time scheduler.Here are my test results from my
sleep_nanosleep_minimum_time_interval.c
program:SCHED_OTHER
/SCHED_NORMAL
"Default Linux time-sharing" scheduler:SCHED_RR
round-robin soft real-time scheduler with the lowest priority of1
:SCHED_RR
round-robin soft real-time scheduler with the highest priority of99
:SCHED_FIFO
first-in/first-out soft real-time scheduler with the lowest priority of1
:SCHED_FIFO
first-in/first-out soft real-time scheduler with the highest priority of99
:As you can see, you get an immediate and huge (55000/4000 = 13.75x) improvement immediately just by switching from the
SCHED_OTHER
/SCHED_NORMAL
non-realtime Linux scheduler to a soft realtime Linux scheduler, such asSCHED_FIFO
orSCHED_RR
, with any priority level. Which of those two schedulers, and which priority you choose, based on my testing, is less important. You'd have to know which thread you want to get what priority, and when, in order to tweak your own threads. As a starter, set threads which have really short sleep times, generally < 1~10 ms to:SCHED_RR
soft real-time round-robin scheduler, and to:As a starter, use
SCHED_RR
with the lowest priority of1
for all your real-time threads, and then adjust their priorities up from there, as needed. You do not want to choose a really high priority right off the bat and unnecessarily block high-priority tasks. Only raise the priority of a thread or process as-needed.How to set the Linux "policy" (scheduler) and "priority"
This answer by @Yann Droneaud is helpful, but it doesn't show how to use a real-time scheduler. The article it links to, however, explains more (but is has a few errors and oversights), as does this Ask Ubuntu Q&A. I studied these two sources to learn the options I present below:
SCHED_RR
policy from command line?Here is how to set your Linux scheduler "policy" (scheduler) and "priority":
chrt
"change real-time" command:To see what
priority
values are available for a given scheduler, runchrt --max
. If on an embedded Linux system with the BusyBox implementation ofchrt
, usechrt -m
instead. Here is the run and output on my x86-64 Linux Ubuntu machine:As you can see, for both
SCHED_FIFO
andSCHED_RR
,1/99
shows that the lowest priority is1
and the highest priority is99
.set_scheduler()
in my sleep_nanosleep_minimum_time_interval.c test file:See also
SCHED_RR
real-time round-robin scheduler, andclock_nanosleep()
with the absolute timing flag on.sleep_us()
fromnanosleep()
Output from
sleep_nanosleep_minimum_time_interval.c
withSCHED_RR
round-robin real-time scheduler:Notice that the error averages around 4 us (scroll to the right a little):
好吧,您必须学会忍受它,因为手册页部分指出:
由于系统延迟和硬件计时器分辨率可能存在的限制,实际睡眠时间可能会更长
:-)现在,关于您问题的答案,我最好的猜测是,这是因为您的第一个循环正在进程内运行。换句话说,不涉及上下文切换,因为您正在全力运行 CPU,并且您将在调度程序给您的 100 毫秒量子内完成所有工作。
然而,由于您明确要求进入睡眠状态,
nanosleep
很有可能会将您切换出去。将进程置于紧密的while
循环中直到持续时间结束,效率不会那么低下:-)这意味着您会受到调度程序的所有变幻莫测的影响,包括 事实上,另一个进程可能会完全用完它的量子,因此您的进程可能会离开那里至少 100 毫秒。在负载足够重的系统上,它可能会出现相当长的一段时间。
Well, you'll have to learn to live with it since the man page states, in part:
the actual time slept may be longer, due to system latencies and possible limitations in the timer resolution of the hardware
:-)Now as to the answer to your question, my best guess is that it's because your first loop is running in-process. In other words, there are no context switches involved since you're running the CPU flat out and you will be doing all that work within your 100ms quanta given to you by the scheduler.
However, there's a good chance that
nanosleep
will switch you out since you are explicitly asking to be put to sleep. It won't be so inefficient as to just put your process in a tightwhile
loop until the duration is over :-)That means you're subject to all the vagaries of the scheduler including the fact that another process may totally use up its quanta, hence your process may be out of there for 100ms at least. On a heavily-enough loaded system, it could be out for quite a while.
// 它比 usleep 好得多。
// it is much better than the usleep.
您可以使用
usleep
方法以微秒为单位获得睡眠。you can use
usleep
method to get sleep in microsecond units.效率——一个允许以几个时钟周期的精度切换任务的操作系统几乎没有其他作用。
有专门的操作系统可以执行此操作 - 但在常规硬件上,您需要为虚拟机管理程序付出大量开销
Efficiency - an Os that allowed tasks to be switched in and out with a precision of a few clock cycles would do very little else.
There are specialized OSes that do this - but on regular hardware you pay a lot of overhead for the hypervisor
这是一个固定的答案 - 我不知道相关的 linux 内部结构,希望专家能够来解决它。
一种可能性是,69us 只是取消调度然后重新调度线程的原始开销。即使睡眠时间很短,内核也可能会做很多工作来执行上下文切换(或者半个上下文切换,如果没有什么可安排的),然后几乎立即撤消它。我不知道在典型 PC 上的 Linux 上“应该”需要多长时间。
如果这不能解释它,调度程序通常有一个“时间片”的概念,这是调度程序考虑切换它之前调度线程将运行的时间,除非它自行调度或其他具有更高的调度线程。优先级变得可调度。内核将有低级计时器在时间片结束时触发中断(除了为某些其他事件(例如可以解锁线程的 I/O)触发的中断)。当时间片结束时,调度程序可以决定是继续使用同一线程,还是切换到另一个线程。
所以看起来好像当你睡觉时,要么(a)调度程序实际上并没有设置一个计时器来让你的线程在请求的时间进行调度,它只是在等待一个时间片,所以CPU的空闲时间比必要的时间要长;否则(b)它使您的线程可以在请求的时间进行调度,但是当您通过睡眠放弃执行时,其他具有相同优先级的线程进入,并且调度程序没有理由优先选择您,直到“轮到您了” ” 再次根据调度程序通常使用的任何规则来决定调度哪个线程。
不过,69us 对于时间切片来说还是相当短的。
您似乎有一个基本的解决方案 - 您可以通过坐在循环中检查时间来延迟很短的时间,就像自旋锁一样。然而,正如其他人所说,在非实时系统中,或多或少根据定义,您不能要求调度程序在任何特定时间运行您的线程。即使在实时系统中,如果您与同等优先级的线程竞争,您也可能会失败,而如果您与更高优先级的线程竞争,您将失败。
This is a holding answer - I don't know the relevant linux internals, hopefully an expert can come along and clear it up.
One possibility is that that 69us is simply the raw overhead of descheduling and then rescheduling the thread. Even though the sleep is short, the kernel might do a lot of work to perform a context switch (or half a context switch, if there's nothing to schedule), and then undo it almost immediately. I don't know how long that "should" take on linux on a typical PC.
If that doesn't explain it, a scheduler generally has a concept of a "timeslice", which is how long a scheduled thread will be left to run before the scheduler thinks about switching it, unless it either deschedules itself or else something with higher priority becomes schedulable. The kernel will have low-level timers firing interrupts at the end of a time slice (in addition to interrupts that fire for certain other events such as I/O that could unblock a thread). When a timeslice ends, the scheduler can decide whether to continue with the same thread, or switch to another.
So it looks as though when you sleep, either (a) the scheduler isn't actually setting a timer that will make your thread schedulable at the requested time, it's just waiting for a timeslice, and so the CPU is going idle longer than necessary; or else (b) it is making your thread schedulable at the requested time, but when you gave up execution by sleeping, some other thread of equal priority got in, and the scheduler has no grounds to prefer you over it until it's "your turn" again according to whatever rules the scheduler usually uses to decide what thread to schedule.
69us is pretty short to be an artefact of timeslicing, though.
You seem to have a rudimentary solution - you can delay for very short periods by sitting in a loop checking the time, just like a spinlock. As everyone else is saying, though, in a non-realtime system, more or less by definition you can't demand that the scheduler runs your thread at any specific time. Even in a realtime system, if you're competing with threads of equal priority you may lose, and if you're competing with threads at higher priority you will lose.
上面的任何答案都没有提到线程的
PR_SET_TIMERSLACK
参数。默认情况下,它设置为 50 us。有关 PR_SET_TIMERSLACK 的更多信息,请参见:https://man7.org/linux/man -pages/man2/prctl.2.html。您可以使用
sudo cat /proc/PID/timerslack_ns
检查任何进程的timerslack(不要忘记将PID
替换为真实的进程ID)。具有实时调度程序策略的线程会忽略timerslack值,因此接受的答案有效,因为在
SCHED_FIFO
和SCHED_RR
中timerslack被忽略。此外,立即调度优先级高于其他线程的线程也更容易。也许在某些情况下将timerslack设置为0就足够了(当5us不必每次都严格时)。如果您不想更改调度策略,则可以采用这种方法。
Any answer above doesn't mention about
PR_SET_TIMERSLACK
parameter of a thread. By default, it's set to 50 us. More about PR_SET_TIMERSLACK here: https://man7.org/linux/man-pages/man2/prctl.2.html.You can check timerslack for any process with
sudo cat /proc/PID/timerslack_ns
(don't forget to replacePID
with real process ID).Threads with a real-time scheduler policy ignore timerslack value, so the accepted answer works because in
SCHED_FIFO
andSCHED_RR
timerslack is ignored. Additionally, it's easier to immediately schedule a thread with a priority higher than any other.Maybe setting timerslack to 0 can be enough in some cases (when 5 us don't have to be strict every single time). This is the way if you don't want to change the scheduling policy.