在C中有睡眠()的替代方案吗?

在传统的嵌入式编程中,我们将提供如下的延迟函数:

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

在微处理器的视图中,这是sleep()函数的工作原理吗?

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

替代方案取决于您尝试做什么以及您使用的操作系统。

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

在大多数unix类型的系统中,你会发现一个’usleep’function,它或多或少像睡眠一样具有更高的分辨率。 小心那个,因为它通常只能睡一个微秒。

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

在Windows系统上,你有睡眠,这几乎是相同的,但需要花费几毫秒。

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

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

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

 // 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. 如果没有准备好(或者,在这种情况下,如果没有指定fds),它将超时。

select()在繁忙循环中的优势在于它在hibernate时消耗的资源非常少,而繁忙的循环在处理器上独占优先级所允许的优先级。

您不会使用您发布的代码在嵌入式系统上hibernate。 一个不错的编译器会完全删除它,即使你的编译器没有删除它也不是最理想的,因为在紧密循环中运行处理器会烧毁功率,这对于嵌入式系统来说是一个问题。 即使是没有使用电池运行的系统也会关注电源使用,因为较低的电量使用意味着更便宜的电源和冷却。

通常这样做的方法是CPU将实现某种IDLE或SLEEP指令,这将导致它暂时停止处理命令。 连接到定时器电路的外部中断线将定期唤醒处理器,并且CPU检查它是否已经睡眠足够长时间,如果没有,它会重新进入hibernate状态。

 //Pseudo code int start = getTime(); int end = start + sleepTime; while (getTime() < end) { asm("SLEEP"); } 

具体细节因处理器而异。 如果您在OS上作为进程运行,则sleep调用通常只是告诉调度程序暂停您的进程,然后内核决定是安排另一个进程还是hibernateCPU。 此外,上述代码不适用于需要截止日期保证等的实时系统。在这些情况下,您需要在循环中获取时间,知道时间中断的持续时间,因此您知道是否可以在没有吹过截止日期,并可能重新编程定时器硬件或忙等待。

你在OP中讨论“嵌入式编程”。 如果您正在进行嵌入式工作并需要像sleep()这样的东西,通常会有硬件计数器/计时器。 这将因架构而异,因此请查看数据表。

如果你没有做嵌入式工作,我道歉:)

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

否则,您将无法确定实际需要多长时间。

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

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

这样,它可以将delta应用于当前时间并在循环中等待,直到达到当前的+ delta。

任何依赖于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语句每秒运行多次,所以你不会错过目标,但这些是运营问题,不影响我提出的概念。

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

顺便说一下,忙碌的等待不一定是业余爱好者 – 尽管它确实会烧掉您可能想要用于其他目的的处理器。 如果您使用的是时间源,则仅限于该源的粒度。 EG如果你有一个1毫秒的计时器,并想要500美元,你有一个问题。 如果您的嵌入式系统可以处理您将在500 uSec的循环中进行嗡嗡声的事实,那么这可能是可以接受的。 即使你有一个具有所需粒度的计时器,你还需要在正确的时间从该计时器中断一个中断…然后调度中断处理程序……然后转到你的代码。 有时繁忙的循环是最方便的解决方案。 有时。

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

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

sleep实际上与操作系统接口,其中hibernate进程位于调度队列之外。 我通常使用:

 poll(0, 0, milliseconds); 

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

约瑟夫·阿尔巴哈里(Joseph Albahari)在“穿线于C#”中的第20页对此进行了有趣的讨论。 你不能在.Net中睡眠少于1毫秒,但DateTime.Ticks的粒度为100纳秒(= 0.1微秒)。 为了控制我的5轴CNC步进器,我只需要在步骤命令之间暂停10微秒。 我已经使用了一个微控制器来进行令人讨厌的循环,但是如果你有一大堆话,我认为可以移交处理器来完成工作,当你可以时停止线程。 至少它不会永远是同一个。

 #include  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); } 

是的,它使用了一些未记录的内核函数,但老实说,它比大多数建议快得多,效果非常好(注意它将仅限于CPU的数量,例如我的只能睡500纳秒(0.5))

在linux中可用usleep(int微秒)nanosleep(…)更精确地查看man page以调用参数

在unix衍生的操作系统中,你可能会安排一个signal()调用,你的代码只会阻塞代码,直到信号被引发。 信号旨在用于此目的,并且它们非常简单和有效。

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

我仍然需要改进这个代码来实现它。 很少有附加组件是受欢迎的。

 // 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  #include  #include  #include  #include  /* 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; } 

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

 #include  #include  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; }