【问题标题】:C write() doesn't send data until close(fd) is calledC write() 在调用 close(fd) 之前不会发送数据
【发布时间】:2010-07-12 12:55:03
【问题描述】:

所以我有这个测试代码通过 USB 串行端口发送“HELLO”:

int fd;
struct termios tty;

if((fd = open("/dev/ttyUSB0", O_WRONLY|O_NONBLOCK|O_NOCTTY)) == -1){
err(1, "Cannot open write on /dev/ttyUSB0");
}

tcgetattr(fd, &tty);
tty.c_iflag = 0;
tty.c_oflag = 0;
tty.c_lflag = 0;
tty.c_cflag = 0;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
cfsetospeed(&tty, B19200);
cfsetispeed(&tty, B19200);
tty.c_cflag |= CREAD|CRTSCTS|HUPCL|CS8;
tcsetattr(fd, TCSANOW, &tty);

printf("Write: %i\n", write(fd, "HELLO", 5));

sleep(5);

if(close(fd) != 0){
warn("Could not close write fd");
}

程序执行良好,发送“HELLO”,但有一个问题。调用 write() 函数时似乎不会发送“HELLO”,而是在文件描述符关闭时发送。我在上面添加了 sleep(5) 行来测试这个理论,果然,“HELLO”在程序执行后约 5 秒发送。如何在 write() 命令之后而不是在 close() 之后立即发送“HELLO”?

【问题讨论】:

  • 你检查tcsetattr返回值了吗?
  • 您说“HELLO”确实似乎会在调用 write() 函数时发送。你是如何确定这一点的?您确定它没有立即发送并且设备没有处理它吗?
  • 我正在读取另一端的串口,在上述程序关闭之前我看不到“HELLO”。
  • 您找到解决方案了吗?
  • 时间太久了我不记得了。对不起!

标签: c linux serial-port tty


【解决方案1】:

该设备是一个 tty 设备,所以 fsync 无济于事,也许 fflush 也无济于事。

默认情况下,设备在规范模式下工作,这意味着数据被打包成行单位。您可能会发现在数据中添加 cr/lf 对会导致数据被发送。

您需要确保规范模式已关闭。另外,R 的答案会很有用。

http://en.wikibooks.org/wiki/Serial_Programming/termios

【讨论】:

  • 您能在此处发布您的适当配置吗?
【解决方案2】:

来自write()的手册页:

从 write() 成功返回并不能保证数据已提交到磁盘。事实上,在一些有缺陷的实现中,它甚至不能保证已成功为数据保留空间。唯一确定的方法是在完成所有数据写入后调用 fsync(2)。

您需要在文件描述符上调用fsync() 以确保实际提交数据。

【讨论】:

  • 添加“fsync(fd);”在 write(fd) 行返回 -1: "Invalid argument" 之后。
  • 不相关。问题是终端设备处于线路缓冲模式。
  • tcflush(fd, TCOFLUSH);返回 0 但没有帮助
  • 另外,尝试传输一个新行。
  • tcdrain(fd);返回 0 但没有帮助
【解决方案3】:

首先,不知道为什么您首先将所有 termios 字段设置为 0,然后,在不对其前面的 0 进行任何修改的情况下,决定在 cflag 上设置通常的 rs232 标志。 (而不是直接在没有 OR 的情况下执行此操作,您现在将其设置为 0,如上)。

您可能喜欢的 - 而不是设置所有这些标志只是 cfmakeraw() termios 字段。

还有,同步();没有任何参数(不是 fsync!;)似乎将所有待处理的输出发送到所有文件描述符,而不仅仅是块设备。还有 tcp 套接字和 rs232..

而且 open() 也有一个选项 O_SYNC(O_SYNC 和 O_ASYNC 的名称令人困惑,但与串行线路协议是否定时无关,一个立即提交 write(),另一个生成一个信号当输入可用时捕获(有点像 dos 上基于 irq 的 rs232;)

在 open() 中设置 O_SYNC 可能已经解决了您的问题。

还有“通过读取另一端的数据”...这些东西称为“leds”和“电阻器”,您可以将它们连接到 TXD 并查看数据;)还有一些东西称为“rs232 突破”框”或可以使其直接可见的范围;)这种方式比“猜测”哪一侧行为不正常要容易得多。

