0

launchdシステム ブート (OS X)で実行されるデーモンがあります。デーモンの起動を 3 ~ 5 秒遅らせる必要がありますが、次のコードは起動時にすぐに実行されますが、起動後は適切に遅延します。

#include <unistd.h>
...
printf("Before delay\n");
unsigned int delay = 3000000;
while( (delay=usleep(delay)) > 0)
{
   ;
}
printf("After delay\n");

システムの起動後に手動で実行すると、正しく遅延します。起動launchd時に開始すると、コンソールログは遅延前と遅延後の間に遅延がないことを示します-それらは同じ秒で実行されます。

起動後の遅延の後にデーモンを実行できればそれもlaunchd問題ありませんが、私の読書では、これは不可能であることが示唆されています (おそらく私は間違っていますか?)。

それ以外の場合は、usleep が機能しない理由と、それを修正するために何ができるか、または起動プロセスの早い段階で機能する代わりに使用できる可能性のある遅延を理解する必要があります。

4

2 に答える 2

2

まず最初に。それを行うことに頼るのではなく、現在の時刻も出力するためにいくつかの追加のコードlaunchdを入れてください。

標準出力のフラッシュ動作が異なる可能性があります。

標準出力が対話型デバイス (コマンド ラインからの実行など) であると判断できる場合、それはバッファーされます。遅延の前に「前」の行がフラッシュされます。

そうしないと、完全にバッファリングされるため、プログラムが終了するまで (または、(たとえば) 4K のバッファ サイズに達するまで) フラッシュが発生しない可能性がありlaunchdます。

行にタイムスタンプを付ける C コードを取得すると、次のような問題があるかどうかがわかります。

#include <stdio.h>
#include <time.h>
#include <unistd.h>

int main (void) {
    printf("%d: Before delay\n", time(0));
    unsigned int delay = 3000000;
    while( (delay=usleep(delay)) > 0);
    printf("%d: After delay\n", time(0));

    return 0;
}

バッファリングが問題になる理由を確認するには、上記のプログラムを次のように実行することを検討してください。

pax> ./testprog | while read; do echo $(date): $REPLY; done
Tue Jan 31 12:59:24 WAST 2012: 1327985961: Before delay
Tue Jan 31 12:59:24 WAST 2012: 1327985964: After delay

バッファリングにより、プログラムの終了while時に両方の行がループに表示されるため、プログラム内で 3 秒離れて生成されたにもかかわらず、同じタイムスタンプが取得されることがわかります。12:59:24

実際、次のように変更すると、次のようになります。

pax> ./testprog | while read; do echo $(date) $REPLY; sleep 10 ; done
Tue Jan 31 13:03:17 WAST 2012 1327986194: Before delay
Tue Jan 31 13:03:27 WAST 2012 1327986197: After delay

while「周囲の」プログラム(ループ、またはあなたの場合は)が見た時間はlaunchd、プログラム自体から完全に切り離されていることがわかります)。

第二に、失敗するusleep可能性のある関数です! また、-1 を返すことで失敗する可能性がありますが、これはほとんどゼロより大きくありません。

つまり、失敗した場合、遅延は実質的にゼロになります。

単一 UNIX 仕様には、次のように記載されていますusleep

正常に完了すると、usleep() は 0 を返します。それ以外の場合は、-1 を返し、エラーを示すために errno を設定します。

usleep() 関数は、次の場合に失敗する可能性があります: [EINVAL]: 1,000,000 マイクロ秒以上で指定された時間間隔。

それは確かにあなたのコードに当てはまりますが、起動前ではなく起動後に機能する理由を説明するのは難しいでしょう.

興味深いことに、Mac OSX のドキュメントには EINVAL が記載されていませんが、スリープが外部から中断された場合は EINTR が許可されます。繰り返しますが、確認すべきことがあります。

次のような方法でこれらの可能性を確認できます。

#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>

int main (void) {
    printf("%d: Before delay\n", time(0));
    unsigned int delay = 3000000;
    while( (delay=usleep(delay)) > 0);
    printf("%d: After delay\n", time(0));
    printf("Delay became %d, errno is %d\n", delay, errno);
}

私が気付いたもう1つのことは、コードから、スリープしusleepていない(残りの)マイクロ秒数を返し、すべてが完了するまでループすると想定しているようですが、その動作はマニュアルページによって裏付けられていません。

nanosleepこれは (渡された構造体を更新して、残り時間を返すのではなく含むようにすることで) 行うことを知っていますが、 usleep0 または -1 しか返しません。

このsleep関数はそのように動作し、残りの秒数を返します。可能であれば、代わりにその関数を使用することを検討するかもしれません。

いずれにせよ、実際の問題が何であるかを確認できるように、上記の (最後の) コード セグメントを引き続き実行します。

于 2012-01-31T04:58:21.300 に答える
1

古い POSIX.1 標準に従い、OSX のマニュアル ページに記載されているようにusleep、成功すると 0 を返し、エラーの場合は -1 を返します。

エラーが発生した場合は、シグナルによって中断されたことを意味する可能性が最も高いですEINTR(OSX のマニュアル ページに記載されている唯一のエラー)。errnoただし、確実に確認することをお勧めします。補足として、Linux のマニュアル ページEINVALには、場合によってはあまりにも取得できると記載されています。

usec が 1000000 以上であること (エラーと見なされるシステムの場合)。

別のusleep補足として、 は最新の POSIX.1 標準で廃止され、nanosleep.

于 2012-01-31T06:39:38.347 に答える