Перевод Serial-Programming-HOWTO Версия для печати   


2. Serial Programming HOWTO Russian

Gary Frerking

======== gary@frerking.org ======

Peter Baumann

Revision History
Revision 1.01 2001-08-26 Revised by: glf
New maintainer, converted to DocBook
Revision 1.0 1998-01-22 Revised by: phb
Initial document release


Этот документ описывает программирование взаимодействия ОС Linux с различными устройствами через последовательный порт.

------------------------------------------------------------ -----------------
Содержание.
1. Введение.
1.1. Информация об авторских правах
1.2. Отказ от обязательств
1.3. Новые версии
1.4. Соавторы
1.5. Обратная связь

2. Начинаем
2.1. Отладка
2.2. Установки порта
2.3. Принципы ввода для последовательных устройств
2.3.1. Ввод в каноническом случае
2.3.2. Ввод в неканоническом случае
2.3.3. Асинхронный ввод
2.3.4. Ожидание ввода от нескольких источников данных

3. Примеры программ
3.1. В каноническом случае
3.2. В неканоническом случае
3.3. В случае асинхронного ввода
3.4. В случае нескольких источников данных

4. Другие источники информации

------------------------------------------------------------ -----------------
1. Введение

Это Linux Serial Programming HOWTO (Как запрограммировать последовательный ввод-вывод в Linux). Все о том, как программировать взаимодействие с другими устройствами/компьютерами через последовательный порт в Linux. Показываются различные подходы: канонический ввод/вывод (передача/прием только целых строк), асинхронный ввод/вывод и работами с несколькими источниками данных.

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

Если вы с нетерпением ждете кого-нибудь, кто поддерживал бы в актуальном состоянии этот документ, то может вы этим и займетесь? Дайте мне знать об этом, я это оценю.

Все примеры были проверены на i386-машине и ядре Linux версии 2.0.29 (работоспособно и на Pentium 4 i845GL и ядре Linux версии 2.6.14 - прим. перев.).

------------------------------------------------------------ -----------------
1.1. Информация об авторских правах

Права на этот документ принадлежат (c) 1997 Peter Baumann и (c) 2001 Gary Frerking, он распространяется на условиях лицензии Проекта Linux-документации (Linux Documentation Project license), приведенной ниже.

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

Все переводы, производные или объединяющие работы с любыми документами Linux HOWTO должны содержать в себе это замечание о правах. Таким образом, вы не можете выполнять производную от HOWTO работу и при этом накладывать дополнительные ограничения на распространение результата. Исключения из этого правила возможны при соблюдении определенных условий, проконсультируйтесь об этом с координатором Linux HOWTO по приведенному ниже адресу.

Короче говоря, мы хотим распространять информацию по максимально возможному количеству каналов максимально далеко. Тем не менее, мы хотим сохранить авторство документов HOWTO и хотим быть в курсе любых планов по их распространению.

С любыми вопросами обращайтесь по адресу linux-howto@metalab.unc.edu

------------------------------------------------------------ -----------------
1.2. Отказ от обязательств

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

Все права принадлежат их владельцам, если не указано иное. Использование торговых марок и названий в этом документе не должно рассматриваться как затрагивающее их законность.

Упоминание отдельных продуктов или торговых марок не должно рассматриваться как передача соответствующих прав (индоссамент).

Также настоятельно рекомендуется выполнить резервное копирование важных данных перед работой и впоследствии делать это регулярно.

------------------------------------------------------------ -----------------
1.3. Новые версии

Как было сказано выше, в содержательной части практически ничего нового.

------------------------------------------------------------ -----------------
1.4. Соавторы

Автор документа благодарит Mr. Strudthoff, Michael Carter, Peter Waltenberg, Antonino Ianella, Greg Hankins, Dave Pfaltzgraff, Sean Lincolne, Michael Wiedmann и Adrey Bonar.

------------------------------------------------------------ -----------------
1.5. Обратная связь

Определенно приветствуется. Без ваших предложений и ответов этот документ не состоялся бы. Пожалуйста, присылайте дополнения, комментарии и критику на этот адрес: gary@frerking.org

------------------------------------------------------------ -----------------
2. Начинаем

2.1. Отладка

