2017-02-20 24 views
0

Я пишу acquisitioning данные программы, которая должнаC - выберите(), кажется, блок дольше, чем тайм-аут

  • засаде сериала с некоторыми()

  • считанных последовательных данных (RS232 в 115200 бод),

  • метка времени он (clock_gettime()),

  • чтения АЦП на SPI,

  • интерпретировать,

  • отправить новые данные поверх другого TTY устройства

  • петли и повторить

АЦП не имеет значения, на данный момент.

В конце цикла я снова использую select() с 0 таймаутом для опроса и посмотреть, доступны ли данные уже, если это означает, что у меня переполнение, I.e. Я ожидаю, что цикл завершится раньше, чем больше данных, и для select() в начале цикла заблокировать и получить его, как только он поступит.

Данные должны поступать каждые 5 мс, мой первый тайм-аут select() рассчитывается как (5,5 мс - время цикла) - это должно быть около 4 мс.

У меня нет таймаутов, но много перерасходов.

Рассмотрение временных меток показывает, что select() блокирует дольше, чем таймаут (но все же возвращает> 0). Похоже, что select() возвращает позже после получения данных до таймаута.

Это может случиться 20 раз в 1000 повторов. Что может быть причиной? Как это исправить?

EDIT: Вот урезанная версия кода (! Я гораздо больше контроля ошибок, чем это)

#include <bcm2835.h> /* for bcm2835_init(), bcm2835_close() */ 

int main(int argc, char **argv){ 

    int err = 0; 

    /* Set real time priority SCHED_FIFO */ 
    struct sched_param sp; 
    sp.sched_priority = 30; 
    if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)){ 
     perror("pthread_setschedparam():"); 
     err = 1; 
    } 

    /* 5ms between samples on /dev/ttyUSB0 */ 
    int interval = 5; 

    /* Setup tty devices with termios, both totally uncooked, 8 bit, odd parity, 1 stop bit, 115200baud */ 
    int fd_wc=setup_serial("/dev/ttyAMA0"); 
    int fd_sc=setup_serial("/dev/ttyUSB0"); 

    /* Setup GPIO for SPI, SPI mode, clock is ~1MHz which equates to more than 50ksps */ 
    bcm2835_init(); 
    setup_mcp3201spi(); 

    int collecting = 1; 

    struct timespec starttime; 
    struct timespec time; 
    struct timespec ftime; 
    ftime.tv_nsec = 0; 

    fd_set readfds; 
    int countfd; 
    struct timeval interval_timeout; 
    struct timeval notime; 

    uint16_t p1; 
    float w1; 

    uint8_t *datap = malloc(8); 
    int data_size; 
    char output[25]; 

    clock_gettime(CLOCK_MONOTONIC, &starttime); 

    while (!err && collecting){ 
     /* Set timeout to (5*1.2)ms - (looptime)ms, or 0 if looptime was longer than (5*1.2)ms */ 
     interval_timeout.tv_sec = 0; 
     interval_timeout.tv_usec = interval * 1200 - ftime.tv_nsec/1000; 
     interval_timeout.tv_usec = (interval_timeout.tv_usec < 0)? 0 : interval_timeout.tv_usec; 
     FD_ZERO(&readfds); 
     FD_SET(fd_wc, &readfds);  
     FD_SET(0, &readfds); /* so that we can quit, code not included */ 
     if ((countfd=select(fd_wc+1, &readfds, NULL, NULL, &interval_timeout))<0){ 
      perror("select()"); 
      err = 1; 
     } else if (countfd == 0){ 
      printf("Timeout on select()\n"); 
      fflush(stdout); 
      err = 1; 
     } else if (FD_ISSET(fd_wc, &readfds)){ 
      /* timestamp for when data is just available */ 
      clock_gettime(CLOCK_MONOTONIC, &time) 
      if (starttime.tv_nsec > time.tv_nsec){ 
       time.tv_nsec = 1000000000 + time.tv_nsec - starttime.tv_nsec; 
       time.tv_sec = time.tv_sec - starttime.tv_sec - 1; 
      } else { 
       time.tv_nsec = time.tv_nsec - starttime.tv_nsec; 
       time.tv_sec = time.tv_sec - starttime.tv_sec; 
      } 

      /* get ADC value, which is sampled fast so corresponds to timestamp */ 
      p1 = getADCvalue(); 

      /* receive_frame, receiving is slower so do it after getting ADC value. It is timestamped anyway */ 
      /* This function consists of a loop that gets data from serial 1 byte at a time until a 'frame' is collected. */ 
      /* it uses select() with a very short timeout (enough for 1 byte at baudrate) just to check comms are still going */ 
      /* It never times out and behaves well */ 
      /* The interval_timeout is passed because it is used as a timeout for responding an ACK to the device */ 
      /* That select also never times out */ 
      ireceive_frame(&datap, fd_wc, &data_size, interval_timeout.tv_sec, interval_timeout.tv_usec); 

      /* do stuff with it */ 
      /* This takes most of the time in the loop, about 1.3ms at 115200 baud */ 
      snprintf(output, 24, "%d.%04d,%d,%.2f\n", time.tv_sec, time.tv_nsec/100000, pressure, w1); 
      write(fd_sc, output, strnlen(output, 23)); 

      /* Check how long the loop took (minus the polling select() that follows */ 
      clock_gettime(CLOCK_MONOTONIC, &ftime); 
      if ((time.tv_nsec+starttime.tv_nsec) > ftime.tv_nsec){ 
       ftime.tv_nsec = 1000000000 + ftime.tv_nsec - time.tv_nsec - starttime.tv_nsec; 
       ftime.tv_sec = ftime.tv_sec - time.tv_sec - starttime.tv_sec - 1; 
      } else { 
       ftime.tv_nsec = ftime.tv_nsec - time.tv_nsec - starttime.tv_nsec; 
       ftime.tv_sec = ftime.tv_sec - time.tv_sec - starttime.tv_sec; 
      } 

      /* Poll with 0 timeout to check that data hasn't arrived before we're ready yet */ 
      FD_ZERO(&readfds); 
      FD_SET(fd_wc, &readfds); 
      notime.tv_sec = 0; 
      notime.tv_usec = 0; 
      if (!err && ((countfd=select(fd_wc+1, &readfds, NULL, NULL, &notime)) < 0)){ 
       perror("select()"); 
       err = 1; 
      } else if (countfd > 0){ 
       printf("OVERRUN!\n"); 
       snprintf(output, 25, ",,,%d.%04d\n\n", ftime.tv_sec, ftime.tv_nsec/100000); 
       write(fd_sc, output, strnlen(output, 24)); 
      } 

     } 

    } 


    return 0; 

} 

