LINUX.ORG.RU

Таймаут чтения из последовательного порта

 ,


0

1

Открываю последовательный порт под Linux. Как-то так:

int fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
fcntl(fd, F_SETFL, 0);
termios options;
tcgetattr(fd, &options);
options.c_cflag |= CLOCAL | CREAD;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_oflag &= ~OPOST;
options.c_cflag &= ~CRTSCTS;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~PARENB;
cfsetspeed(&options, B115200);
options.c_cc[VMIN] = 10;
options.c_cc[VTIME] = 20;
tcsetattr(fd, TCSAFLUSH, &options);
char buffer[10];
int count = read(fd, buffer, sizeof(buffer));
printf("count = %i\n", count);

Я ожидаю, что при отсутствии символов, приходящих через последовательный порт, программа завершится через 2 секунды (установленное значение c_cc[VTIME]), однако она зависает в бесконечном ожидании.

Если же убрать вызов fcntl, то read мгновенно возвращает 0 байт, но я то хочу, чтобы он подождал.

ЧЯДНТ?

P. S.: Я знаю про select, однако он не позволяет задать, сколько именно байт я хочу получить через порт - ожидание прервётся даже если придёт 1 байт, а в данном примере я жду сразу 10.

P. P. S.: Я работаю с виртуальным COM-портом, созданным микросхемой FT232RL.

★★★★★

Последнее исправление: beastie (всего исправлений: 4)

Таймаут чтения из последовательного порта

P. P. S.: Я работаю с виртуальным COM-портом, созданным микросхемой FT232RL.

И т.к. в ядре, к сожалению, разные драйверы, то и поведение разное.

gag ★★★★★
()
Ответ на: комментарий от gag

То есть нормального решения кроме включения неблокирующего режима и select в цикле (делаем read, если прочитали не всё, то делаем select с заданным таймаутом, если дождались, то продолжаем цикл, при этом уменьшаем количество на столько байт, сколько прошлый read прочитал, а таймаут на время выполнения select) нет?

KivApple ★★★★★
() автор топика

последовательный порт это символьное устройство, соответственно границы передаваемых блоков никто не гарантирует

zudwa
()
Ответ на: комментарий от KivApple

включения неблокирующего режима и select в цикле

Так select же используется как раз для блокирующего режима: перед каждым read (и даже write) делаем select.

То есть нормального решения ... нет?

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

gag ★★★★★
()
Ответ на: комментарий от gag

Так select же используется как раз для блокирующего режима: перед каждым read (и даже write) делаем select.

select позволяет уснуть, пока не появятся входящие символы, чтобы зря не грузить процессор busy loop. Затем вызываем неблокирующий read (открываем файл с флагом O_NDELAY). Зачем неблокирующий? Потому что select лишь гарантирует, что там не меньше 1 символа пришло, но он не знает, что мы хотим прочитать их, скажем, 100. В этом случае read вполне может заблокироваться. А в таком режиме он не заблокируется, а вернёт сколько символов реально получилось прочитать. Если прочитались не все, то запускаем новый select, но в следующий раз будем пытаться читать уже меньше символов (а ещё timeout у select надо уменьшить на длительность вызова прошлого select).

Я уже написал реализацию, отлично работает. Более того, можно ждать больше 255 символов и дольше 25.5 секунд. Заодно применимо не только к последовательному порту, но и к сокетам и стандартному потоку ввода.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 3)

К примеру сделать так, что если данные не пришли сразу(100-300ms) то засыпать на две секунды.

Cactus64k
()
Ответ на: комментарий от KivApple

И т.к. в ядре, к сожалению, разные драйверы, то и поведение разное.

4.2
На самом деле при MIN > 0, TIME > 0 таймер включается только после приёма первого символа, поэтому и называется этот режим «чтение с блокировкой по времени между байтами (read with interbyte timeout)». При вызове read(2) таймер включается только при MIN == 0, TIME > 0 "(чтение с блокировкой по времени (read with timeout))". И это всё описано в man 3 termios, и даже по-русски.

ABW ★★★★★
()
Ответ на: комментарий от ABW

На самом деле ...

Спасибо за уточнение, но

И т.к. в ядре, к сожалению, разные драйверы, то и поведение разное.

4.2

поведение тем не менее в общем случае всё равно разное.

gag ★★★★★
()
Ответ на: комментарий от KivApple

В этом случае read вполне может заблокироваться.

Вроде, нет:

read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

Я уже написал реализацию, отлично работает.

Но она не такая, как принято.

gag ★★★★★
()
Ответ на: комментарий от gag

Это если в файле байтики кончились или ещё какое-нибудь событие случилось (типа разрыва сокета). Разумеется, read не может прочитать больше, чем есть в источнике чисто физически (и поэтому в документации сказано, что он может теоретически прочитать мешьше). Но последовательный порт то бесконечный. Так что гарантий, что не заблокируется нет.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

Но последовательный порт то бесконечный.

Точно так же как и потоковый сокет. И таких проблем нет.

Так что гарантий, что не заблокируется нет.

Как же нет - ведь select, давший добро на read и есть эта гарантия. Т.е. select блокируется. Ядро получает данные в свой буфер, разблокирует select, а при следующем read ядро просто перегоняет байтики в пространство пользователя copy_user'ом. Т.е. как только байтиков >= 1 никакой блокировки произойти не может. А функции read передаётся не запрос на прочитай мне N байт, а запрос прочитай-ка мне столько сколько сможешь вот прям сейчас за раз, но не больше, т.к. буфер ограниченного размера.

gag ★★★★★
()
Ответ на: комментарий от gag

Хочешь сказать, что select модифицирует поведение read?

KivApple ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.