警告:未测试代码。它编译。但我所有的 ttyUSB0 电缆都在另一栋楼里。但我认为你的主要问题是 O_SYNC 。将所有 termios 废话设置为 0 与 cfmakeraw() 几乎相同...如果您只打开它,为什么还要设置 CREAD? (为什么要打开它只写而不是读写? - 而且只写你不必害怕它成为一个控制 tty (O_NOCTTY ;) 所以在只写的情况下,这也不是完全需要的.. .

刚刚注意到 %i(与 %d btw 相同)格式化程序也会触发类型不匹配,警告 write() 的 ssize_t 返回值,因此将其转换为 (int)

#include<termios.h>
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>

void main(){
int fd;
struct termios tty;
fd=-1;
while(fd<0){fd=open("/dev/ttyUSB0",O_WRONLY|O_NONBLOCK|O_NOCTTY|O_SYNC);sleep(1);};
cfmakeraw(&tty);
tty.c_cflag=CREAD|CRTSCTS|HUPCL|CS8;
cfsetospeed(&tty,B19200);
cfsetispeed(&tty,B19200);
tcsetattr(fd,TCSANOW,&tty);
printf("Write: %i\n",(int)write(fd,"HELLO",5));
sync();//if all else fails, also try without, O_SYNC should already fix that.
close(fd);
};

【讨论】:

    【解决方案4】:

    输出端口通常是缓冲的,因此在您写入输出流与实际发送到磁盘、线路或其他内容的内容之间存在或多或少的差距。这通常是为了提高效率。

    请参阅 fflush(3) 以强制将缓冲区提交到输出。

    您也可以以一种非缓冲方式打开输出描述符,但使用fflush 说“就是这样,我完成了”可能会更好。

    【讨论】:

    • 哎呀,是的,fflush(3) 用于FILE*,正如其他人指出的那样,如果您确实需要使用 FD,fsync(2) 可能就是您想要的。
    • 其实看你平台上的fcntl(2)。在某些平台上,这将允许您禁用输出缓冲,但这不是可移植的。如果您想完全放弃可移植性,那么您设备的 ioctl 可能会对此有所帮助,但这越来越令人绝望了。
    • fd -> fdopen -> fh; fh -> fileno -> fd... 问题在于没有“FILE*”或 fd(如果符合 POSIX 而不是严格的 C89/C99)
    【解决方案5】:

    改变这一行:

    tty.c_cc[VMIN] = 0;
    

    到这里:

    tty.c_cc[VMIN] = 1;
    

    【讨论】:

      【解决方案6】:

      缓冲区未刷新。刷新。

      【讨论】:

      • fflush 需要一个 FILE*,这里 OP 有一个文件描述符(并且可能不想使用 stdio)
      • fdopen可以使用;如果这解决了 OP 问题并且他没有理由不使用文件处理程序而不是文件描述符。
      【解决方案7】:

      请参阅this question。基本上,您需要刷新文件才能在需要时进行 IO。

      【讨论】:

        【解决方案8】:

        尝试做一个

        fflush( NULL );
        

        write() 之后。也许有一些内部缓冲区没有被刷新。

        【讨论】:

        • 无法解决问题有两个原因:1) fflush 是否关心打开时打开的文件?也许需要一个 fdopen ; 2) 手册页中的注释: fflush() 仅刷新 C 库提供的用户空间缓冲区。为了确保数据物理存储在磁盘上,内核缓冲区也必须刷新,例如,使用 sync(2) 或 fsync(2)
        猜你喜欢
        • 2011-12-07
        • 2020-09-04
        • 1970-01-01
        • 1970-01-01
        • 2011-02-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-24
        相关资源
        最近更新 更多