Метки времени я вижу на последовательный поток, выход я довольно регулярно (отклонение обычно улавливается следующим циклом). Отрывок выхода:

6.1810,0,225.25 
6.1867,0,225.25 
6.1922,0,225.25 
6,2063,0,225.25 
,,,0.0010 

Здесь, до 6.1922s, все в порядке. Следующий образец равен 6.2063 - 14.1ms после последнего, но он не ушел в прошлое, и предыдущий цикл от 6.1922-6.2063 не поймал переполнение с помощью select(). Мой вывод состоит в том, что в последнем цикле было время выборки, и выбор взял -10ms слишком долгое возвращение без тайм-аута.

The ,,, 0.0010 указывает время цикла (ftime) цикла после - я действительно должен проверять, какое время цикла было, когда оно пошло не так. Я попробую это завтра.

+0

Как вы устанавливаете свои маски ?, и передаете нулевой указатель на аргумент времени или устанавливаете значения struct в ноль. Каждый из них приводит к собственному поведению. – ryyker

+0

Возможно, вам придется включить _ [SSCCE] (http://sscce.org/) _, чтобы проиллюстрировать проблему. Очевидно, есть что-то другое, кроме проблемы с тайм-аутом, вызывающей вашу проблему. Это также привлечет более широкую аудиторию. – ryyker

+0

Добавлена ​​малина-пи к тегам. – ryyker

ответ

1

Тайм-аут, пройденный до select, является грубой нижней границей - select разрешено задерживать ваш процесс чуть больше.В частности, ваш процесс будет задерживаться, если он будет выгружен другим процессом (коммутатором контекста) или обработкой прерываний в ядре.

Вот что страница Linux руководство имеет сказать по этой теме:

Обратите внимание, что интервал ожидания будет округлена до системных часов зернистости, и задержки планирования ядра означает, что блокирующий интервал может занять небольшую сумму.

А вот стандарт POSIX:

Реализация может также поставить ограничения на зернистости интервалов времени ожидания. Если запрошенный интервал ожидания требует более тонкой детализации, чем поддержка реализации , фактический интервал времени ожидания должен быть , округленный до следующего поддерживаемого значения.

Избежать этого трудно в системе общего назначения. Вы получите разумные результаты, особенно в многоядерной системе, заблокировав свой процесс в памяти (mlockall) и установив свой процесс на приоритет в реальном времени (используйте sched_setscheduler с SCHED_FIFO и не забудьте заснуть достаточно часто, чтобы дать другим процессам шанс запустить).

Более сложный подход заключается в использовании микроконтроллера реального времени, предназначенного для запуска кода реального времени. Некоторые люди заявляют об этом reliably sample at 20MHz on fairly cheap hardware.

+0

Я читал, что, следовательно, мой комментарий, спрашивающий об упреке на рийкерах, отвечает. Выберете() не время, если это произойдет, а только поздно? И может ли быть так много, что он переполняется на 5 мс? Я уже установил SCHED_FIFO с небольшой разницей.Я попробую завтракать завтра (хотя я использую очень мало памяти). Я вообще не сплю, но я бы подумал, что просто провести время в select() делает то же самое. Я всего лишь 250 примеров/сек здесь –

+0

Да, я не удивлюсь плохо написанному драйверу, вводящему латентности порядка 10 мс. Похоже, что драйвер SPI на RPi имеет некоторые проблемы - см. Https://www.raspberrypi.org/forums/viewtopic.php?t=19489 – jch

+0

Я использую библиотеку bcm2835 (http: //www.airspayce. com/mikem/bcm2835 /), который не имеет одинаковых проблем (я считаю). Я постараюсь, не прочитав из АЦП завтра, хотя я видел сообщения гораздо больше, чем 200 образцов в секунду. В ближайшее время я отредактирую свой вопрос с помощью некоторого кода. –

1

Если значения для struct timeval установлены в ноль, то select не будет блокировать, но если аргумент тайм-аута является указателем NULL, он будет ...

Если тайм-аут аргумент не NULL указатель, он указывает на объект структуры типа, формата: первый формат определяет максимальный интервал для ожидания для выбора, чтобы закончить. Если аргумент таймаута указывает на объект типа struct timeval, члены которого равны 0, select() делает не блочным. Если аргумент таймаута является указателем NULL, выберите() блокирует до тех пор, пока событие не приведет к возврату одной из масок с действительным (отличным от нуля) значением или до тех пор, пока не появится сигнал, который должен быть отправлен . Если срок истекает до того, как происходит событие, что бы вызвать один из масок должен быть установлен в ненулевое значение, выберите() завершается успешно и возвращает 0.

Подробнее here

EDIT обратиться комментарии, и добавить новую информацию:

пару примечательных точек.

Первый - в комментариях есть предложение добавить sleep() в рабочий цикл. Это хорошее предложение. Причины stated here, хотя они имеют дело с точками ввода потоков, по-прежнему применяются, поскольку вы создаете непрерывный цикл.

Второго - Linux select() системный вызов с интересной историей implemantation, и как таковые имеет целый ряд различных поведений от реализации к реализации, некоторые из которых может способствовать неожиданному поведению, которые вы видите. Я не уверен, какой из основных кровных линий Linux Arch Linux приходит, но man7.org page for select() включает в себя следующие два сегмента, которые в описаниях появляются, чтобы описать условия, которые могли бы внести вклад в задержки вы испытываете.

Bad контрольная сумма:

Under Linux, select() may report a socket file descriptor as "ready 
for reading", while nevertheless a subsequent read blocks. This could 
for example happen when data has arrived but upon examination has wrong 
checksum and is discarded. 

Race состояние: (вводит и обсуждает pselect())

...Suppose the signal handler sets a global flag and returns. Then a test 
of this global flag followed by a call of select() could hang indefinitely 
if the signal arrived just after the test but just before the call... 

Учитывая описание ваших наблюдений, а также в зависимости от того, как вашей версии Linux реализован, либо одна из этих реализаций функций может быть возможным автором.

+0

Я думаю, что вы ошибочно, но с таймаутом, установленным в 0, он не будет блокироваться. Это то, что я ожидаю, и то, что я получаю на своем втором select() в конце цикла. Проблема заключается в первом select() с ненулевым таймаутом. Он возвращается намного позже таймаута (иногда 5 мс) без тайм-аута. Я предполагаю, что данные были получены до таймаута, но не могут подтвердить. –

+0

@StefanHartman - Да, спасибо. Я просто поймал неправильный тип. Он считает, что сейчас это правильно. Убедитесь, что вы правильно устанавливаете маски. Я предполагаю, что все это происходит в однопоточном приложении? – ryyker

+0

Да, однопоточный. Маски и таймауты устанавливаются и сбрасываются правильно с каждым повторением цикла. Возможно ли, что поток был выгружен когда-то во время select(), который заставляет его возвращаться позже без тайм-аута? Это на малиновой пи. –

0

Извините, я не могу комментировать (недостаточно), но сколько у вас есть fd ??? Вы уверены, что вы установили fd_max + 1 в select() ?? Однажды я сделал такую ​​ошибку. И какие данные вы отправляете? выберите, просто скажет, доступен ли он для чтения, но он может быть переполнен?