Лучшим способом проверить ваш код является соединение двух Linux-машин нуль-модемным кабелем. Для передачи данных можно воспользоваться утилитой miniterm (доступной из руководства программистов LDP по адресу ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-guide/l pg-0.4.tar.gz в каталоге примеров ("examples") дистрибутива lpg). После очень простой компиляции и запуска miniterm позволит передать через последовательный порт весь клавиатурный ввод как есть (raw). Перед сборкой необходимо только проверить выражение #define MODEMDEVICE "/dev/ttyS0" в исходном коде программы. Установите его в ttyS0 для COM1, ttyS1 для COM2 и т.д. Для тестирования важно, что все символы передаются по линии без обработки, как есть (raw). Для проверки соединения просто запустите программу на обоих компьютерах и жмите клавиши. Символы, вводимые на одном компьютере, должны появляться на экране другого. При вводе символы на экране исходного компьютера не отображаются.

Для изготовления нуль-модемного кабеля необходимо соединить перекрестно контакты TxD (передача) и RxD (прием) разных концов. За более подробными инструкциями и пояснениями обратитесь к разделу 7 Serial-HOWTO.

Существует возможность выполнить тестирование и при наличии только одного компьютера при условии что у вас имеется два последовательных порта. В этом случае надо запустить два экземпляра miniterm в двух виртуальных консолях. Если вы освободили последовательный порт отключив мышь, то не забудьте перенаправить /dev/mouse если она существует. Если у вас мультипортовая карта, то убедитесь что она настроена правильно. В моем случае она была настроена неверно и все работало замечательно только при тестированим на моем компьютере. Когда я подключился к другому компьютеру, порт начал терять символы. Выполнение двух программ на одной машине все-таки не по настоящему асинхронно.

------------------------------------------------------------ -----------------
2.2. Установки порта

Устройства /dev/ttyS* предназначены для подключения терминалов к вашей Linux-машине и настраиваются соответствующим образом в процессе загрузки системы. Необходимо помнить об этом при программировании взаимодействия с ненастроенным устройством. К примеру, порты настроены на эхо-ответ символов, полученных от устройства обратно ему же, что обычно при передаче данных необходимо отключать.

Все параметры можно изменять прямо из программы. Конфигурация находится в структуре struct termios, которая определена в asm/termbits.h:

#define NCCS 19
struct termios {
    tcflag_t c_iflag;               /* флаги режима ввода */
    tcflag_t c_oflag;               /* флаги режима вывода */
    tcflag_t c_cflag;               /* флаги режима упр-я */
    tcflag_t c_lflag;               /* флаги лок-го режима */
    cc_t c_line;                    /* параметры линии */
    cc_t c_cc[NCCS];                /* управляющие символы */
};

Этот файл также включает в себя определения всех флагов. Флаги режима ввода в c_iflag применяются при обработке ввода, т.е. символы от устройства могут быть обработаны до их чтения при помощи вызова read. Точно также c_oflag учавствуют в организации вывода. c_cflag содержит такие настройки порта как скорость передачи в бодах, кол-во бит на символ, стоп-биты и т.д. Флаги локального режима хранятся в c_lflag и выставляются в случае эхо-повтора символов, определяют какие сингалы будут посланы вашей программе и т.д. Наконец, массив c_cc определяет управляющие символы для конца файла, останова и т.д. Значения по умолчанию для управляющих символов определены в asm/termios.h. Сами флаги описаны в мануале на termios(3). Структура termios также содержит элемент c_line (параметры линии), который не используется в POSIX-совместимых системах.

------------------------------------------------------------ -----------------
2.3. Принципы ввода для последовательных устройств.

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

------------------------------------------------------------ -----------------
2.3.1. Ввод в каноническом случае

Это нормальный режим работы для терминалов, но он может быть полезен и для взаимодействия с другими устройствами. Ввод выполняется линиями (строками), т.е. read вернет целую строку символов. Строка по умолчанию завершается символом NL (ASCII-символ LF) и символом конца файла или символом конца строки. По умолчанию символ CR (конец строки по умолчанию в DOS/Windows) не является символом завершения строки.

В каноническом случае также обрабатываются очистка, удаление слова, повторная печать символов, преобразование CR в NL и т.д.

------------------------------------------------------------ -----------------
2.3.2. Ввод в неканоническом случае

Неканонический ввод обрабатывает фиксированное количество символов за один вызов read и позволяет использовать символьный таймер. Этот режим должен использоваться в том случае, если ваше приложение считывает фиксированное количество символов или подключенное устройство посылает очереди символов.

------------------------------------------------------------ -----------------
2.3.4. Асинхронный ввод

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

------------------------------------------------------------ -----------------
2.3.5. Ожидание ввода от нескольких источников данных

