web-dev-qa-db-ja.com

Cでsleep()の代替手段はありますか?

従来の組み込みプログラミングでは、次のような遅延関数を使用します。

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

マイクロプロセッサの見解では、これはsleep()関数がどのように機能するのですか?

Cのsleep()関数に代わるものはありますか?

14
Manoj Doubts

選択肢は、何をしようとしているのか、どのOSを使用しているのかによって異なります。

時間を無駄にしたいだけなら、次のことが役立つかもしれません。

ほとんどのUNIXタイプのシステムでは、「usleep」関数があります。これは、多かれ少なかれ、より高い解像度のスリープに似ています。通常は1マイクロ秒しか眠れないので注意してください。

一部のUNIXタイプのシステムでは、かなり正確な1秒未満の待機を取得するために、すべてのファイル記述子セットをゼロにしてselectシステムコールを使用できます。

Windowsシステムでは、スリープがあります。これはほとんど同じですが、数ミリ秒かかります。

マルチタスクオペレーティングシステムでは、スリープ機能にパラメータとして0を指定できる場合があります。これにより、通常、関数はタイムスライスを放棄しますが、他のタスクを実行する準備ができていない場合は、すぐに再スケジュールされます。

12
Michael Kohne

あなたが説明する種類のループは「ビジーウェイト」と呼ばれます。実際のオペレーティングシステムでは、スリープによってビジーウェイトが発生することはありません。スリープ期間が終了するまでプロセスをスケジュールしないようにオペレーティングシステムに指示します。

25

一般的なメカニズムの1つは、タイムアウトが保証されているselect()を使用し、スリープ時間をタイムアウトとして指定することです。

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

select()は通常、ファイル記述子のセットをチェックし、少なくとも1つがI/Oを実行する準備ができるまで待機するために使用されます。準備ができていない場合(または、この場合、fdsが指定されていない場合)、タイムアウトになります。

ビジーループに対するselect()の利点は、スリープ中にリソースをほとんど消費しないのに対し、ビジーループは優先度レベルで許可されている限りプロセッサを独占することです。

22
Adam Liss

公開したコードを使用して、組み込みシステムでスリープ状態にすることはありません。まともなコンパイラはそれを完全に削除します。コンパイラが削除しなくても、プロセッサをタイトなループで実行すると電力が消費されるため、最適ではありません。これは組み込みシステムの問題です。バッテリーで動作していないシステムでも、電力使用量に注意が必要です。電力使用量が少ないほど、電源と冷却が安価になるためです。

通常これを行う方法は、CPUが何らかのIDLEまたはSLEEP命令を実装し、コマンドの処理を一時的に停止することです。タイマー回路に接続された外部割り込みラインは、プロセッサを一定の間隔でウェイクアップし、CPUが十分な時間スリープしているかどうかを確認し、そうでない場合はスリープ状態に戻ります。

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

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

正確な詳細はプロセッサごとに異なります。 OSでプロセスとして実行している場合、スリープ呼び出しは通常、スケジューラにプロセスを一時停止するように指示するだけで、カーネルは別のプロセスをスケジュールするか、CPUをスリープするかを決定します。また、上記のコードは、期限の保証などが必要なリアルタイムシステムには適していません。そのような場合は、ループ内の時間を取得する必要があります。時間割り込みの期間を知っているので、期限を過ぎて、タイマーハードウェアまたはビジーウェイトを再プログラムする可能性があります。

11
Louis Gerbarg

あなたはOPの「組み込みプログラミング」について話します。組み込み作業を行っていて、sleep()のようなものが必要な場合は、多くの場合、ハードウェアカウンター/タイマーを利用できます。これはアーキテクチャごとに異なるため、データシートを参照してください。

埋め込み作業を行っていない場合は、お詫び申し上げます:)

7
Tony Arkles

Forループを使用している場合は、それらが何にコンパイルされるのか、およびそれらの命令が指定されたクロック速度でどのくらいの時間がかかるのかをよく知っている必要がありますおよび CPUは命令を実行し、他には何も実行しません(これは組み込みシステムで実行できますが、割り込みを許可しないため注意が必要です)。

