Linux中串口描述符上的“close”函数会阻塞

5

最近我遇到了一个问题,这对我来说是比较新的,我需要一些建议。我正在使用termios函数在Linux上进行串口通信。实际上,我并没有使用真正的串口,而是虚拟的串口驱动程序 /dev/ttyGS0。文件描述符被打开为非阻塞。

我的程序周期性地生成数据并将其发送到 /dev/ttyGS0。没有信息表明另一端是否读取了它。如果没有读取,一些内部fifo就会填满,并返回“would block”错误。到目前为止,我没有遇到任何问题。

问题是,当我想要关闭这样的文件描述符时,带有填充的fifo,close函数会阻塞!不是无限期的,但大约需要10秒钟。

我尝试在关闭之前执行tcflush(uart->fd, TCOFLUSH),但没有任何效果。

这对我来说是如此奇怪的行为,我找不到任何描述,close可能会阻塞。有没有办法避免这种情况?或者至少减少这个超时时间?应该在哪里寻找这个超时时间?VTIME属性对此也没有影响。


2
close() 映射到设备驱动程序中的调用,这可能是延迟发生的地方。如果您有驱动程序的源代码,可能可以修改其行为。还要查找该设备支持的任何自定义 ioctl() 调用,以允许修改行为。 - Amardeep AC9MF
我认为在数据正在传输时调用 close() 并阻塞是完全合理的,而且我认为非阻塞 IO 的 POSIX 语义并不适用于 close()。我期望在执行 close() 后,设备将处于准备好再次进行 open() 的状态。由于这是通过 USB 进行虚拟串口通信,我会开始怀疑端点的关闭是否无序,并且 10 秒后会超时。 - marko
3个回答

2
正如Amardeep所述,close()调用由驱动程序处理。关闭本身总是阻塞调用,但通常它是快速的。
因此,答案是延迟取决于虚拟设备驱动程序。我没有使用过那个驱动程序,无法提供帮助。
关闭文件有多重要?如果延迟是一个主要问题并且需要关闭文件(例如在长时间运行的进程中避免文件描述符泄漏),则关闭可能需要在单独的线程中调用。显然,最好的答案是针对该驱动程序的特定答案;也许在那里进行研究可能会得出一个答案,例如一个ioctl()调用,可以清除虚拟设备的状态。

1

您可能需要配置端口的closing_wait参数。来自setserial手册:

closing_wait延迟 指定内核在关闭端口时等待串口传输数据的时间,单位为百分之一秒。如果指定为“none”,则不会发生延迟。如果指定为“infinite”,则内核将无限期地等待缓冲数据传输。默认设置为3000或30秒延迟。这个默认值通常适用于大多数设备。如果选择了太长的延迟,则当关闭未连接且有挂起数据的串口时,串口可能会挂起很长时间。如果选择了太短的延迟,则存在某些传输数据根本没有输出的风险。如果设备非常慢(如绘图仪),则closing_wait可能需要更大。

使用setserial检查端口的参数:

$ setserial -g -a /dev/ttyS0
/dev/ttyS0, Line 0, UART: 16550A, Port: 0x03f8, IRQ: 4
        Baud_base: 115200, close_delay: 50, divisor: 0
        closing_wait: 3000
        Flags: spd_normal skip_test

在我的情况下,一个故障设备没有接收到我发送的最后几个字节,并且由于这个原因关闭端口总是需要30秒钟。您可以使用setserial更改此超时时间,例如将其设置为1秒钟:
$ sudo setserial /dev/ttyS0 closing_wait 100

当然,您可能希望在启动时发出此命令,并将其放置在/etc/rc.local或其他脚本中,以配置端口。

0

我遇到了同样的问题,在我的情况下,在关闭设备之前禁用流控制有所帮助。您可以使用以下函数来执行此操作:

int set_flowcontrol(int fd, int control) 
{
    struct termios tty;
    memset(&tty, 0, sizeof tty);
    if (tcgetattr(fd, &tty) != 0) 
    {
        perror("error from tggetattr");
        return -1;
    }

    if(control) tty.c_cflag |= CRTSCTS;
    else tty.c_cflag &= ~CRTSCTS;

    if (tcsetattr(fd, TCSANOW, &tty) != 0)
    {
        perror("error setting term attributes");
        return -1;
    }
    return 0;
}

在关闭之前只需调用此函数:

...
rc = set_flowcontrol(fd, 0);
if (rc != 0)
{
    perror("error setting flowcontrol: ");
    exit(-1);
}

rc = close(fd);
if (rc != 0)
{
    perror("error closing fd: ");
    exit(-1);
}
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接