Это не еще один режим ввода, но он может быть полезен если вам приходится работать с несколькими устройствами. В своем приложении я обрабатывал ввод через TCP/IP сокет и через последовательное соединение с другим компьютером квазиодновременно. Приведенный далее пример программы ожидает ввода от двух различных источников. Если становятся доступными данные от одного из них, то они обрабатываются и затем программа ожидает появления следующих данных.

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


------------------------------------------------------------ -----------------
3. Примеры программ

Все примеры построены на основе программы miniterm.c. Длина приемного буфера ограничена 255ю символами, что соответствует максимальной длине строки в случае канонического ввода (linux/limits.h или posix1_lim.h).

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

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

Не забудьте выставить правильные права на последовательные порты (т.е. chmod a+rw /dev/ttyS1)!

------------------------------------------------------------ -----------------
3.1. В каноническом случае

#include 
#include 
#include 
#include 
#include 

/* установки бодрейта определены в , который подключается файлом  */
#define BAUDRATE B38400

/* измените это на правильное устройство в вашем случае */
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* POSIX-совместимый код */
#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE;

main()
{
  int fd,c, res;
  struct termios oldtio,newtio;
  char buf[255];

  /*
    Открываем модемное устройство для чтения и записи и не как терминал
    потому что нам не нужно завершение в случае появления в линии CTRL-C
  */
  fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
  if (fd <0) {perror(MODEMDEVICE); exit(-1); }

  tcgetattr(fd,&oldtio); /* сохраняем текущие настройки порта */
  bzero(&newtio, sizeof(newtio)); /* очищаем структуру под новые настройки */

  /*
    BAUDRATE: Установка кол-ва бод в секунду. Также можно воспользоваться
              cfsetispeed и cfsetospeed.
    CRTSCTS : аппаратное управление исходящим потоком (используется только
              если кабель содержит все необходимые линии. Смотрите раздел 7
			  Serial-HOWTO)
    CS8     : 8n1 (8 бит, без контроля четности, 1 стопбит)
    CLOCAL  : локальное соединение, без управления модемом
    CREAD   : разрешаем получать символы
  */
  newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

  /*
    IGNPAR  : игнорируем байты с ошибками четности
    ICRNL   : отображаем CR на NL (иначе появление CR на другом компьютере не завершит ввод)
    в остальном оставляем устройство ненастроенным (raw, без других обработок ввода)
  */
  newtio.c_iflag = IGNPAR | ICRNL;

  /* Ненастроенный (raw, как есть) вывод */
  newtio.c_oflag = 0;

  /*
    ICANON  : включаем режим канонического ввода
              отключаем любую эхо-функциональность и не посылаем
			  сигналов вызвавшей программе
  */
  newtio.c_lflag = ICANON;

  /*
    инициализируем все управляющие символы
    значения по умолчанию можно найти в /usr/include/termios.h и они
    продублированы в комментариях, но здесь они не необходимы
  */
  newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */
  newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
  newtio.c_cc[VERASE]   = 0;     /* del */
  newtio.c_cc[VKILL]    = 0;     /* @ */
  newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
  newtio.c_cc[VTIME]    = 0;     /* междусимвольный таймер выключен */
  newtio.c_cc[VMIN]     = 1;     /* блокировать read до появления 1 символа */
  newtio.c_cc[VSWTC]    = 0;     /* '\0' */
  newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */
  newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
  newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
  newtio.c_cc[VEOL]     = 0;     /* '\0' */
  newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
  newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
  newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
  newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
  newtio.c_cc[VEOL2]    = 0;     /* '\0' */

  /* сбрасываем модемную линию и активируем настройки порта */
  tcflush(fd, TCIFLUSH);
  tcsetattr(fd,TCSANOW,&newtio);

  /*
    настройка терминала выполнена, теперь обрабатываем ввод
    в этом примере появление 'z' в начале строки завершит программу
  */

  while (STOP==FALSE) {     /* повторять до выставления условия останова */
    /* read блокирует выполнение программы до появления символа конца строки
       даже если получено больше 255 символов. Если количество считанных
       символов меньше количества поступивших симоволов, то последующие
       вызовы read вернут оставшиеся символы. res будет содержать
       количество реально считанных символов */
    res = read(fd,buf,255);
    buf[res]=0;         /* устанавливаем конец строки чтобы вызвать printf */
    printf(":%s:%d\n", buf, res);
    if (buf[0]=='z') STOP=TRUE;
  }
  /* восстанавливаем исходные настройки порта */
  tcsetattr(fd,TCSANOW,&oldtio);
}

------------------------------------------------------------ -----------------
3.2. В неканоническом случае

