我在/ dev / ttyACM0文件中读取的速度有多快?

时间:2018-02-12 19:38:19

标签: c++ linux serial-port linux-device-driver

我有一个自定义USB cdc-acm设备,可以通过串行通信将图像发送到计算机。内核驱动程序工作正常,因为设备显示为/ dev / ttyACM0,我能够使用打开,写入和读取功能发送命令和获取数据。

当设备连续发送数据时,我在while循环中的单独线程中获取此数据:

while (m_isListening)
{
    int rd = read(m_file_descriptor, read_buffer, 512);

    // Display read data
    if (rd > 0)
    {
        std::cout << rd << " bytes read: ";
        for (int i = 0; i < rd; i++)
        {
            printf(" %x", read_buffer[i]);
        }
        std::cout << " # END" << std::endl;
    }
    // Nothing was to be read
    else if (rd == 0)
    {
        std::cout << "No bytes read =(" << std::endl;
    }
    // Couldn't acces the file for read
    else
    {
        printf("%d %s \n", errno, strerror(errno));
    }
}
std::cout << std::endl;

我设法读取和显示数据,但我也有很多这样的行(11 = EAGAIN错误):

11 Ressource Temporarily Unavailable

所以我有几个问题:

  • 我/我应该多快访问(读入)tty文件?
  • EAGAIN错误是否意味着当设备写入文件时我无法读取文件?
  • 如果是,那么结果是否意味着当我在文件中读取时,设备无法写入文件?那么数据会丢失吗?
  • 最后(以及更主观的问题),我是否正在使用这种代码做一些非常脏的事情?有关通过tty文件访问实时数据的任何意见或建议吗?

感谢。

修改

更精确一些:此代码的目的是仅在linux下与设备建立C / C ++接口。我知道该设备以特定的帧速率发送大约105千字节的帧(但代码不应该依赖于此帧速率,并且在15 fps和1 fps时都是平滑的......没有滞后)。

由于这是我第一次编写这种代码并且我不理解我发现的手册页中的evrything,我保留了我为大多数代码(打开和阅读)找到的样本的默认设置:

断路:

// Open the port
    m_file_descriptor = open("/dev/ttyACM0", O_RDWR | O_NOCTTY | O_NDELAY);
    if (m_file_descriptor == -1)
    {
        std::cerr << "Unable to open port " << std::endl;
}

我在阅读之前做的唯一设置修改是:

fcntl(m_file_descriptor, F_SETFL, FNDELAY);

...为了设置读数非阻塞(如果我理解的话,FNDELAY相当于O_NONBLOCK)。

3 个答案:

答案 0 :(得分:1)

subprocess.call('cd /d G:\Programs\dsi_studio_64', shell=True) (可能)是一个串行端口,它的速度有限,这取决于它运行的波特率。所以关于你的问题:

  

我/我应该多快访问(读入)tty文件?

完全取决于端口的波特率。

  

EAGAIN错误是否意味着当设备写入文件时我无法读取文件?

Eagain意味着稍后再试。所以现在串口的缓冲区中没有数据,但是如果你稍后再试,它可能会有一些数据。

  

如果是,那么这意味着当我在文件中阅读时,设备无法写入文件?那么数据会丢失吗?

没有。只要您正在快速或快速地阅读发送数据写入的设备(您就是这样,因为您获得了EAGAIN),您就不会丢失数据。

  

最后(还有更主观的问题),我是否正在用这种代码做一些非常脏的事情?有关通过tty文件访问实时数据的任何意见或建议吗?

我看不到任何脏东西。

答案 1 :(得分:1)

  

我在/ dev / ttyACM0文件中读取的速度有多快?

这取决于许多因素,包括处理器负载,进程/线程的优先级,代码的效率以及(当然)数据的接收速率。

  

我有一个自定义USB cdc-acm设备,可以通过串行通信将图像发送到计算机。内核驱动程序工作正常,因为设备显示为/ dev / ttyACM0 ...

因此,您有一个通过模拟串行端口进行通信的USB小工具 您的Linux程序将此设备作为串行终端访问。

  

我/我应该多快访问(读入)tty文件?

您的程序可能/确实比实际收到的数据更频繁地使非阻塞读取()系统调用,但这会导致EAGAIN或EWOULDBLOCK返回代码并且效率低下。
对于典型的波特率(例如远小于兆比特)和运行Linux内核的典型处理器,您的程序可能必须等待数据,并且应该使用阻塞 read()系统调用来提高效率。 /> 由于您有一个专用的输入线程,并且在等待数据时没有计算/处理(在此线程中),因此阻塞I / O肯定是一种谨慎的方法。

  

EAGAIN错误是否意味着当设备写入文件时我无法读取文件?

你似乎认识到中间tty缓冲区,但很难确定 EAGAIN应解释为:“此时无法满足数据请求,请稍后再试”。 EAGAIN的典型原因很简单,缓冲区中没有“读取”数据 设备驱动程序是否对缓冲区进行锁定无需担心。

  

如果是,那么结果是否意味着当我在文件中读取时,设备无法写入?那么数据会丢失吗?

