2016-03-01 3 views
0

Linux manpage для SO_RCVTIMEO говорит:SO_RCVTIMEO просыпается слишком рано

Укажите получение или отправка не таймаут до сообщения об ошибке ... Если функции ввода или вывода блоков за этот период времени ... [и ] никакие данные не были переданы и тайм-аут был достигнут, то возвращается -1 с ERRNO набор для EAGAIN или EWOULDBLOCK или EINPROGRESS (для подключения (2))

Это звучит для меня как I/O должен ждать не менее SO_RCVTIMEO перед возвратом выполнения вызывающему абоненту. Между тем, at the Open Group, они документируют обратное:

Устанавливает значение тайм-аута, который определяет количество времени функции входа ждет, пока он не закончит максимум.

Какое значение минимальное время блокировки или максимальное время блокировки? Ответ выглядит так: да. Вот что происходит, когда я прошу .500s тайм-аут на системе Linux:

time: 0.497054 result: 0 
time: 0.495352 result: 0 
time: 0.504948 result: 0 
time: 0.495119 result: 0 
time: 0.507884 result: 0 
time: 0.491892 result: 0 
time: 0.500764 result: 0 

Мы видим, что время неправильно, обычно так же, как 7мс или так, что долгое время, чтобы быть неправильно, , И ошибка происходит в обоих направлениях. В то же время на Дарвина:

time: 0.500426 result: -1 
time: 0.501144 result: -1 
time: 0.500507 result: -1 
time: 0.501119 result: -1 
time: 0.501016 result: -1 
time: 0.500540 result: -1 
time: 0.500127 result: -1 
time: 0.500815 result: -1 
time: 0.500341 result: -1 
time: 0.500871 result: -1 
time: 0.500835 result: -1 
time: 0.501138 result: -1 
time: 0.501087 result: -1 
time: 0.501153 result: -1 
time: 0.501149 result: -1 

Ошибка гораздо ниже (~ 1 мс), но по-прежнему существует, и они четко интерпретировать 500мс как минимальное время, не максимальным.

Теперь некоторые вопросы:

  • ли SO_RCVTIMEO должна быть минимальная или максимальная продолжительность блокировки абонента?
  • Если это максимальная продолжительность, то какой минимум? Разумеется, реализация не является бесплатной для выбора неблокирующего чтения при запросе на 500 мс тайм-аута?
  • Если это минимальная продолжительность, Дарвин ошибается?
  • Если я хочу гарантировать, что я пытался читать за как минимум 500 мс, я должен продолжать пытаться в цикле до истечения 500 мс? Что такое «правильный путь» для реализации поведения «по крайней мере X ms»?
  • Почему существует множество вариаций от вызова к вызову в Linux? Каков источник ошибки?
  • Есть ли лучший API, который я должен использовать для чтения из сокетов?

код я использовал для измерения этого:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <time.h> 
#include <fcntl.h> 

#ifdef __MACH__ 
#include <mach/clock.h> 
#include <mach/mach.h> 
#endif 
void error(const char *msg) 
{ 
    perror(msg); 
    exit(1); 
} 

struct timespec os_time() { 
    struct timespec ts; 
    #ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time 
    clock_serv_t cclock; 
    mach_timespec_t mts; 
    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); 
    clock_get_time(cclock, &mts); 
    mach_port_deallocate(mach_task_self(), cclock); 
    ts.tv_sec = mts.tv_sec; 
    ts.tv_nsec = mts.tv_nsec; 

    #else 
    clock_gettime(CLOCK_REALTIME, &ts); 
    #endif 
    return ts; 
} 

