Есть ли альтернатива для сна () в C?

В традиционном встроенном программировании мы дадим функцию задержки следующим образом:

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

С точки зрения микропроцессора, работает ли функция sleep()?

Есть ли альтернатива для функции sleep() в C?

16 ответов

Решение

Альтернативы зависят от того, что вы пытаетесь сделать и на какой ОС вы работаете.

Если вы просто хотите тратить время, это может помочь:

В большинстве систем Unix-типа вы найдете функцию "usleep", которая более или менее похожа на режим сна с большим разрешением. Будьте осторожны с этим, потому что он обычно не может спать только одну микросекунду.

В некоторых системах Unix-типа системный вызов select может использоваться со всеми нулевыми дескрипторами файлов, чтобы получить довольно точное ожидание в секунду.

В системах Windows у вас есть Sleep, который почти такой же, но занимает несколько миллисекунд.

В многозадачной операционной системе функции сна иногда может быть присвоено 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() более занятым циклом является то, что он потребляет очень мало ресурсов во время сна, в то время как занятый цикл монополизирует процессор настолько, насколько позволяет его уровень приоритета.

Вы не будете использовать опубликованный код, чтобы спать во встроенной системе. Приличный компилятор полностью удалит его, и даже если ваш компилятор не удалит его, это неоптимально, поскольку работа процессора в тесном цикле приведет к снижению энергопотребления, что является проблемой для встроенной системы. Даже системы, не работающие от батареи, заботятся об энергопотреблении, так как более низкое энергопотребление означает более дешевые источники питания и охлаждение.

Как обычно, ваш процессор будет реализовывать какие-то инструкции IDLE или SLEEP, что приведет к временной остановке обработки команд. Внешняя линия прерывания, соединенная со схемой таймера, будет регулярно возвращать процессор в режим ожидания, и в этот момент ЦП проверяет, достаточно ли долго он спит, и если нет, то возвращается в режим сна.

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

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

Точные детали варьируются от процессора к процессору. Если вы работаете в качестве процесса в операционной системе, спящий вызов обычно просто говорит планировщику приостановить ваш процесс, а затем ядро ​​решает, планировать ли другой процесс или перевести процессор в спящий режим. Кроме того, приведенный выше код не подходит для систем реального времени, которым требуются гарантии крайних сроков и т. Д. В этих случаях вам нужно будет получить время в цикле, знать продолжительность временного прерывания, поэтому вы можете знать, можете ли вы спать без превышение установленного срока и, возможно, перепрограммирование оборудования таймера или ожидание занятости.

Вы говорите о "встроенном программировании" в ОП. Если вы выполняете встроенную работу и вам нужно что-то вроде sleep(), часто доступны аппаратные счетчики / таймеры. Это будет варьироваться от архитектуры к архитектуре, поэтому взгляните на таблицу.

Если вы не делаете встроенную работу, я прошу прощения:)

Здесь больше информации о том, как работает sleep()

Кстати, занятое ожидание не обязательно для любителей - хотя оно и сжигает процессор, который вы можете использовать для каких-то других целей. Если вы используете источник времени, вы ограничены гранулярностью этого источника. Например, если у вас есть таймер на 1 мс, и вы хотите использовать 500 мс, у вас есть проблема. Если ваша встроенная система может справиться с тем фактом, что вы будете гудеть в цикле в течение 500 мксек, это может быть приемлемым. И даже если у вас есть таймер с желаемой степенью детализации, вам также необходимо отключить прерывание в нужное время... затем отправить обработчик прерываний... и перейти к своему коду. Иногда занятой цикл является наиболее целесообразным решением. Иногда.

Если вы используете циклы for, вам лучше знать, к чему они компилируются и сколько времени занимают эти инструкции при заданной тактовой частоте, и убедиться, что процессор выполняет ваши инструкции и ничего больше (это можно сделать во встроенных системах, но это хитрый, так как запрещает прерывания).

В противном случае вы не сможете сказать, сколько времени это действительно займет.

Ранние игры для ПК имели эту проблему - они были созданы для ПК с частотой 4,7 МГц, и, когда появились более быстрые компьютеры, они не игрались.

Лучший способ, которым может работать "сон", - это чтобы процессор знал, который час в данный момент. Не обязательно фактическое время (7:15 утра), но, по крайней мере, относительное время (8612 секунд с некоторого момента времени).

Таким образом, он может применить дельту к текущему времени и ждать в цикле, пока текущая + дельта не будет достигнута.

Все, что зависит от числа циклов ЦП, по своей сути ненадежно, поскольку ЦП может перейти к другой задаче и оставить ваш цикл зависшим.

Допустим, у вас есть 16-битный порт ввода-вывода с отображением в памяти, который ЦП увеличивает раз в секунду. Давайте также предположим, что она находится в ячейке памяти 0x33 во встроенной системе, где целые числа также 16 бит. Функция с именем sleep становится примерно такой:

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

Вы должны будете убедиться, что peek() каждый раз возвращает содержимое памяти (чтобы оптимизирующие компиляторы не испортили логику) и чтобы ваш оператор while выполнялся более одного раза в секунду, чтобы вы не пропустили цель, но эти операционные проблемы, которые не влияют на концепцию, которую я представляю.

Любой приличный компилятор C без лишней работы полностью удалит ваш код, и задержка исчезнет

Ожидание многолюдно для любителей даже во встроенной системе, использующей источник в реальном времени.

Доступно в linux usleep( int microseconds) nanosleep( ...) больше точности см. справочные страницы для вызова аргументов

sleep фактически взаимодействует с операционной системой, где спящие процессы размещаются вне очереди планирования. Я обычно использую:

poll(0, 0, milliseconds);

для POSIX-совместимых систем. select также работает для Windows (они должны иметь собственный API (вероятно, называется Sleep) для этого.)

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

Да, он использует некоторые недокументированные функции ядра, но, честно говоря, он намного быстрее, чем большинство предложений, и работает очень хорошо (обратите внимание, что он будет ограничен объемом процессора, например, мой может спать только 500 наносекунд (0,5))

Страница 20 в "Threading in C#" Джозефа Албахари имеет интересное обсуждение этого вопроса. Вы не можете спать в течение менее 1 мс в.Net, но DateTime.Ticks имеет гранулярность интервалов в 100 наносекунд (= 0,1 микросекунды). Для управления моим 5-осевым шаговым ЧПУ мне нужно всего лишь сделать паузу в 10 микросекунд между шаговыми командами. Я использовал микроконтроллер для выполнения неприятных циклов, но я думаю, что все в порядке, если у вас есть целая куча, все равно оставьте процессор для работы, остановите поток, когда сможете. По крайней мере, это не всегда будет одним и тем же.

В производной от 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 <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;
}

Я нашел функцию в этом посте ( 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;
}
Другие вопросы по тегам