C 中的 sleep() 有替代方案吗?

发布于 2024-07-07 16:35:53 字数 172 浏览 6 评论 0原文

在传统的嵌入式编程中,我们会给出这样的延迟函数:

for(i=0;i<255;i++)
   for(j=0;j<255;j++);

在微处理器看来,sleep()函数是这样工作的吗?

C 中的 sleep() 函数有替代方案吗?

In traditional embedded programming, we will give a delay function like so:

for(i=0;i<255;i++)
   for(j=0;j<255;j++);

In the microprocessor's view, is this how the sleep() function works?

Is there an alternative for the sleep() function in C?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

骄兵必败 2024-07-14 16:35:53

您描述的那种循环称为“忙等待”。 在真实的操作系统中,休眠不会导致忙等待; 它告诉操作系统在睡眠期结束之前不要安排进程。

The kind of loop you describe is called a "busy wait". In real operating systems, sleeping does not cause a busy wait; it tells the operating system to not schedule the process in until the sleep period is over.

饮湿 2024-07-14 16:35:53

一种常见的机制是使用保证超时的 select() ,并将睡眠时间指定为超时:

// Sleep for 1.5 sec
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 500000;
select(0, NULL, NULL, NULL, &tv);

select() 通常用于检查一组文件描述符并等待至少一个准备好执行 I/O。 如果没有准备好(或者,在这种情况下,如果没有指定 fd),它将超时。

select() 相对于繁忙循环的优势在于,它在休眠时消耗的资源非常少,而繁忙循环则在其优先级允许的范围内独占处理器。

One common mechanism is to use a select() that is guaranteed to time out, and specify the sleep time as the timeout:

// Sleep for 1.5 sec
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 500000;
select(0, NULL, NULL, NULL, &tv);

The select() is typically used to check a set of file descriptors and wait until at least one is ready to perform I/O. If none is ready (or, in this case, if no fds are specified), it will time out.

The advantage of select() over a busy loop is that it consumes very little resources while sleeping, while a busy loop monopolizes the processor as much as permitted by its priority level.

腹黑女流氓 2024-07-14 16:35:53

替代方案取决于您想要执行的操作以及您使用的操作系统。

如果你只是想浪费时间,那么这些可能会有所帮助:

在大多数 UNIX 类型的系统上,你会发现一个“usleep”函数,它或多或少类似于具有更高分辨率的睡眠。 小心那个,因为它通常不能休眠一微秒。

在某些 UNIX 类型的系统上,可以将 select 系统调用与所有文件描述符设置为零一起使用,以获得相当准确的亚秒级等待。

在 Windows 系统上,您有睡眠,这几乎相同,但需要一些毫秒。

在多任务操作系统中,睡眠函数有时可以给定0作为参数。 这通常会导致函数放弃其时间片,但如果没有其他任务准备运行,则会立即重新安排。

Alternatives depend in what you are trying to do and what OS you are on.

If you just want to waste time, then these might help:

On most unix-type systems you'll find a 'usleep' function, which is more or less like sleep with greater resolution. Be careful with that one because it usually can not sleep for just one microsecond.

On some unix-type systems, the select system call can be used with all file descriptor sets zero in order to get a fairly accurate sub-second wait.

On windows systems, you have Sleep, which is pretty much the same, but taking a number of milliseconds.

In a multi-tasking operating system, a sleep function can sometimes be given 0 as a parameter. This generally causes the function to give up it's timeslice, but be re-scheduled immediately if no other task is ready to run.

终陌 2024-07-14 16:35:53

您不会使用您发布的代码在嵌入式系统上休眠。 一个像样的编译器会完全删除它,即使您的编译器不删除它也是次优的,因为在紧密循环中运行处理器会消耗电量,这对于嵌入式系统来说是一个问题。 即使不使用电池运行的系统也会关心功耗,因为较低的功耗意味着更便宜的电源和冷却。