В этом режиме входные данные не разделяются на строки и их обработка (очистка, удаление и т.д.) не выполняется. Поведением системы в этом режиме управляют два параметра: c_cc[VTIME] устанавливает символьный таймер и c_cc[VMIN] устанавливает минимальное количество символов, после получения которого происходит возврат из read.

Если MIN > 0 и TIME = 0, то значение MIN есть минимальное количество символов в буфере для возврата из read. Т.к. TIME равно 0, то таймер не используется.

Если MIN = 0 и TIME > 0, то TIME является величиной таймаута. Возврат из read случится либо при появлении символа, либо при достижении таймаута (t = TIME * 0.1 с). При выходе по таймауту никакие символы не возвращаются.

Если MIN > 0 и TIME > 0, то TIME является величиной междусимвольного таймаута. Возврат из read происходит либо при приеме MIN символов, либо при достижении таймаута между получением двух символов. Таймер перезапускается при получении каждого символа и активизируется только после получения первого символа.

Если MIN = 0 и TIME = 0, то возврат из read происходит немедленно. Возвращается либо количество прочитанных символов, либо количество запрошенных символов (если их пришло больше размера указанного в read буфера - прим. перев.). Antonio сообщает (см. Соавторы), что для достижения аналогичного результата можно перед вызовом read выполнить вызов fcntl(fd, F_SETFL, FNDELAY).

Вышеперечисленные варианты можно проверить изменением newtio.c_cc[VTIME] и newtio.c_cc[VMIN]:
#include 
#include 
#include 
#include 
#include 

#define BAUDRATE B38400
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* POSIX-совместимый код */
#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE;

main()
{
  int fd,c, res;
  struct termios oldtio,newtio;
  char buf[255];

  fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
  if (fd <0) {perror(MODEMDEVICE); exit(-1); }

  tcgetattr(fd,&oldtio); /* сохраняем текущие настройки порта */

  bzero(&newtio, sizeof(newtio));
  newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
  newtio.c_iflag = IGNPAR;
  newtio.c_oflag = 0;

  /* устанавливаем режим ввода (неканонический, без эхо, ...) */
  newtio.c_lflag = 0;

  newtio.c_cc[VTIME] = 0; /* междусимвольный таймер выключен */
  newtio.c_cc[VMIN]  = 5; /* блокируем read после получения 5 символов */

  tcflush(fd, TCIFLUSH);
  tcsetattr(fd,TCSANOW,&newtio);

  while (STOP==FALSE) {       /* цикл ввода */
    res = read(fd,buf,255);   /* возврат из read случится после
                                 получения 5 символов */
    buf[res]=0;               /* теперь безопасно вызвать printf... */
    printf(":%s:%d\n", buf, res);
    if (buf[0]=='z') STOP=TRUE;
  }
  tcsetattr(fd,TCSANOW,&oldtio);
}

------------------------------------------------------------ -----------------
3.3. В случае асинхронного ввода

#include 
#include 
#include 
#include 
#include 
#include 

#define BAUDRATE B38400
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* POSIX-совместимый код */
#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE;

void signal_handler_IO (int status);   /* объявление обработчика сигнала */
int wait_flag=TRUE;                    /* TRUE до получения сигнала */

main()
{
  int fd,c, res;
  struct termios oldtio,newtio;
  struct sigaction saio;           /* объявление структуры под сигнал */
  char buf[255];

  /* открываем устройство для неблокирущего чтения (немедленный возврат из read) */
  fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
  if (fd <0) {perror(MODEMDEVICE); exit(-1); }

  /* устанавливаем обработчик сигнала перед переводом
     устройства в асинхронный режим */
  saio.sa_handler = signal_handler_IO;

  /* в моем случае эту строку пришлось закомментировать - прим. перев.) */
  saio.sa_mask = 0;

  saio.sa_flags = 0;
  saio.sa_restorer = NULL;
  sigaction(SIGIO,&saio,NULL);

  /* позволяем процессу получать SIGIO */
  fcntl(fd, F_SETOWN, getpid());

  /* делаем файловый дескриптор асинхронным (в мануале говорится, что
     только O_APPEND и O_NONBLOCK будут работать с F_SETFL...) */
  fcntl(fd, F_SETFL, FASYNC);

  tcgetattr(fd,&oldtio); /* сохраняем текущие настройки порта */

  /* выставляем новые настройки порта для работы в каноническом режиме */
  newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
  newtio.c_iflag = IGNPAR | ICRNL;
  newtio.c_oflag = 0;
  newtio.c_lflag = ICANON;
  newtio.c_cc[VMIN]=1;
  newtio.c_cc[VTIME]=0;
  tcflush(fd, TCIFLUSH);
  tcsetattr(fd,TCSANOW,&newtio);

  /* зацикливаемся в процессе ожидания ввода, также можно делать здесь
     что-нибудь полезное */
  while (STOP==FALSE) {
    printf(".\n");
    usleep(100000);
    /* после получения SIGIO wait_flag = FALSE, данные доступны и их
       можно прочитать */
    if (wait_flag==FALSE) {
      res = read(fd,buf,255);
      buf[res]=0;
      printf(":%s:%d\n", buf, res);
      if (res==1) STOP=TRUE; /* выходим из цикла если получен только CR */
      wait_flag = TRUE;      /* ожидаем новых данных */
    }
  }
  /* восстанавливаем исходные настройки порта */
  tcsetattr(fd,TCSANOW,&oldtio);
}

