従来の組み込みプログラミングでは、遅延関数を次のように指定します。
for(i=0;i<255;i++)
for(j=0;j<255;j++);
マイクロプロセッサの見解では、これが sleep() 関数の仕組みですか?
C の sleep() 関数に代わるものはありますか?
あなたが説明する種類のループは、「ビジー待機」と呼ばれます。実際のオペレーティング システムでは、スリープしてもビジー待機は発生しません。スリープ期間が終了するまでプロセスをスケジュールしないようにオペレーティング システムに指示します。
一般的なメカニズムの 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()
、スリープ中にリソースをほとんど消費しないことです。一方、ビジー ループは、その優先レベルで許可されている限りプロセッサを独占します。
公開したコードを使用して、組み込みシステムでスリープすることはありません。まともなコンパイラはそれを完全に削除します。コンパイラが削除しなくても、プロセッサをタイトなループで実行すると電力が消費されるため、最適ではありません。これは組み込みシステムの問題です。バッテリーで動作していないシステムでも、電力使用量を気にします。これは、電力使用量が少ないほど、電源と冷却が安価になるためです。
通常これを行う方法は、CPUが何らかのIDLEまたはSLEEP命令を実装し、コマンドの処理を一時的に停止することです。タイマー回路に接続された外部割り込みラインは、プロセッサを定期的にウェイクアップし、CPUが十分な時間スリープしているかどうかを確認し、そうでない場合はスリープに戻ります。
//Pseudo code
int start = getTime();
int end = start + sleepTime;
while (getTime() < end) {
asm("SLEEP");
}
正確な詳細はプロセッサごとに異なります。OSでプロセスとして実行している場合、スリープ呼び出しは通常、スケジューラにプロセスを一時停止するように指示するだけで、カーネルは別のプロセスをスケジュールするか、CPUをスリープするかを決定します。また、上記のコードは、期限の保証などが必要なリアルタイムシステムには適していません。そのような場合は、ループ内の時間を取得する必要があります。時間の中断の長さを知っているので、期限を過ぎて、タイマーハードウェアまたはビジーウェイトを再プログラムする可能性があります。
代替手段は、何をしようとしているのか、どの OS を使用しているかによって異なります。
時間を無駄にしたい場合は、次のものが役立ちます。
ほとんどの UNIX タイプのシステムでは、'usleep' 関数を見つけることができます。これは多かれ少なかれ、より高い解像度のスリープに似ています。通常は 1 マイクロ秒しかスリープできないため、注意してください。
一部の UNIX タイプのシステムでは、select システム コールをすべてのファイル ディスクリプタ セット ゼロで使用して、かなり正確な 1 秒未満の待機を得ることができます。
Windows システムでは、Sleep があります。これはほとんど同じですが、数ミリ秒かかります。
マルチタスク オペレーティング システムでは、スリープ関数にパラメータとして 0 を指定できる場合があります。これにより、通常、関数はそのタイムスライスを放棄しますが、他のタスクを実行する準備ができていない場合はすぐに再スケジュールされます。
OPで「組み込みプログラミング」について話します。組み込み作業を行っていて、sleep() のようなものが必要な場合は、多くの場合、ハードウェア カウンター/タイマーが利用できます。これはアーキテクチャによって異なるため、データシートを参照してください。
埋め込み作業を行っていない場合は、お詫び申し上げます:)
forループを使用している場合は、それらが何にコンパイルされ、指定されたクロック速度でそれらの命令がどれくらいかかるかを知り、CPUが命令のみを実行することを確認する必要があります(これは組み込みシステムで実行できますが、割り込みを許可しないため、注意が必要です)。
そうしないと、実際にどれくらいの時間がかかるかわかりません。
初期の PC ゲームにはこの問題がありました。それらは 4.7MHz PC 用に構築されており、より高速なコンピューターが登場すると、プレイできなくなりました。
「スリープ」が機能する最善の方法は、CPU が任意の時点での時刻を知ることです。必ずしも実際の時間 (午前 7 時 15 分) ではなく、少なくとも相対時間 (ある時点から 8612 秒) です。
そうすれば、デルタを現在の時間に適用し、現在の時間+デルタに達するまでループで待機できます。
CPU サイクル数に依存するものはすべて、CPU が別のタスクに移行してループがハングしたままになる可能性があるため、本質的に信頼性が低くなります。
CPU が 1 秒に 1 回インクリメントする、メモリにマップされた 16 ビット I/O ポートがあるとします。また、組み込みシステムのメモリ位置 0x33 にあり、int も 16 ビットであると仮定しましょう。sleep と呼ばれる関数は次のようになります。
void sleep (unsigned int delay) {
unsigned int target = peek(0x33) + delay;
while (peek(0x33) != target);
}
peek() が毎回メモリの内容を返すことを確認する必要があります (最適化コンパイラがロジックを台無しにしないようにするため)、while ステートメントが 1 秒間に複数回実行されるため、ターゲットを見逃すことはありませんが、私が提示している概念に影響を与えない運用上の問題です。
sleep() がどのように機能するかについての詳細はこちら
ところで、忙しい待機は必ずしもアマチュア向けではありませんが、他の目的に使用したいプロセッサを燃やすことはあります. タイム ソースを使用している場合は、そのソースの粒度に制限されます。たとえば、1 ミリ秒のタイマーがあり、500 μS を使用したい場合、問題が発生します。組み込みシステムが、500 マイクロ秒のループでブーンという音を鳴らしているという事実を処理できる場合、それは許容できるかもしれません。また、希望する粒度のタイマーがある場合でも、適切なタイミングでそのタイマーから割り込みを取得する必要があります...次に、割り込みハンドラーをディスパッチします...そして、コードにアクセスします。ビジー ループが最も適切な解決策である場合があります。 時々。
ビジー待機は組み込みシステムでもアマチュア向けです。リアルタイム ソースを使用してください。
適切な C コンパイラは、余分な作業をしなくても、コードを完全に削除し、遅延はなくなります。
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);
}
Linux で利用可能 usleep( int マイクロ秒 ) nanosleep( ... ) より精度が高い
Joseph Albahari による「Threading in C#」の 20 ページには、これに関する興味深い議論があります。.Net では 1 ミリ秒未満スリープすることはできませんが、DateTime.Ticks の粒度は 100 ナノ秒 (= 0.1 マイクロ秒) 間隔です。私の 5 軸 CNC ステッパーを制御するには、ステップ コマンドの間に 10 マイクロ秒だけ一時停止する必要があります。私は厄介なループを行うためにマイクロコントローラーを使用しましたが、とにかくたくさんある場合はジョブ用のプロセッサーを引き渡しても問題ないと思います。可能な場合はスレッドを停止します。少なくとも、常に同じであるとは限りません。
Unix 派生 OS では、おそらく signal() 呼び出しをスケジュールし、コードはシグナルが発生するまで単純にコードをブロックします。シグナルはその目的のために意図されており、非常にシンプルで効率的です。
この投稿 ( 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;
}
試み...この問題を実際に解決するための試み、つまり機能するもの(上記の回答の試みとは異なります)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;
}