通常执行此操作的方式是 CPU 将执行某种 IDLE 或 SLEEP 指令,这将导致它暂时停止处理命令。 连接到定时器电路的外部中断线将定期唤醒处理器,CPU 会检查该时间点是否已休眠足够长的时间,如果没有,则返回休眠状态。

//Pseudo code
int start = getTime();
int end = start + sleepTime;

while (getTime() < end) {
       asm("SLEEP");
}

确切的细节因处理器而异。 如果您在操作系统上作为进程运行,则睡眠调用通常只是告诉调度程序挂起您的进程,然后内核决定是调度另一个进程还是让 CPU 休眠。 另外,上面的代码对于需要截止时间保证等的实时系统来说是不够的。在这些情况下,您将需要获取循环中的时间,知道时间中断的持续时间,以便知道是否可以重新睡眠而无需超过最后期限,并可能重新编程计时器硬件或忙等待。

You would not use the code you published to sleep on an embedded system. A decent compiler would entirely remove it, and even if your compiler does not remove it is suboptimal, since running the processor in a tight loop will burn power, which is an issue for embedded system. Even systems not running on battery care about power usage, since lower power usage means cheaper power supplies and cooling.

The way you normally do this is your CPU will implement some sort of IDLE or SLEEP instructions, that will cause it to temporarily stop processing commands. An external interrupt line connected to a timer circuit will wake the processor back up at regular intervals, and which point the CPU checks to see if it has been asleep for long enough, and if not it goes back to sleep.

//Pseudo code
int start = getTime();
int end = start + sleepTime;

while (getTime() < end) {
       asm("SLEEP");
}

The exact details vary from processor to processor. If you are running as a process on an OS the sleep call generally just tells the scheduler to suspend your process, and then the kernel decides whether to schedule another process or to sleep the CPU. Also, the above code will not be adequete for real time systems, which want deadline guarantees, etc. In those cases you will need to get the time in the loop, know the duration of the time interrupt so ou know if you can resleep without blowing the deadline, and potentially reprogram the timer hardware or busy wait.

月亮坠入山谷 2024-07-14 16:35:53

你在OP中谈论“嵌入式编程”。 如果您正在进行嵌入式工作并需要 sleep() 之类的功能,通常可以使用硬件计数器/计时器。 这会因架构而异,因此请查看数据表。

如果你不做嵌入式工作,我很抱歉:)

You talk about "embedded programming" in the OP. If you're doing embedded work and need something like sleep(), there are often hardware counters/timers available. This will vary from architecture to architecture, so have a look at the datasheet.

If you're not doing embedded work, I apologize :)

兔小萌 2024-07-14 16:35:53

有关 sleep() 如何工作的更多信息这里

顺便说一下,忙着等待不一定适合业余爱好者——尽管它确实会烧毁您可能想用于其他目的的处理器。 如果您使用时间源,则您将受到该源的粒度的限制。 例如,如果您有一个 1 ms 计时器,并且想要 500 uS,那么您就会遇到问题。 如果您的嵌入式系统可以处理循环嗡嗡声 500 uSec 的情况,那么这可能是可以接受的。 即使您有一个具有所需粒度的计时器,您也需要在正确的时间从该计时器获得中断...然后分派中断处理程序...然后进入您的代码。 有时繁忙循环是最方便的解决方案。 有时。

There's more information on how sleep() works here

By the way, busy waiting is not necessarily for amateurs--although it does burn processor that you may want to use for some other purpose. If you are using a time source, you are limited to the granularity of that source. E.G. if you have a 1 ms timer, and want to way 500 uS, you have a problem. If your embedded system can handle the fact that you'll be buzzing in a loop for 500 uSec, that might be acceptable. And even if you have a timer with your desired granularity, you also need to get an interrupt off that timer at the right time...then dispatch ot the interrupt handler...then get to your code. Sometimes a busy loop is the most expedient solution. Sometimes.

