使用 VTIME 和 read() 的 POSIX 行为

POSIX behaviour with VTIME and read()

本文关键字:POSIX 行为 VTIME read 使用      更新时间:2023-10-16

在网上花了几个小时研究这个之后,我仍然不太清楚VTIMEread(int fildes, void *buf, size_t nbyte);是如何一起工作的。在我看来,一旦read()得到一个字节,它就会忽略VTIME. 仅当未读取任何字节时,才遵守VTIME

如果我没看错,这里的解释似乎证实了这一点:

VMIN = 0 且 VTIME> 0 这是一个纯粹的定时阅读。如果数据在输入队列中可用,则会将其传输到调用方的缓冲区,最大为 nbytes,并立即返回到调用方。否则,驱动程序将阻止,直到数据到达,或者当 VTIME 十分之一从呼叫开始过期时。如果计时器过期时没有数据,则返回零。单个字节足以满足此读取调用,但如果输入队列中有更多可用字节,则会将其返回给调用方。请注意,这是一个整体计时器,而不是字符间计时器。

有没有办法让read()仅在满足nbyte或在最后一个字节后达到VTIME时才返回?

VTIMEread()会这样做似乎有点奇怪。为什么它不尝试在超时之前读取nbytes

例如,在下面的代码中,read() 不会等待 10 秒就返回。如果未发生写入,则会发生。

int main (void) {
    int usbSerial;
    struct termios options;
    std::string port = "/dev/tty.usb001";

    usbSerial = open(port.c_str(), O_RDWR| O_NOCTTY | O_NONBLOCK);
    // Check if unopen
    if(usbSerial == -1) {
        printf("Error: Unable to open %sn", port.c_str());
    }
    else { // Set to blocking
        fcntl(usbSerial, F_SETFL, 0);
        printf("Connection to serial device established.n");
    }
    // Set port settings
    tcgetattr(usbSerial, &options); // read old port settings
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);
    options.c_cflag &= ~PARENB;    // set no parity, 1 stop bit, data bits
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    options.c_cflag     &=  ~CRTSCTS;           // no flow control.
    options.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines
    options.c_cc[VMIN]   =  0;
    options.c_cc[VTIME]  =  100;

    // Flush port and then apply new options
    tcflush(usbSerial, TCIOFLUSH);
    if (tcsetattr(usbSerial, TCSANOW, &options) != 0) {    // TCSANOW == make option change immediately
        printf("Error %i from tcsetattr.n", errno);
    }
    // write
    unsigned char message[] = {0x03, 0x05, 0x01, 0x01, 0x04};
    ssize_t n = write(usbSerial, &message, sizeof(message)/sizeof(message[0]));

    unsigned char buffer[64] = {};
    tcdrain(usbSerial);

    ssize_t readChars = read(usbSerial, &buffer, 10);
    printf("Done.n");
}

我相信你说的是正确的:

在我看来,一旦 read() 获得一个字节,它就会忽略 VTIME。

read()的返回值:

成功后,将返回读取的字节数(零表示文件结束),并且文件位置将提前此数字。 如果此数字小于请求的字节数,则不是错误;例如,这可能是因为现在实际可用的字节较少(可能是因为我们接近文件末尾,或者因为我们从管道或终端读取),或者因为 read() 被信号中断。链接

您可能需要循环,可能是类似(未经测试的):

int totalNeeded = 10;
int remaining   = 10;
while (remaining > 0){
    ssize_t readChars = read(usbSerial, &buffer[totalNeeded - remaining], remaining);
    if (readChars > 0){
        remaining -= readChars;
    }
    else{
        // handle error or EOF
    }
}

不确定您需要如何处理部分读取,尤其是因为O_NONBLOCK,但我认为 VTIME 和 read() 之间的行为交互受read()支配。