/***************************************************************************
* обработчик сигнала, устанавливает wait_flag в FALSE чтобы информировать  *
* вышеприведенный цикл о получении символов                                *
***************************************************************************/

void signal_handler_IO (int status)
{
  printf("получен сигнал SIGIO.\n");
  wait_flag = FALSE;
}

------------------------------------------------------------ -----------------
3.4. В случае нескольких источников данных

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

Системный вызов select и соответствующий макрос используют fd_set. Это битовый массив, который содержит по биту на каждый правильный номер файлового дескриптора. select принимает fd_set с битами, установленными для соответствующих файловых дескрипторов и возвращает fd_set с установленными битами для тех дескрипторов, у которых случились ввод, вывод или исключение. Всю обработку fd_set выполняет приведенный макрос. Смотрите также страницу руководства select(2).

#include 
#include 
#include 

main()
{
  int    fd1, fd2; /* источники данных 1 и 2 */
  fd_set readfs;   /* набор файловых дескрипторов */
  int    maxfd;    /* макс. номер использованного дескриптора */
  int    loop=1;   /* зацикливаемся пока TRUE */

  /* open_input_source открывает устройство, правильно настраивает
     порт и возвращает файловый дескриптор */
  fd1 = open_input_source("/dev/ttyS1");   /* COM2 */
  if (fd1<0) exit(0);
  fd2 = open_input_source("/dev/ttyS2");   /* COM3 */
  if (fd2<0) exit(0);
  maxfd = MAX (fd1, fd2)+1; /* максимальный битовый элемент (fd) для проверки */

  /* цикл ввода */
  while (loop) {
  FD_SET(fd1, &readfs);  /* включаем проверку для источника 1 */
  FD_SET(fd2, &readfs);  /* включаем проверку для источника 2 */

  /* блокируемся до получения данных на входе */
  select(maxfd, &readfs, NULL, NULL, NULL);
  if (FD_ISSET(fd1))         /* данные от источника 1 доступны */
    handle_input_from_source1();
  if (FD_ISSET(fd2))         /* данные от источника 2 доступны */
    handle_input_from_source2();
  }
}

Программа в приведенном примере блокируется на неопределенное время до получения данных от любого из источников. Если вам необходим таймаут, то просто замените вызов select на следующее:
int res;
struct timeval Timeout;

/* устанавливаем величину таймаута в цикле ввода */
Timeout.tv_usec = 0;  /* миллисекунды */
Timeout.tv_sec  = 1;  /* секунды */
res = select(maxfd, &readfs, NULL, NULL, &Timeout);
if (res==0)
/* количество дескрипторов с изменениями = 0, случился таймаут. */

Этот код сработает при таймауте в одну секунду. В случае таймаута select возвращает 0, но имейте в виду, что Timeout уменьшается со временем реального ожидания ввода вызовом select (?.. ориг. - "... but beware that Timeout is decremented by the time actually waited for input by select"). Если величина таймаута выставлена в 0, то select завершается немедленно.

------------------------------------------------------------ -----------------
4. Другие источники информации

==*= Linux Serial HOWTO описывает процесс настройки последовательных портов и также содержит информацию об аппаратной части.

==*= Руководство по программированию последовательных портов для POSIX-совместимых операционных систем (Serial Programming Guide for POSIX Compliant Operating Systems) от Michael Sweet.

==*= Страница руководства termios(3) (man 3 termios) описывает все флаги в структуре termios.

Опубликовал: San АНДРЕЕВ
Дата: 15.05.2006
постоянный адрес статьи: http://linuxportal.ru/entry.php/P2171_0_3_0/