邮友 2024-07-14 16:35:53

如果您使用 for 循环,您最好知道它们编译的内容以及这些指令在给定时钟速度下需要多长时间,确保 CPU 运行您的指令而不运行其他指令(这可以可以在嵌入式系统中完成,但这很棘手,因为它不允许中断)。

否则,你将无法知道它到底需要多长时间。

早期的 PC 游戏存在这个问题 - 它们是为 4.7MHz PC 构建的,当更快的计算机出现时,它们就无法玩了。

“睡眠”工作的最佳方式是让 CPU 知道任何给定点的时间。 不一定是实际时间(上午 7:15),但至少是相对时间(自某个时间点以来的 8612 秒)。

这样它就可以将增量应用到当前时间并循环等待,直到达到当前+增量。

任何依赖于 CPU 周期数的东西本质上都是不可靠的,因为 CPU 可能会转到另一个任务并使循环挂起。

假设您有一个内存映射的 16 位 I/O 端口,CPU 每秒递增一次。 我们还假设它位于嵌入式系统中的内存位置 0x33,其中整数也是 16 位。 一个名为 sleep 的函数就变成了这样:

void sleep (unsigned int delay) {
    unsigned int target = peek(0x33) + delay;
    while (peek(0x33) != target);
}

你必须确保 peek() 每次都返回内存内容(这样优化编译器就不会搞乱逻辑)并且你的 while 语句每秒运行一次以上,这样你就可以不要错过目标,但这些都是操作问题,不会影响我所呈现的概念。

If you're using for-loops, you'd better know what they compile to and how long those instructions take at your given clock speed, and ensure the CPU runs your instructions and nothing else (this can be done in embedded systems but it's tricky since it disallows interrupts).

Otherwise, you won't be able to tell how long it's really going to take.

Early PC games had this problem - they were built for a 4.7MHz PC and, when the faster computers came along, they were unplayable.

The best way a 'sleep' can work is for the CPU to know what time it is at any given point. Not necessarily the actual time (7:15 am) but at least the relative time (8612 seconds since some point in time).

That way it can apply a delta to the current time and wait in a loop until the current+delta is reached.

Anything that relies on number CPU cycles is inherently unreliable as the CPU may go off to another task and leave your loop hanging.

Let's say you have a memory-mapped 16-bit I/O port which the CPU increments once a second. Let's also assume it's at memory location 0x33 in your embedded system, where ints are also 16 bits. A function called sleep then becomes something like:

void sleep (unsigned int delay) {
    unsigned int target = peek(0x33) + delay;
    while (peek(0x33) != target);
}

You'll have to ensure that peek() returns the memory contents every time (so optimizing compilers don't muck up the logic) and that your while statement runs more than once per second so you don't miss the target, but these are operational issues that don't affect the concept I'm presenting.

心舞飞扬 2024-07-14 16:35:53

即使在嵌入式系统中,忙等待也适用于业余爱好者,使用实时源。

Busy-waiting is for amateurs even in an embedded system, use a real time source.

哀由 2024-07-14 16:35:53

任何像样的 C 编译器都会在没有额外工作的情况下完全删除你的代码,延迟就会消失

any decent C compiler would, without extra work, remove your code entirely and the delay would vanish

白衬杉格子梦 2024-07-14 16:35:53

sleep 实际上与操作系统交互,其中睡眠进程被放置在调度队列之外。 我通常使用:

poll(0, 0, milliseconds);

用于 POSIX 兼容系统。 select 也适用于 Windows(它们必须有一个本机 API(可能称为 Sleep)。)

sleep actually interfaces with operating system, where sleeping processes are placed outside of scheduling queue. I usually use:

poll(0, 0, milliseconds);

for POSIX compliant systems. select also works for windows (they must have a native API (probably called Sleep) for that.)

偏闹i 2024-07-14 16:35:53

