为什么usleep在启动时不工作?

时间:2012-01-31 04:45:02

标签: macos delay sleep boot launchd

我有一个守护进程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不起作用,我可以做些什么来解决它,或者我可以使用什么延迟而不是在启动过程的早期工作。

2 个答案:

答案 0 :(得分:2)

首先要做的事情。添加一些额外的代码以打印当前时间,而不是依靠launchd来执行此操作。

标准输出的不同冲洗行为可能会发挥作用。

如果可以确定标准输出是交互式设备(例如从命令行运行它),则 line 缓冲 - 您将在延迟之前刷新“之前”行

否则,它是完全缓冲的,因此在程序退出之前(或者达到(例如)4K的缓冲区大小时,可能不会发生刷新。这意味着launchd可能会看到线条一起出现,两者< em>延迟之后。

获取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失败,这非常不大于零。

这意味着,如果失败,你的延迟实际上就没有了。

usleep的{​​{3}}州:

  

成功完成后,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函数以这种方式起作用,返回尚未开始的秒数。如果可能的话,也许您可​​能会考虑使用该功能。

在任何情况下,我仍会运行上面的(最后一个)代码段,以便您可以确定实际问题是什么

答案 1 :(得分:1)

根据旧的POSIX.1标准,并且如OSX manual page中所述,usleep成功时返回0,错误时返回-1。

如果您收到错误,则最有可能是EINTR(OSX手册页中记录的唯一错误),这意味着它已被信号中断。你最好检查一下errno是否确定。作为旁注,在Linux manual page上,它表明在某些情况下您也可以获得EINVAL

  

usec不小于1000000.(在被认为是错误的系统上。)

另一方面,usleep已在最新的POSIX.1标准中被使用,有利于nanosleep