int main(int argc, char *argv[]) 
{ 
    int sockfd, newsockfd, portno; 
    socklen_t clilen; 
    char buffer[256]; 
    struct sockaddr_in serv_addr, cli_addr; 
    int n; 
    if (argc < 2) { 
     fprintf(stderr,"ERROR, no port provided\n"); 
     exit(1); 
    } 
    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd < 0) 
     error("ERROR opening socket"); 
    bzero((char *) &serv_addr, sizeof(serv_addr)); 
    portno = atoi(argv[1]); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr = INADDR_ANY; 
    serv_addr.sin_port = htons(portno); 
    if (bind(sockfd, (struct sockaddr *) &serv_addr, 
       sizeof(serv_addr)) < 0) 
       error("ERROR on binding"); 
    listen(sockfd,5); 
    clilen = sizeof(cli_addr); 
    newsockfd = accept(sockfd, 
       (struct sockaddr *) &cli_addr, 
       &clilen); 
    if (newsockfd < 0) 
      error("ERROR on accept"); 
    for (int i = 0; i < 100; i++) { 
     struct timeval tv; 

     tv.tv_sec = 0; 
     tv.tv_usec = 500000; 
     char buf[1]; 
     if (setsockopt(newsockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)) != 0){ 
      error("setsockopt error"); 
     } 
     struct timespec start = os_time(); 
     int result = recv(newsockfd,buf,1,0); 
     struct timespec end = os_time(); 

     double end_time = (double)end.tv_sec + ((double)end.tv_nsec)/1.0E9; 
     double start_time = (double)start.tv_sec + ((double)start.tv_nsec)/1.0E9; 
     printf("time: %f result: %d\n",end_time-start_time, result); 
    } 
    return 0; 
} 

Размножение:

clang test.c && ./a.out 5551 & 
telnet localhost 5551 
time: 0.497839 result: 0 
time: 0.501052 result: 0 
time: 0.498565 result: 0 
time: 0.500741 result: 0 
time: 0.500108 result: 0 
time: 0.500244 result: 0 
time: 0.499040 result: 0 
time: 0.500212 result: 0 
time: 0.500137 result: 0 
time: 0.499920 result: 0 
time: 0.500758 result: 0 
time: 0.498068 result: 0 

ответ

3

Это звучит для меня, как и I/O должны ждать по крайней мере SO_RCVTIMEO перед возвращением выполнения в вызывающего абонента.

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

В то же время, в Open Group, они документируют наоборот:

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

Какое значение минимальное время блокировки или максимальное время блокировки?

Максимальное время блокировки.

они четко интерпретируют 500 мс как минимум, а не максимум.

Здесь вы спрашиваете и тестируете два разных вопроса: разрешение таймера и скорость, с которой операционная система перенастраивает поток после таймаута. Также не указано.

Предполагается, что SO_RCVTIMEO является минимальной или максимальной продолжительностью для блокировки вызывающего абонента?

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

Если это максимальная продолжительность, то какой минимум?

Zero.

Несомненно, реализация не является бесплатной для чтения без блокировки при запросе на 500 мс тайм-аута?

Конечно, это так. Если данные уже присутствуют в буфере приема сокетов, recv() передает эти данные и немедленно возвращается. Зачем это ждать?

Если это минимальная продолжительность, Дарвин ошибается?

Нет, у него просто другое разрешение и задержка перепланирования.

Если я хочу гарантировать, что пытался прочитать хотя бы 500 мс, я должен продолжать пытаться в цикле до истечения 500 мс? Что такое «правильный путь» для реализации поведения «по крайней мере X ms»?

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

Почему существует множество вариаций от вызова к вызову в Linux? Каков источник ошибки?

Таймер джиттера; реструктурирующий джиттер.Это не операционная система реального времени

Есть ли лучший API, который я должен использовать для чтения из сокетов?

Определить «лучше». Ваши ожидания кажутся довольно странными. Этот API был достаточно хорош для всех более 30 лет.

+0

«Если данные уже существуют или до истечения времени ожидания, метод возвращается в этой точке, не дожидаясь истечения времени ожидания». Я понимаю это. Ситуация, о которой я спрашиваю, - это когда данные НЕ поступают. В этом случае Linux дает 490 мс в тайм-аут 500 мс. Это разумно? Или что нужно, чтобы Linux вместо этого отказался от 0 мс в тайм-аут 500 мс? Или попросить другой способ: если я знаю, что некоторые данные будут считаны ровно через 500 мс, какое значение SO_RCVTIMEO гарантирует, что чтение будет успешным? – Drew

+0

Что нужно, чтобы предотвратить это спецификация, но вам нужно понять, что таймеры имеют детализацию. Например, в Windows это 16 мс. Вы не можете получить больше детализации, чем есть. – EJP

+0

Где спецификация? Где я могу узнать, как долго или коротко может быть тайм-аут в Linux? – Drew

Смежные вопросы