可在 Linux 中使用
usleep( int 微秒 )
nanosleep( ... ) 更精确,请参阅调用参数的手册页

available in linux
usleep( int microseconds )
nanosleep( ... ) more precision see man pages for calling arguments

静若繁花 2024-07-14 16:35:53

Joseph Albahari 的《C# 中的线程》第 20 页对此进行了有趣的讨论。 在 .Net 中睡眠时间不能少于 1 毫秒,但 DateTime.Ticks 的间隔粒度为 100 纳秒(= 0.1 微秒)。 为了控制我的 5 轴 CNC 步进器,我只需要在步进命令之间暂停 10 微秒。 我已经使用了一个微控制器来完成令人讨厌的循环,但我认为如果你有一大堆,那么移交一个处理器来完成这项工作是可以的,尽可能停止线程。 至少它不会总是同一个。

Page 20 in 'Threading in C#' by Joseph Albahari has an interesting discussion of this. You cannot sleep for less than 1 ms in .Net but DateTime.Ticks has a granularity of 100-nanoseconds (= 0.1 microseconds) intervals. For controlling my 5 axis CNC stepper I only need to pause for 10 microseconds between step commands. I have used a micro controller to do the nasty looping but I think it is OK hand over a processor for the job if you have a whole bunch anyway, stop the thread when you can. At least it wont always be the same one.

做个ˇ局外人 2024-07-14 16:35:53
#include <Windows.h>

static NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOL, PLARGE_INTEGER)) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");

static NTSTATUS(__stdcall *ZwSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG)) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution");




static void SleepShort(float milliseconds) {
    static bool once = true;
    if (once) {
        ULONG actualResolution;
        ZwSetTimerResolution(1, true, &actualResolution);
        once = false;
    }

    LARGE_INTEGER interval;
    interval.QuadPart = -1 * (int)(milliseconds * 10000.0f);
    NtDelayExecution(false, &interval);
}
#include <Windows.h>

static NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOL, PLARGE_INTEGER)) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");

static NTSTATUS(__stdcall *ZwSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG)) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution");




static void SleepShort(float milliseconds) {
    static bool once = true;
    if (once) {
        ULONG actualResolution;
        ZwSetTimerResolution(1, true, &actualResolution);
        once = false;
    }

    LARGE_INTEGER interval;
    interval.QuadPart = -1 * (int)(milliseconds * 10000.0f);
    NtDelayExecution(false, &interval);
}
故乡的云 2024-07-14 16:35:53

在 unix 衍生操作系统中,您可能会安排 signal() 调用,并且您的代码将简单地阻塞代码,直到发出信号为止。 信号就是为了这个目的而设计的,而且它们非常简单和高效。

In a unix-derivative OS, you would probably schedule a signal() call, and your code would simply block the code until the signal is raised. Signals are intended for the purpose, and they are very simple and efficient.

夕嗳→ 2024-07-14 16:35:53

