字节未发送到串行驱动程序缓冲区

Bytes are not sent to the serial driver buffer

我有这个程序:

// 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, '[=10=]', 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() 立即变为 return,即 “非阻塞”。 =60=] 什么状态。然后在使用任何其他 I/O 函数之前,函数清除 O_NONBLOCK 标志并切换回 “阻塞”。然后设置 VMINVTIME 以便 read():

blocks until VMIN or more bytes is received/exists in driver buffer. (It may block indefinitely)

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 & ~(O_NONBLOCK | O_APPEND | O_DSYNC | O_RSYNC | O_SYNC));
    
    tcgetattr(fd, &fo);
    fo.c_cc[VMIN] = 1;
    fo.c_cc[VTIME] = 0;
    tcsetattr(fd, TCSANOW, &fo);

    fputs("───────────────────────────────────────────────────  terminal settings\n", stdout);
    printf("VMIN = %d\n", fo.c_cc[VMIN]);
    printf("VTIME = %d\n", fo.c_cc[VTIME]);
    putchar('\n');
    return(fd);

之后我的程序进入一个无限的 while 循环,其中 read() 阻塞,直到我在键盘焦点位于 /dev/pts/1.[=35= 时输入任何键]

我面临的问题是有时我在 /dev/pts/1 中按下一个键,用这个键注册的字节没有传输到驱动程序缓冲区?!字节只停留在/dev/pts/1。 ASCII(1 字节)字符和 UTF8(多字节)字节会发生这种情况...

我知道 read() 试图从驱动程序缓冲区中读取 请求的 字节数 (BS)。但如果驱动程序缓冲区中没有 BS 字节可用,它会读取较少的字节。所以......如果一些字节稍后到达,它可以稍后读取它们。但是由于某种原因这些字节永远不会到达驱动程序缓冲区...

什么可能导致字节未到达驱动程序缓冲区并永远保留在 /dev/pts/1 中?

pts 是在虚拟终端连接到系统时创建的(通常是 ssh)。 pts 连接为 stdin/stdout 用于连接启动的 shell,因此您已经有一个进程附加到 pts 和读取来自它。

一旦你附加了你的应用程序,你就有效地开始了两个进程(你的应用程序和 shell)之间的竞争,所以谁更快谁就会收到缓冲区的内容。该字符并未保留在 pts 的缓冲区中,而是被附加到 pts 的 shell 读取。

为了能够不中断附加进程的读取,您需要通过通知主多路复用器 ptmx 来拦截 pts 的缓冲区,请参阅 ptmx(4). You can study how it's done in the interceptty[ 了解更多信息=21=]