そうしないと、実際にどれくらいの時間がかかるかを知ることができません。

初期のPCゲームにはこの問題がありました。4.7MHzのPC用に構築されており、より高速なコンピューターが登場すると、プレイできなくなりました。

「スリープ」が機能する最善の方法は、CPUが任意の時点で何時かを知ることです。必ずしも実際の時間(午前7時15分)ではありませんが、少なくとも相対的な時間(ある時点から8612秒)です。

このようにして、現在の時刻にデルタを適用し、current + deltaに達するまでループで待機できます。

CPUが別のタスクに移行し、ループがハングしたままになる可能性があるため、CPUサイクル数に依存するものは本質的に信頼できません。

CPUが1秒に1回インクリメントするメモリマップド16ビットI/Oポートがあるとします。また、組み込みシステムのメモリ位置0x33にあり、intも16ビットであると仮定しましょう。スリープと呼ばれる関数は、次のようになります。

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

Peek()が毎回メモリの内容を返すようにし(最適化コンパイラがロジックを台無しにしないように)、whileステートメントが1秒間に複数回実行されるようにして、ターゲットを見逃さないようにする必要がありますが、これらは私が提示している概念に影響を与えない運用上の問題です。

6
paxdiablo

Sleep()の仕組みに関する詳細情報があります ここ

ちなみに、ビジーウェイトは必ずしもアマチュア向けではありませんが、他の目的で使用したいプロセッサを焼き付けます。タイムソースを使用している場合は、そのソースの粒度に制限されます。例えば。 1ミリ秒のタイマーがあり、500 uSを使用したい場合は、問題があります。組み込みシステムが500uSecのループでブーンという音を処理できるのであれば、それは許容できるかもしれません。また、必要な粒度のタイマーがある場合でも、適切なタイミングでそのタイマーから割り込みを取得する必要があります...次に、割り込みハンドラーをディスパッチします...次に、コードにアクセスします。ビジーループが最も便利な解決策である場合があります。 時々。

6
bog

ビジーウェイトは、組み込みシステムでもアマチュア向けです。リアルタイムソースを使用してください。

5
Ana Betts

まともなCコンパイラは、余分な作業をしなくても、コードを完全に削除し、遅延はなくなります。

4
Scott Evernden

スリープは実際にはオペレーティングシステムとインターフェイスし、スリーププロセスはスケジューリングキューの外に配置されます。私は通常使用します:

poll(0, 0, milliseconds);

pOSIX準拠システムの場合。 selectはWindowsでも機能します(そのためにはネイティブAPI(おそらくSleepと呼ばれる)が必要です)。

3
ididak
#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)しかスリープできないなど、CPUの量に制限されます)。

2
Oskar Dahlberg

linuxで利用可能usleep(int microseconds)nanosleep(...)より正確な引数の呼び出しについてはmanページを参照してください

2
David Nissman

JosephAlbahariによる「ThreadinginC#」の20ページには、これについて興味深い議論があります。 .Netでは1ミリ秒未満スリープすることはできませんが、DateTime.Ticksの粒度は100ナノ秒(= 0.1マイクロ秒)間隔です。 5軸CNCステッパーを制御するために、ステップコマンド間で10マイクロ秒だけ一時停止する必要があります。私はマイクロコントローラーを使用して厄介なループを実行しましたが、とにかくたくさんある場合は、プロセッサーを引き渡しても問題ないと思います。可能な場合はスレッドを停止してください。少なくとも、常に同じになるとは限りません。

2
Patrick

UNIX派生OSでは、おそらくsignal()呼び出しをスケジュールし、コードはシグナルが発生するまでコードをブロックするだけです。信号は目的を目的としており、非常にシンプルで効率的です。

1
dkretz

試み...この問題を本当に解決するために、つまりうまくいくもの(上記の答えの試みとは異なります)笑

私はまだそれを作るためにこのコードを改善する必要があります。いくつかのアドオンは大歓迎です。

// 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;
}
0
user2046229

私はこの投稿で関数を見つけました( 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;
}
0
Rosana Aira