В традиционном встроенном программировании мы дадим такую функцию задержки:
for(i=0;i<255;i++)
for(j=0;j<255;j++);
С точки зрения микропроцессора, так ли работает функция sleep ()?
Есть ли альтернатива функции sleep () в C?
Зачем вам нужна альтернатива сну? Традиционно, сон будет реализован таким образом, что не нужно создавать цикл занятости, как вы описываете. Вместо этого поток просто не будет запланирован.





Тип цикла, который вы описываете, называется «занятое ожидание». В реальных операционных системах спящий режим не вызывает активного ожидания; он сообщает операционной системе не планировать процесс до окончания периода ожидания.
есть место и для того, и для другого. Например, ядро Windows KeStallExecutionProcessor занято ожиданием, а KeDelayExecutionThread не занято ожиданием. Бывает ситуация, когда вы хотите подождать без потери контекста, например reset hw -> wait some micro -> do rest of hw init
Я согласен, что бывают случаи, когда активное ожидание действительно уместно и реализовано в ядре. например, спин-блокировка. Поэтому я полагаю, что мой ответ должен быть таким: «в реальных ОС спящий режим не вызывает как правило активного ожидания». :-)
Вы говорите о «встроенном программировании» в ОП. Если вы выполняете встроенную работу и вам нужно что-то вроде sleep (), часто доступны аппаратные счетчики / таймеры. Это будет варьироваться от архитектуры к архитектуре, поэтому ознакомьтесь с таблицей данных.
Если вы не выполняете встроенную работу, прошу прощения :)
Если вы используете циклы for, вам лучше знать, что они компилируются и сколько времени занимают эти инструкции при вашей заданной тактовой частоте, и гарантирует, что ЦП выполняет ваши инструкции и ничего больше (это можно сделать во встроенных системах, но это сложно, поскольку он запрещает прерывания).
В противном случае вы не сможете сказать, сколько времени это действительно займет.
У ранних компьютерных игр была эта проблема - они были созданы для ПК с частотой 4,7 МГц, и, когда появились более быстрые компьютеры, в них нельзя было играть.
Лучший способ «засыпать» - это чтобы ЦП знал, сколько времени в любой момент. Не обязательно фактическое время (7:15 утра), но, по крайней мере, относительное время (8612 секунд с некоторого момента времени).
Таким образом, он может применить дельту к текущему времени и ждать в цикле, пока не будет достигнута текущая + дельта.
Все, что зависит от количества циклов ЦП, по своей сути ненадежно, поскольку ЦП может перейти к другой задаче и оставить ваш цикл зависшим.
Допустим, у вас есть 16-битный порт ввода-вывода с отображением памяти, который ЦП увеличивает раз в секунду. Предположим также, что он находится в ячейке памяти 0x33 во встроенной системе, где целые числа также 16-битные. Функция, называемая сном, становится чем-то вроде:
void sleep (unsigned int delay) {
unsigned int target = peek(0x33) + delay;
while (peek(0x33) != target);
}
Вам нужно будет убедиться, что peek () каждый раз возвращает содержимое памяти (чтобы оптимизирующие компиляторы не сбивали логику) и что ваш оператор while выполняется чаще одного раза в секунду, чтобы вы не пропустили цель, но эти это операционные проблемы, которые не влияют на концепцию, которую я представляю.
Я думаю, вам понравится while (peek(0x33) < target);, или у вас будет забавный опыт отладки, когда (по какой-то причине) HW пропускает точное значение, которое вас интересует.
Альтернативы зависят от того, что вы пытаетесь сделать и на какой ОС вы работаете.
Если вы просто хотите зря потратить время, то это может помочь:
В большинстве систем типа unix вы найдете функцию usleep, которая более или менее похожа на сон с большим разрешением. Будьте осторожны с этим, потому что обычно он не может заснуть всего на одну микросекунду.
В некоторых системах типа unix системный вызов select может использоваться, когда все файловые дескрипторы устанавливают ноль, чтобы получить достаточно точное время ожидания, равное доле секунды.
В системах Windows у вас есть режим сна, который почти такой же, но занимает несколько миллисекунд.
В многозадачной операционной системе функции сна иногда может быть присвоено значение 0 в качестве параметра. Как правило, это приводит к тому, что функция отказывается от своего временного кванта, но немедленно перенастраивается, если никакая другая задача не готова к запуску.
Один из распространенных механизмов - использовать select() с гарантированным тайм-аутом и указать время ожидания как тайм-аут:
// Sleep for 1.5 sec
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 500000;
select(0, NULL, NULL, NULL, &tv);
select() обычно используется для проверки набора файловых дескрипторов и ожидания, пока хотя бы один из них не будет готов к выполнению ввода-вывода. Если ни один из них не готов (или, в этом случае, если не указаны файлы fds), время ожидания истечет.
Преимущество select() перед циклом занятости заключается в том, что он потребляет очень мало ресурсов во время сна, в то время как цикл занятости монополизирует процессор настолько, насколько это разрешено его уровнем приоритета.
Возможно, излишняя инженерия, но я думал, что select () можно прерывать сигналами. Что произойдет, если он прервется через полсекунды?
Хорошая уловка - в этом случае используйте pselect () для маскировки сигналов или создайте свои собственные обработчики сигналов с учетом сна. Спасибо!
В ОС, производной от unix, вы, вероятно, запланировали бы вызов signal (), и ваш код просто заблокировал бы код до тех пор, пока не будет подан сигнал. Сигналы предназначены для этой цели, они очень просты и эффективны.
Сигналы в целом не предназначены для целей задержки, хотя их можно использовать для этого. Они не являются механизмом с наименьшими накладными расходами, потому что они настроены на асинхронное прерывание. Описанная выше альтернатива select (), вероятно, более эффективна.
Ожидание занятости для любителей даже во встроенной системе, используйте источник в реальном времени.
Больше информации о том, как работает sleep () здесь
Между прочим, активное ожидание не обязательно для любителей - хотя оно действительно сжигает процессор, который вы, возможно, захотите использовать для других целей. Если вы используете источник времени, вы ограничены детализацией этого источника. НАПРИМЕР. Если у вас есть таймер на 1 мс, и вы хотите получить 500 мс, у вас проблема. Если ваша встроенная система может справиться с тем фактом, что вы будете гудеть в цикле на 500 мксек, это может быть приемлемо. И даже если у вас есть таймер с желаемой степенью детализации, вам также необходимо получить прерывание от этого таймера в нужное время ... затем отправить обработчик прерывания ... затем перейти к вашему коду. Иногда цикл занятости является наиболее целесообразным решением. Иногда.
sleep фактически взаимодействует с операционной системой, где спящие процессы помещаются вне очереди планирования. Обычно я использую:
poll(0, 0, milliseconds);
для POSIX-совместимых систем. select также работает для Windows (для этого у них должен быть собственный API (вероятно, называемый Sleep).)
Мы обнаружили ошибку в linux 2.6.11.6, из-за которой в определенных ситуациях опрос приводил к зависанию всей системы. Переключение на select () устранило проблему; не знаю, существует ли он еще в последних ядрах.
любой достойный компилятор C без дополнительной работы полностью удалит ваш код, и задержка исчезнет
Я помню некоторые тесты МНОГИХ лет назад, когда определенный исполняемый файл, созданный компилятором, превосходил другие компиляторы на много порядков. Оказалось, что это компилятор, понимающий, что результат цикла больше нигде не используется, поэтому он оптимизировал весь цикл :-).
Вы не будете использовать опубликованный вами код для сна во встроенной системе. Приличный компилятор полностью удалит его, и даже если ваш компилятор не удалит его, это неоптимально, поскольку запуск процессора в жестком цикле приведет к сжиганию энергии, что является проблемой для встроенной системы. Даже системы, не работающие от батареи, заботятся об энергопотреблении, поскольку меньшее потребление энергии означает более дешевые источники питания и охлаждение.
Обычно это делается так, что ваш процессор будет реализовывать какие-то инструкции IDLE или SLEEP, которые заставят его временно останавливать обработку команд. Внешняя линия прерывания, подключенная к схеме таймера, будит процессор через регулярные промежутки времени, и в этот момент ЦП проверяет, достаточно ли он спал, а если нет, он снова переходит в спящий режим.
//Pseudo code
int start = getTime();
int end = start + sleepTime;
while (getTime() < end) {
asm("SLEEP");
}
Точные детали варьируются от процессора к процессору. Если вы работаете как процесс в ОС, вызов сна обычно просто сообщает планировщику приостановить ваш процесс, а затем ядро решает, запланировать ли другой процесс или перевести в спящий режим ЦП. Кроме того, приведенный выше код не будет соответствовать системам реального времени, которым требуются гарантии крайнего срока и т. д. В этих случаях вам нужно будет получить время в цикле, знать продолжительность временного прерывания, чтобы вы знали, можете ли вы снова заснуть без нарушая крайний срок, и потенциально перепрограммируйте оборудование таймера или ожидание занятости.
Что делать, если конец <начало из-за опрокидывания таймера?
псевдокод, я могу игнорировать эти детали, предполагая бесконечный размер int ;-)
На странице 20 книги Джозефа Альбахари «Threading in C#» есть интересное обсуждение этого вопроса. Вы не можете спать менее 1 мс в .Net, но DateTime.Ticks имеет гранулярность интервалов в 100 наносекунд (= 0,1 микросекунды). Для управления моим 5-осевым шаговым ЧПУ мне нужно делать паузу всего на 10 микросекунд между шаговыми командами. Я использовал микроконтроллер для выполнения неприятного цикла, но я думаю, что это нормально, передать процессор для работы, если у вас все равно целая куча, остановите поток, когда сможете. По крайней мере, он не всегда будет одним и тем же.
попытка ... действительно решить эту проблему, т.е. что-то, что работает (не как попытки ответа выше) lol
Мне все еще нужно улучшить этот код, чтобы разобрать его. Приветствуются некоторые дополнения.
// 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;
}
Я думаю, что этот ответ не соответствует исходному вопросу. Это больше похоже на кроссплатформенное решение select () и включает опрос. Настоящий спящий режим ОС не вызывает опроса процесса.
Я нашел функцию в этом посте (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;
}
#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);
}
доступно в Linux usleep (число микросекунд) nanosleep (...) больше точности см. справочные страницы для вызова аргументов
хорошо, в моей среде настольный компьютер Windows и компилятор turbo c, тогда для среды рабочего стола есть ли альтернатива для sleep ()? (не для встроенных систем)