【问题标题】:POSIX behaviour with VTIME and read()VTIME 和 read() 的 POSIX 行为
【发布时间】:2015-03-24 01:57:31
【问题描述】:

经过数小时的在线研究,我仍然不太清楚VTIMEread(int fildes, void *buf, size_t nbyte); 如何协同工作。在我看来,只要read() 得到一个字节,它就会忽略VTIMEVTIME 仅在未读取任何字节时才会遵守。

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

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 %s\n", 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");

}

【问题讨论】:

  • 不相关,但在将操作数的地址与数组一起使用时要小心。如果数组实际上不是数组(并记住数组衰减为指针),那么事情很可能不会按预期工作。而且由于数组衰减到指向它们第一个元素的指针,因此在将数组传递给期望指针的函数时,您实际上不需要地址运算符。
  • @JoachimPileborg 啊,完全正确!谢谢你。
  • 您描述的程序行为与您发布的代码不一致。设置 VMIN 和 VTIM 仅适用于非规范模式,但您的代码并未明确配置非规范或原始模式的串行端口。端口可能已经处于原始模式,但是由于部分/不完整的初始化,您将面临无法预测的程序行为的风险。更有可能您的 read() 是一个非阻塞规范操作,既不使用 VMIN 也不使用 VTIME。
  • 我认为它默认为原始模式,因为 VMIN 工作正常,没有写入字符时 VTIME 也是,但我添加了options.c_lflag &= ~(ICANON | ECHO | ISIG); 只是为了确定。

标签: c++ macos unix serial-port posix


【解决方案1】:

我相信你说的没错:

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

read() 的返回值:

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

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

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() 控制。

【讨论】:

  • 谢谢。我希望它的行为类似于 pyserial 或 MATLAB 中的其他超时操作。
猜你喜欢
  • 2021-10-11
  • 2019-02-27
  • 1970-01-01
  • 2013-01-03
  • 1970-01-01
  • 1970-01-01
  • 2016-07-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多