tty子系统将其缓冲区视为关键资源。您不必担心此缓冲区的争用。设备驱动程序有自己的与tty缓冲区不同的缓冲区,用于从设备接收数据。参见Serial Drivers的图3 丢失您可以控制的数据的一种机制是缓冲区溢出,即如果您的程序没有像接收到数据一样快地读取(),那么tty缓冲区最终将被填充,然后被超限,导致数据丢失 请注意,tty缓冲区通常为4KB。

  

最后(还有更主观的问题),我是否正在用这种代码做一些非常脏的事情?

你的代码是(不必要的)CPU密集型/效率低下且不完整 只要返回代码为EAGAIN,循环中的重复非阻塞 read()系统调用就会浪费CPU周期。

您没有显示配置终端的termios代码。您可以使用已存在的任何设置,但编码很差。无论何时更改波特率和/或线路设置,您的程序都将无法读取任何内容,或者您​​将this problem。 使用串行终端的编写良好的程序将始终将该终端初始化为其期望/需要的配置 有关示例代码,请参阅this answerthis answer

  

有关通过tty文件访问实时数据的任何意见或建议吗?

Linux并不是一个实时操作系统,尽管有“实时”补丁可以使延迟更具确定性。但是,您没有表达任何超出以前使用典型硬件的嵌入式Linux项目的要求 快速而强大的串行终端传输技术不依赖于非阻塞I / O,这可能会导致过多的处理开销(特别是在做得不好时),从而损害其他进程/线程。
阻止非规范终端I / O有很多选项可以指定何时应该满足 read()并返回给调用者。请参阅this answer for details

要最小化 read()系统调用,一次扫描/解析一个字节的接收数据,请缓冲程序中的数据,例如: sample code

答案 2 :(得分:0)

首先,依赖于读取/写入数据的速度以避免饥饿或拒绝服务是一个坏主意,因为速度会因许多因素而变化(除非您能保证某些确定性的响应时间) 。最好理解为什么你会得到这个错误以及如何强有力地处理它。这取决于您正在使用的操作系统。由于问题标签中有“linux”,我假设您使用的是Linux。从手册页(参见man(2) read):

   EAGAIN The file descriptor fd refers to a file other than a socket and has been marked non-
          blocking (O_NONBLOCK), and the read would block.  See open(2) for further details on
          the O_NONBLOCK flag.

   EAGAIN or EWOULDBLOCK
          The file descriptor fd refers to a socket and has been  marked  nonblocking  (O_NON-
          BLOCK),  and  the read would block.  POSIX.1-2001 allows either error to be returned
          for this case, and does not require these constants to have the  same  value,  so  a
          portable application should check for both possibilities.

现在,您没有向我们展示您如何打开设备或您设置的fcntrl选项。我假设设备是非阻塞的(通过在文件描述符上设置O_NONBLOCK标志来实现),或者设备驱动程序默认实现非阻塞I / O.

在这种情况下,EAGAINEWOULDBLOCK表示当前没有数据存在,read()通常会在此时阻止,直到数据可用。

一个简单的解决方案,没有考虑任何其他要求(因为你没有说明)是检查EAGAIN(以及EWOULDBLOCK的可移植性),如果发生任何一个,{ {1}}在恢复之前的几毫秒。这是您的代码稍作修改:

sleep

顺便说一下,根据设备驱动程序处理while (m_isListening) { errno = 0; int rd = read(m_file_descriptor, read_buffer, 512); // Display read data if (rd > 0) { std::cout << rd << " bytes read: "; for (int i = 0; i < rd; i++) { printf(" %x", read_buffer[i]); } std::cout << " # END" << std::endl; } // Nothing was to be read else if (rd == 0) { std::cout << "No bytes read =(" << std::endl; } // Couldn't acces the file for read else { if (errno == EAGAIN || errno == EWOULDBLOCK) { usleep(2000); // Sleep for 2 milliseconds. } else { printf("%d %s \n", errno, strerror(errno)); } } } std::cout << std::endl; 的方式,子句read()可能是多余的。我说这取决于,因为在某些情况下返回0表示文件结束。这可能会也可能不会被视为错误。例如,在TCP / IP的情况下,如果if (rd == 0)返回0,则表示对等方关闭了连接。

修改

反映对原始问题的修改:是的,read()O_NDELAY同义(事实上,您不需要额外调用O_NONBLOCK来设置fcntrl因为您已经使用该选项集打开设备)。这意味着您的设备是非阻止的,因此如果数据不可用,而不是阻止并等待数据到达,则驱动程序抛出O_NDELAYEAGAIN也是合法的)。

如果您没有严格的时间限制并且可以容忍阻止,则只需删除EWOULDBLOCK选项即可。否则,按照我上面的建议行事。

关于数据丢失:如果未设置O_NDELAY(或等效地O_NDELAY),则O_NONBLOCK将在数据可用时立即返回(但不会等待填写缓冲到请求的字节数 - 调用read()时的第三个参数 - 而是返回可用的字节数,直到指定的请求数。但是,如果数据不可用,它将阻止(再次,假设这是驱动程序的行为)。如果你想要它返回0 - 好吧,这取决于驱动程序。这正是read()选项提供的原因。

这里的一个缺点是,除非驱动程序提供某种控制延迟的机制,否则无法知道设备可能会阻塞多长时间(取决于数据到达的速度)。这就是人们通常设置O_NONBLOCK然后手动控制O_NONBLOCK延迟(例如在实时要求下)的原因。