如何使用文件描述符刷新写入?

50

事实证明这个open()和fopen()的误解源于Linux 2.6.14内核上一个ARM平台下有bug的I2C驱动程序。回退到一个有效的bit bashed驱动程序解决了我在此要处理的问题的根本原因。

我正在尝试解决Linux中(I2C)串行设备驱动程序的问题。似乎通过在写入和读取设备之间添加定时操作系统暂停(sleeps),事情会变得更好。(更好)。

另外:I2C的特性是主设备读取或写入的每个字节都由另一端的设备(从属设备)进行确认——暂停改善了事情,鼓励我认为驱动程序是异步工作的——这是我无法与总线如何工作相协调的地方。无论如何......

我想要刷新写入,以确保(而不是使用固定持续时间的暂停),或者以某种多线程友好的方式测试写入/读取事务是否已经完成

使用fflush(fd);的问题在于它需要“fd”是流指针(而不是文件描述符),即

FILE * fd = fopen("filename","r+");
... // do read and writes
fflush(fd);

我的问题是我需要使用ioctl(),它不使用流指针。

int fd = open("filename",O_RDWR);
ioctl(fd,...);

有什么建议吗?


2
在用户空间代码中,write()函数自动为非缓冲。您不需要刷新它 - 它是自动刷新的。实际上,除非您使用特殊的系统调用,否则它也与设备驱动程序同步。 - Jonathan Leffler
如果您正在编写内核级模块,则规则可能会有所不同,但是那时您应该使用文件描述符 I/O 而不是标准(文件指针)I/O。 - Jonathan Leffler
6个回答

46

我认为你可能在寻找的是

int fsync(int fd);
或者
int fdatasync(int fd);

fsync会将文件从内核缓冲区刷新到磁盘。fdatasync也会除了元数据。


1
OP表示这是一个i2c字符设备,而不是磁盘。 - Alnitak
2
好的,i2c设备节点不是页面缓冲的,因此不需要刷新/同步。设备驱动程序中的fops确定实际执行的操作。 - Danke Xie
8
虽然这并不是回答原帖问题的解决方案,但对于其他人提出主标题问题是有用的。通过fd写入磁盘,因此了解如何刷新内核缓冲区非常有帮助。 - Eponymous

30

你有两个选择:

  1. 使用fileno()来获取与stdio流指针相关联的文件描述符。

  2. 完全不使用<stdio.h>,这样你就不需要担心刷新 - 所有写入都会立即发送到设备上,对于字符设备,write()调用甚至在低级IO完成之前都不会返回(理论上)。

对于设备级别的IO操作,我会说使用stdio是相当不寻常的。我强烈建议使用更低级别的open()read()write()函数(基于你后来的回复):

int fd = open("/dev/i2c", O_RDWR);
ioctl(fd, IOCTL_COMMAND, args);
write(fd, buf, length);

避免使用<stdio.h>是我一直在做的事情;我原始帖子中的“旁白”让我想到文件描述符可能已经被缓冲了。感谢您确认它们没有被缓冲,并且我应该寻找另一个原因,造成I2C总线故障。 - Jamie
2
让我们澄清一下术语 - UNIX文件描述符是一个整数索引,应该是无缓冲的,以便对write/read的调用是立即的。它应该非常适用于“大”写入,并且对于单字节写入效率低下。fopen提供了带缓冲的I/O。请使用open/read/write。 - plinth

13

fflush()仅刷新stdio fopen()层所添加的缓冲区,由FILE *对象管理。作为内核所见的底层文件并未在此级别上进行缓冲。这意味着绕过FILE *层使用fileno()和原始write()执行的写操作也不会以fflush()所能刷新的方式被缓冲。

正如其他人指出的那样,尽可能不要混合使用两者。如果需要使用“原始”I/O函数(例如ioctl()),则应该直接open()文件而不使用stdio中的fopen()等函数。不要混用它们。


5

你尝试过禁用缓存吗?


(是否尝试过关闭缓存?)
setvbuf(fd, NULL, _IONBF, 0);

1
这对我有用。我不确定我的设置是什么,但我使用了这个和“fsync”(使用fileno获取int而不是FILE*),在关闭和重新打开文件后,我能够寻找并读取文件。 - Grifball
2
这仅适用于fopen()而不适用于open()。原始问题询问如何禁用open()调用的缓冲。 - Anupam
2
open()没有缓冲区,它直接进入设备驱动程序。 - rustyx

3

听起来你需要的是fsync()函数(或者fdatasync()?),或者你可以在open()调用中使用O_SYNC标志。


2
如果您想反过来使用(将FILE*与现有文件描述符关联),请使用fdopen()函数:
                                                          FDOPEN(P)

NAME

       fdopen - associate a stream with a file descriptor

SYNOPSIS

       #include <stdio.h>

       FILE *fdopen(int fildes, const char *mode);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接