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时两条线都出现在循环中,尽管它们在程序中相隔三秒生成,但它们得到相同的时间戳。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);
}

我刚刚注意到的另一件事是,从您的代码中,您似乎假设usleep返回未睡眠(剩余)的微秒数,然后循环直到全部完成,但是手册页并未证实这种行为。

我知道这样nanosleep做(通过更新传递的结构以包含剩余时间而不是返回它)但usleep只返回 0 或 -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 回答