什么是从这个缓冲区读取的“最安全”的方法?

时间:2013-03-30 14:56:59

标签: c linux serial-port buffer port

我正在尝试在Linux(Ubuntu 12.04)中读取和编写串行端口,其中另一端的微控制器在完成某项任务时会产生1或3个字节。我能够成功读取和写入设备,但问题是我的读取现在有点'危险':

do
{
    nbytes = read(fd, buffer, sizeof(buffer));
    usleep(50000);
} while(nbytes == -1);

即。为了简单地监控设备发送给我的是什么,我每隔半秒轮询缓冲区。如果它是空的,它在这个循环中空闲。如果它收到某些东西或错误,它会踢出去。然后,一些逻辑处理1或3个数据包并将其打印到终端。半秒钟通常是一个足够长的窗口,可以让某些东西完全出现在缓冲区中,但对于最终看到它不会认为它很慢的人来说足够快。

'通常'是关键字。如果我在中间读取缓冲区,则爆破3个字节。我读得不好;缓冲区中将包含1或2个字节,它将在数据包处理中被拒绝(如果我捕获3字节数据包中的第一个,则不会是故意发送的一个字节的值)。

我考虑/试过的解决方案:

  1. 我想过一次只读取一个字节,如果它是3字节传输的一部分,则会输入额外的字节。然而,这会产生一些丑陋的循环(因为read()只返回只有最前一次读取的字节数),如果我能

  2. 我想避免
  3. 我试过读取0个字节(例如nbytes = read(fd,buffer,0);)只是为了看看在尝试将它加载到我自己的缓冲区之前缓冲区中当前有多少字节,但我怀疑它只返回0.

  4. 如果在将其加载到我自己的缓冲区之前可以查看端口缓冲区的内容,似乎很容易解决我的很多问题。但是read()是破坏性的,直到你告诉它读取的字节数。

    我怎样才能从这个缓冲区中读取这样的信息,这样我就不会在接收传输的过程中这样做,但是要做得足够快,以免对用户显得慢?我的串行信使分为发送者和接收者线程,因此我不必担心我的程序循环阻塞某处而忽略了另一半。

    感谢您的帮助。

3 个答案:

答案 0 :(得分:3)

修复您的数据包处理。我总是最终使用状态机来处理这样的实例,所以如果我收到部分消息,我会记得(有状态的)我停止处理的地方,并且可以在数据包的其余部分到达时恢复。

通常我必须在继续进行其他处理之前验证数据包末尾的校验和,因此“我停止处理的地方”总是“等待校验和”。但我存储了部分数据包,以便在有更多数据到达时使用。

即使您无法查看驱动程序缓冲区,也可以将所有这些字节加载到您自己的缓冲区中(在C ++中deque是一个不错的选择)并查看所有您想要的内容。

答案 1 :(得分:2)

您需要知道发送的邮件有多大。有几种方法可以做到这一点:

  1. 使用邮件的长度作为邮件的前缀。
  2. 有一个消息终结符,一个字节(或字节序列),它不能成为消息的一部分。
  3. 使用“命令”计算长度,即当您读取命令字节时,您知道应该遵循多少数据,因此请读取该数量。
  4. 第二种方法最适用于您可能不同步的情况,因为这样就会读取,直到您获得消息终结符序列,并且您确定下一个字节将成为新消息。

    您当然可以将这些方法结合起来。

答案 2 :(得分:1)

要轮询设备,最好使用类似poll(2)的多路复用系统调用,当某些数据可用于从该设备读取时,该系统调用会成功。请注意poll是多路复用:您可以一次轮询多个文件描述符,只要一个(任何)文件描述符可以用poll读取,POLLIN就会成功(或者可写,如果是的话)问POLLOUT等等......)。

poll fd POLLIN成功fd read poll select {{1}} {{1}}

当然,您需要了解硬件设备使用的有关其消息的约定。请注意,单个{{1}}可能会收到多条消息,或者只能获取一个(或更多)消息的一部分。无法阻止读取部分消息(或“数据包”) - 可能是因为您的PC串行I / O比单片机内部的串行I / O快得多。您应该知道定义消息的约定(如果您可以更改微控制器内部的软件,为此定义一个简单的约定)并实现适当的状态机和缓冲等......

注意:还有用于多路复用的旧read(2)系统调用,它具有与C10K问题相关的限制。我建议在新代码中使用{{1}}代替{{1}}。