Байты не отправляются в буфер последовательного драйвера

#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. Большое вам спасибо, сэр!