#c #posix
Вопрос:
У меня есть эта программа:
// C headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
// POSIX headers
#include <unistd.h>
#include <sys/ioctl.h>
// Other headers
#include "ZL_sleep.h"
#include "ZL_utf8.h"
#include "ZL_serial.h"
#define BS 4 // Buffer size
int main(int argc, char * argv[]){
int32_t p; // Port
int32_t r; // Read finished
uint8_t b[BS]; // Buffer
uint32_t n; // Number of bytes to be read from driver buffer
if(argc != 2){
printf("ERROR: Supply a "serial port" device file.");
exit(EXIT_FAILURE);
}
p = ZL_open_tty(argv[1]);
if(p < 0){
perror("ERROR: open()");
exit(EXIT_FAILURE);
}
while(1){
memset(b, '', BS);
r = read(p, b, BS);
if(r < 0){
if(errno == EAGAIN){
}
else{
perror("ERROR: read()");
close(p);
exit(EXIT_FAILURE);
}
}
ZL_utf8_print_bytes(b, BS);
putchar('n');
}
close(p);
return EXIT_SUCCESS;
}
это использует функцию ZL_utf8_print_bytes()
, которая выводит байты буфера (один за другим в цикле for). Он также вызывает функцию ZL_open_tty()
и передает argv[1]
ей ( /dev/pts/1
) в качестве аргумента.
Функция ZL_open_tty()
функция устанавливает O_NONBLOCK
флаг с open()
для того open()
, чтобы немедленно вернуться, т. е. «неблокируется» с любым статусом. Затем, прежде чем использовать любую другую функцию ввода-вывода, функция снимает O_NONBLOCK
флаг и переключается обратно в режим «блокировка». Затем он устанавливается VMIN
и VTIME
так, чтобы read()
:
блокируется до
VMIN
тех пор, пока в буфере драйвера не будет получено/сохранено не более байтов. (Он может блокироваться на неопределенный срок)
uint32_t ZL_open_tty(const char * terminal){
uint32_t fd; // File's descriptor
uint32_t fl; // File's flags
struct termios fo; // File's options
fd = open(terminal, O_RDWR | O_NOCTTY | O_NONBLOCK);
if(fd < 0){
return -1;
}
fl = fcntl(fd, F_GETFL, 0);
if(fl < 0){
perror("ERROR: fcntl():");
return -1;
}
fcntl(fd, F_SETFL, fl amp; ~(O_NONBLOCK | O_APPEND | O_DSYNC | O_RSYNC | O_SYNC));
tcgetattr(fd, amp;fo);
fo.c_cc[VMIN] = 1;
fo.c_cc[VTIME] = 0;
tcsetattr(fd, TCSANOW, amp;fo);
fputs("─────────────────────────────────────────────────── terminal settingsn", stdout);
printf("VMIN = %dn", fo.c_cc[VMIN]);
printf("VTIME = %dn", fo.c_cc[VTIME]);
putchar('n');
return(fd);
После этого моя программа переходит в бесконечный цикл while, где read()
блокируется до тех пор, пока я не введу любую клавишу, когда на клавиатуре находится фокус /dev/pts/1
.
Проблема, с которой я сталкиваюсь, заключается в том, что иногда я нажимаю клавишу, /dev/pts/1
и байты, зарегистрированные с помощью этого ключа, не передаются в буфер драйвера?! Байты просто остаются в /dev/pts/1
. Это происходит с символами ASCII (1 байт) и байтами UTF8 (многобайтовыми)…
Я знаю, что read()
он пытается прочитать запрошенное количество байтов ( BS
) из буфера драйвера. Но он считывает меньше байтов, если BS
в буфере драйвера нет доступных байтов. так что… если некоторые байты поступят позже, он сможет прочитать их позже. Но по какой-то причине эти байты никогда не поступают в буфер драйвера…
Что может быть причиной того, что байты не поступают в буфер драйвера и навсегда остаются в /dev/pts/1
нем ?
Комментарии:
1. разве вам также не нужно отключить канонический режим? ИКАНОН…
2. Я добавил
fo.c_lflag amp;= ~(ICANON | ECHO | ECHOE | ISIG);
,ZL_open_tty()
чтобы отключить канонический режим, и результаты идентичны.3. Даже если я попытаюсь запросить только чтение
1
байта, например,r = read(p, b, 1);
некоторые байты не передаются в буфер последовательного драйвера и, следовательно, не считываются.
Ответ №1:
Он pts
был создан, когда к системе был подключен виртуальный терминал (обычно ssh
). Он pts
был подключен как stdin
/ stdout
для оболочки, которая была запущена для подключения, поэтому у вас уже есть процесс, подключенный к pts
нему и считывающий из него.
Как только вы присоедините свое приложение, вы фактически начнете гонку между обоими процессами (вашим приложением и оболочкой), поэтому тот, кто быстрее, получит содержимое буфера. Символ не остался в буфере pts
, скорее он был прочитан оболочкой, прикрепленной к pts
.
Чтобы иметь возможность читать без прерывания из подключенного процесса, вам необходимо перехватить буфер pts
, сообщив об этом главному мультиплексору ptmx
, см. Подробнее в ptmx(4). Вы можете изучить, как это делается в перехватчике
Комментарии:
1. Большое вам спасибо, сэр!