我在这篇文章中找到了该函数(http://cboard .cprogramming.com/c-programming/111229-how-use-sleep-function.html)并且它的工作原理:

#include <stdio.h>
#include <windows.h>

int main()
{
    puts("Hello \n");
    /* in windows.h is declared the Sleep (upper S) function and it takes time in 
miliseconds */
    Sleep(3000);
    puts("World \n");
    return 0;
}

I found the function in this post (http://cboard.cprogramming.com/c-programming/111229-how-use-sleep-function.html) and it works:

#include <stdio.h>
#include <windows.h>

int main()
{
    puts("Hello \n");
    /* in windows.h is declared the Sleep (upper S) function and it takes time in 
miliseconds */
    Sleep(3000);
    puts("World \n");
    return 0;
}
一抹微笑 2024-07-14 16:35:53

尝试......真正解决这个问题,即有效的东西(不像上面的回答尝试)哈哈,

我仍然需要改进这个代码才能解决这个问题。 欢迎一些附加组件。

// Sleep for both Windows and Linux: 
// Too bad? No one proposed you a solution that works? 
// Since Windows has no select.h nor poll.h, an implementation
// is necessary.
//  
// Solutions on boards are often refered to use either select or poll, but ok, what about C (not c++)?
//
/// implementation of poll is destined in this attempt for windows
/// Ideally, you add this part of code to the header of you *.c file, and it might work through...

#ifdef WIN32
#include <time.h>
#include <sys/time.h>
#include <ws2tcpip.h>
#include <Winsock2.h>
#include <windows.h>
/* winsock doesn't feature poll(), so there is a version implemented
 * in terms of select() in mingw.c. The following definitions
 * are copied from linux man pages. A poll() macro is defined to
 * call the version in mingw.c.
 */
#define POLLIN      0x0001    /* There is data to read */
#define POLLPRI     0x0002    /* There is urgent data to read */
#define POLLOUT     0x0004    /* Writing now will not block */
#define POLLERR     0x0008    /* Error condition */
#define POLLHUP     0x0010    /* Hung up */
#define POLLNVAL    0x0020    /* Invalid request: fd not open */
struct pollfd {
  SOCKET fd;        /* file descriptor */
  short events;     /* requested events */
  short revents;    /* returned events */
};

int mingw_poll (struct pollfd *, unsigned int, int);

#define poll(x, y, z)        mingw_poll(x, y, z)
#endif





int mingw_poll(struct pollfd *fds, unsigned int nfds, int timo)
{
    struct timeval timeout, *toptr;
    fd_set ifds, ofds, efds, *ip, *op;
    int i, rc;

    /* Set up the file-descriptor sets in ifds, ofds and efds. */
    FD_ZERO(&ifds);
    FD_ZERO(&ofds);
    FD_ZERO(&efds);
    for (i = 0, op = ip = 0; i < nfds; ++i) {
    fds[i].revents = 0;
    if(fds[i].events & (POLLIN|POLLPRI)) {
        ip = &ifds;
        FD_SET(fds[i].fd, ip);
    }
    if(fds[i].events & POLLOUT) {
        op = &ofds;
        FD_SET(fds[i].fd, op);
    }
    FD_SET(fds[i].fd, &efds);
    } 

    /* Set up the timeval structure for the timeout parameter */
    if(timo < 0) {
    toptr = 0;
    } else {
    toptr = &timeout;
    timeout.tv_sec = timo / 1000;
    timeout.tv_usec = (timo - timeout.tv_sec * 1000) * 1000;
    }

#ifdef DEBUG_POLL
    printf("Entering select() sec=%ld usec=%ld ip=%lx op=%lx\n",
           (long)timeout.tv_sec, (long)timeout.tv_usec, (long)ip, (long)op);
#endif
    rc = select(0, ip, op, &efds, toptr);
#ifdef DEBUG_POLL
    printf("Exiting select rc=%d\n", rc);
#endif

    if(rc <= 0)
    return rc;

    if(rc > 0) {
        for (i = 0; i < nfds; ++i) {
            int fd = fds[i].fd;
        if(fds[i].events & (POLLIN|POLLPRI) && FD_ISSET(fd, &ifds))
            fds[i].revents |= POLLIN;
        if(fds[i].events & POLLOUT && FD_ISSET(fd, &ofds))
            fds[i].revents |= POLLOUT;
        if(FD_ISSET(fd, &efds))
            /* Some error was detected ... should be some way to know. */
            fds[i].revents |= POLLHUP;
#ifdef DEBUG_POLL
        printf("%d %d %d revent = %x\n", 
                FD_ISSET(fd, &ifds), FD_ISSET(fd, &ofds), FD_ISSET(fd, &efds), 
                fds[i].revents
        );
#endif
        }
    }
    return rc;
}

an attempt... to really solve this issue, i.e. something that works (not like the above attempts of answer) lol

I still have to improve this code to make it out. Few add-ons are welcome.

// Sleep for both Windows and Linux: 
// Too bad? No one proposed you a solution that works? 
// Since Windows has no select.h nor poll.h, an implementation
// is necessary.
//  
// Solutions on boards are often refered to use either select or poll, but ok, what about C (not c++)?
//
/// implementation of poll is destined in this attempt for windows
/// Ideally, you add this part of code to the header of you *.c file, and it might work through...

#ifdef WIN32
#include <time.h>
#include <sys/time.h>
#include <ws2tcpip.h>
#include <Winsock2.h>
#include <windows.h>
/* winsock doesn't feature poll(), so there is a version implemented
 * in terms of select() in mingw.c. The following definitions
 * are copied from linux man pages. A poll() macro is defined to
 * call the version in mingw.c.
 */
#define POLLIN      0x0001    /* There is data to read */
#define POLLPRI     0x0002    /* There is urgent data to read */
#define POLLOUT     0x0004    /* Writing now will not block */
#define POLLERR     0x0008    /* Error condition */
#define POLLHUP     0x0010    /* Hung up */
#define POLLNVAL    0x0020    /* Invalid request: fd not open */
struct pollfd {
  SOCKET fd;        /* file descriptor */
  short events;     /* requested events */
  short revents;    /* returned events */
};

int mingw_poll (struct pollfd *, unsigned int, int);

#define poll(x, y, z)        mingw_poll(x, y, z)
#endif





int mingw_poll(struct pollfd *fds, unsigned int nfds, int timo)
{
    struct timeval timeout, *toptr;
    fd_set ifds, ofds, efds, *ip, *op;
    int i, rc;

    /* Set up the file-descriptor sets in ifds, ofds and efds. */
    FD_ZERO(&ifds);
    FD_ZERO(&ofds);
    FD_ZERO(&efds);
    for (i = 0, op = ip = 0; i < nfds; ++i) {
    fds[i].revents = 0;
    if(fds[i].events & (POLLIN|POLLPRI)) {
        ip = &ifds;
        FD_SET(fds[i].fd, ip);
    }
    if(fds[i].events & POLLOUT) {
        op = &ofds;
        FD_SET(fds[i].fd, op);
    }
    FD_SET(fds[i].fd, &efds);
    } 

    /* Set up the timeval structure for the timeout parameter */
    if(timo < 0) {
    toptr = 0;
    } else {
    toptr = &timeout;
    timeout.tv_sec = timo / 1000;
    timeout.tv_usec = (timo - timeout.tv_sec * 1000) * 1000;
    }

#ifdef DEBUG_POLL
    printf("Entering select() sec=%ld usec=%ld ip=%lx op=%lx\n",
           (long)timeout.tv_sec, (long)timeout.tv_usec, (long)ip, (long)op);
#endif
    rc = select(0, ip, op, &efds, toptr);
#ifdef DEBUG_POLL
    printf("Exiting select rc=%d\n", rc);
#endif

    if(rc <= 0)
    return rc;

    if(rc > 0) {
        for (i = 0; i < nfds; ++i) {
            int fd = fds[i].fd;
        if(fds[i].events & (POLLIN|POLLPRI) && FD_ISSET(fd, &ifds))
            fds[i].revents |= POLLIN;
        if(fds[i].events & POLLOUT && FD_ISSET(fd, &ofds))
            fds[i].revents |= POLLOUT;
        if(FD_ISSET(fd, &efds))
            /* Some error was detected ... should be some way to know. */
            fds[i].revents |= POLLHUP;
#ifdef DEBUG_POLL
        printf("%d %d %d revent = %x\n", 
                FD_ISSET(fd, &ifds), FD_ISSET(fd, &ofds), FD_ISSET(fd, &efds), 
                fds[i].revents
        );
#endif
        }
    }
    return rc;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文