【问题标题】:Problems reading and writing to the same file descriptor in Linux在 Linux 中读取和写入同一文件描述符的问题
【发布时间】:2018-01-10 23:54:42
【问题描述】:

我在 Linux 中成功读取了 fd,但读取的字节数为 0,这意味着已达到 EOF。我应该在每次读取时读取 19 个字节。

该项目是一个电机驱动器,它发送 19 个字节的数据包来驱动 2 个直流电机,它还需要读取来自电机的相同大小的数据包,其中包含更新的位置、命令和状态信息。

我这样打开fd:

mc_fd = InitPort("/dev/ttyS1", "COM2", O_NONBLOCK | O_RDWR | O_SYNC, B115200); 

这里是初始化端口的函数:

int InitPort( char *port, char *name, int oflags, speed_t baudRate ) {

int fd;                             // File descriptor
fd = open(port, oflags);            // Open the port like a file
assert(fd > 0);                     // Open returns -1 on error

struct termios options;             // Initialize a termios struct
tcgetattr(fd, &options);            // Populate with current attributes
cfsetospeed (&options, baudRate);   // Set baud rate out
cfsetispeed (&options, baudRate);   // Set baud rate in (same as baud rate out)
options.c_cflag &= ~CSIZE;          // Clear bit-length flag so it can be set
    //8N1 Serial Mode
    options.c_cflag |=  CS8;        // Set bit-length:  8
    options.c_cflag &= ~PARENB;     // Set parity:      none
    options.c_cflag &= ~CSTOPB;     // Set stop bit:        1
    options.c_cflag &= ~CRTSCTS;    // Set flow control:    none

options.c_iflag &= ~ICANON;         // Enable canonical input
options.c_oflag &= ~OPOST;          // Disables all output processing (prevents CR in output)
options.c_cflag |= (CLOCAL | CREAD);// Enable receiver, and set local mode
tcsetattr(fd, TCSANOW, &options);   // Set new attributes to hardware
return fd;
}

最初,我只使用了 O_RDWR 标志,并且读取 fd 会因 EAGAIN(或 EWOULDBLOCK)而失败。我一直在尝试同步和非阻塞设置,看看我是否可以接收数据包。至少现在我阅读成功(我认为)。

我能够以 120Hz 的频率写入数据包,并且读取 fd 以相同的速率返回“成功”,尽管读取的是 0 字节。

如何让 read() 读取传入的数据包? 这是读取的代码以及终端的输出:

bytesRead = read( mc_fd, readPacket, MC_PACKET_SIZE );
printf("\npacket: %019X\n", &readPacket);
perror("error type ");
printf("bytes read = %d\n", bytesRead);

packet: 00000000000B63B4140
error type : Success
bytes read = 0

数据包最低有效部分中的 8 位十六进制数字始终与显示的相似,并且不是数据包中的预期值。

这是在嵌入式 linux SBC(单板计算机)上运行的 Debian。我能够毫无问题地读取程序中的其他文件描述符。我对 Linux 还很陌生,可能会遗漏一些明显的东西。谢谢!

【问题讨论】:

  • InitPort 是什么?
  • errnoread 设置很可能被printf 破坏。先尝试perror

标签: c linux serial-port file-descriptor read-write


【解决方案1】:

...但是读取了 0 个字节,这意味着已达到 EOF。

不正确。
您正在非阻塞模式下读取串行终端
返回代码为零仅表示当时终端没有可用数据。
当您使用非阻塞模式时,这就是您的程序(您应该已经发布)必须处理的问题。

如何让 read() 读取传入的数据包?

如果您不想看到返回码为零(或将errno 设置为 EAGAIN),请使用阻塞模式(即从 open() 中删除 O_NONBLOCK 选项。
但是不要指望 read() 系统调用会为您对数据包进行消息对齐,除非您在规范模式下有文本。

学习this answer

数据包最低有效部分的 8 位十六进制数字始终与显示的相似,并且不是数据包中的预期值。

您发布的代码太少(这是结束问题的理由),但您尝试将数据读入readPacket,这似乎是一个(字节?)数组。
但随后你将readPacket 视为printf() 中的整数。

打印数组地址的地址(或整数变量的地址)什么也没做(即“8 位十六进制数...总是与显示的类似”)。您没有显示任何可能已收到的内容。

如果您使用的是 little-endian、32 位处理器,则以长整数形式访问字节数组将反转每个字的字节顺序(即 “不是预期的”),并且只访问前四个字节,可以用八个十六进制数字表示。

我对 Linux 还很陌生,可能缺少一些明显的东西。

尽管 Linux 是(几乎)“一切都是文件”的操作系统之一,但这些“文件”可能并不相同。特别是您的程序访问的设备文件,即/dev/ttyS1,是一个串行终端设备。串行终端需要额外的设备配置,这是使用 termios 结构执行的。
由于您只发布了几行程序,并且没有提及除波特率之外的任何 termios 概念,因此无法评估您的程序。


附录

既然您已经发布了一些初始化代码,那么显然还有一些错误。

无论您的编程经验如何,以下代码所做的与注释之间的不一致是一个可以延长调试时间的缺陷。

options.c_iflag &= ~ICANON;         // Enable canonical input

清除 ICANON 标志会启用非规范输入,这与评论所述相反。
你没有描述这19个字节的数据,所以无法确定规范模式是否合适。

您的 termios 初始化写得很好(即您使用正确的布尔运算符而不是直接赋值),但不完整(基于为非规范模式执行的现有代码)。
只需使用 cfmakeraw() 例程即可配置非规范模式的所有必要标志。
您的代码不会初始化 VMIN 和 VTIME 参数,但由于非规范和非阻塞模式的组合禁用了该功能,所以没关系。

由于您在描述您想要做的事情方面做得很差,因此无法建议适当的更正。

【讨论】:

    【解决方案2】:

    我应该在每次读取时读取 19 个字节。 [...]

    "/dev/ttyS1"

    在只有 16 字节 fifo 或更少的串行端口上?我认为这行不通。

    串行端口是字符设备有充分的理由 - 您希望以一种从零字节(如果启用超时)、一个字节到内部 fifo 大小的任意位置读取工作的方式进行编码。即使您(尝试)以这种方式发送它们,也不要期望它们保持整齐的包装。

    【讨论】:

    • UART FIFO 的大小与 read() 系统调用可能返回的字节数无关。
    • 使用O_NONBLOCK 的代码不应期望 read() 填充缓冲区,因为系统调用不会等待数据到达。但你是对的,可能有比 UART 内部 fifo 更多的字节——内核本身缓冲数据。但这也意味着最后一次read() 电话发生在一段时间前。
    猜你喜欢
    • 1970-01-01
    • 2019-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    • 2019-06-20
    相关资源
    最近更新 更多