Почему usleep не работает во время загрузки? - PullRequest
0 голосов
/ 31 января 2012

У меня есть демон, который 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 ]

2 голосов
/ 31 января 2012

обо всем по порядку. Введите также дополнительный код , чтобы также распечатать текущее время, вместо того, чтобы полагаться на 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, но они do допускают 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 голос
/ 31 января 2012

В соответствии со старым стандартом POSIX.1 и документацией на странице руководства OSX , usleep возвращает 0 в случае успеха и -1 в случае ошибки.

Если вы получили ошибку, скорее всего, EINTR (единственная ошибка, документированная на странице руководства OSX), означающая, что она была прервана сигналом. Вам лучше проверить errno, чтобы быть уверенным. В качестве примечания на странице руководства Linux говорится, что в некоторых случаях вы можете получить EINVAL:

usec не меньше 1000000. (В системах, где это считается ошибкой.)

В качестве еще одного примечания, usleep был отменен в последнем стандарте POSIX.1 в пользу nanosleep.

...