C write() 在调用 close(fd) 之前不会发送数据

发布于 2024-09-09 05:48:03 字数 774 浏览 6 评论 0原文

所以我有这个测试代码来通过 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”,但有一个问题。 “HELLO”似乎不是在调用 write() 函数时发送的,而是在文件描述符关闭时发送的。我在上面添加了 sleep(5) 行来测试这个理论,果然,“HELLO”在程序执行后大约 5 秒被发送。如何在 write() 命令之后立即发送“HELLO”,而不是在 close() 时发送?

So I have this test code to send "HELLO" over a USB serial port:

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");
}

The program executes fine and "HELLO" is sent but there is one problem. "HELLO" doesn't seem to be sent when the write() function is called, but rather when the file descriptor is closed. I added the sleep(5) line above to test out this theory and sure enough, "HELLO" is sent ~5 seconds after the program is executed. How can I get "HELLO" to be sent immediately after the write() command instead of on close()?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

无语# 2024-09-16 05:48:03

该设备是一个 tty 设备,因此 fsync 不会有帮助,也许也不会 fflush。

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

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

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

The device is a tty device, so fsync isn't going to help, maybe not fflush either.

By default the device is working in canonical mode which means that data is packaged up into units of lines. You'll probably find that adding a cr/lf pair to your data will cause it to be sent.

You need to make sure canonical mode is off. Also, R's answer will be of use.

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

摇划花蜜的午后 2024-09-16 05:48:03

write() 的手册页:

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

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

From the man page of write():

A successful return from write() does not make any guarantee that data has been committed to disk. In fact, on some buggy implementations, it does not even guarantee that space has successfully been reserved for the data. The only way to be sure is to call fsync(2) after you are done writing all your data.

You need to call fsync() on your file descriptor to ensure the data is actually committed.

南风起 2024-09-16 05:48:03

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

您可能想要的只是 cfmakeraw() termios 字段,而不是设置所有这些标志。

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

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

在 open() 中设置 O_SYNC

也可能已经解决了您的问题“通过读取另一端的数据”...有这些事情。称为“LED”和“电阻器”,您可以将它们连接到 TXD 并查看数据;)还有一些称为“RS232 接线盒”或可以使其直接可见的示波器;)这样比“更容易”猜测哪一方表现不佳。

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

注意到 %i (顺便说一句,与 %d 相同)格式化程序也会触发类型不匹配警告 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);
};

first of all, no idea why you first set all termios fields to 0, and then later, without any modification to that 0 preceding it, decide to set the usual rs232 flags on the cflag. (rather than doing that without the OR directly, where you now set it to 0, above).

what you might like -instead- of setting all those flags is just cfmakeraw() the termios fields.

also, sync(); without any parameters (NOT fsync! ;) seems to send all pending output to ALL filedescriptors, not just block devices. also tcp sockets and rs232..

and also open() has an option O_SYNC (O_SYNC and O_ASYNC have confusing names but have nothing to do with the serial line protocol being clocked or not, the one immediately commits write()'s and the other generates a signal for trapping when input becomes available (kinda like irq based rs232 on dos ;)

setting O_SYNC in the open() might already fix your issue.

also 'by reading the data on the other end'... there are these things called 'leds' and 'resistors' which you can just connect to TXD and SEE the data ;) also there are things called 'rs232 breakout box' or a scope that can make it -directly visible- ;) much easier that way than 'guessing' which side is not behaving properly.

WARNING: DID NOT TEST CODE. it compiles. but i have all my ttyUSB0 cables in another building. but i think your main issue is O_SYNC anyway. setting all termios crap to 0 is pretty much the same as cfmakeraw()... also why set CREAD if you are gonna open it write only? (why open it write only rather than readwrite anyway? - and also with write only you won't have to be scared of it becoming a controlling tty (O_NOCTTY ;) so in the case of write only, that's not exactly needed either...

just noticed the %i (same for %d btw) formatter also triggers a type mismatch warning the ssize_t return value of write() so casted that to (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);
};
爱本泡沫多脆弱 2024-09-16 05:48:03

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

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

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

Output ports are often buffered, so that there's a greater or lesser gap between you writing to an output stream, and the content actually being sent to the disk, line, or whatever. This is generally for efficiency.

See fflush(3) to force the buffer to be committed to the output.

You might also be able to open the output descriptor in a way which makes it non-buffered, but using fflush to say 'that's it, I'm done', is probably better.

俯瞰星空 2024-09-16 05:48:03

将此行:更改

tty.c_cc[VMIN] = 0;

为:

tty.c_cc[VMIN] = 1;

Change this line:

tty.c_cc[VMIN] = 0;

to this:

tty.c_cc[VMIN] = 1;
救星 2024-09-16 05:48:03

缓冲区没有被刷新。冲洗。

the buffer isn't flushed. fflush.

北风几吹夏 2024-09-16 05:48:03

请参阅此问题。基本上,您需要刷新文件以便在需要时进行 IO。

Please see this question. BAsically you need to flush the file in order for IO to take place when you want to.

许仙没带伞 2024-09-16 05:48:03

尝试

fflush( NULL );

write() 之后执行 a。也许有一些内部缓冲区未刷新。

Try doing a

fflush( NULL );

after the write(). Maybe there is some internal buffer which is not flushed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文