49

It turns out this whole misunderstanding of the open() versus fopen() stems from a buggy I2C driver in the Linux 2.6.14 kernel on an ARM. Backporting a working bit bashed driver solved the root cause of the problem I was trying to address here.

I'm trying to figure out an issue with a serial device driver in Linux (I2C). It appears that by adding timed OS pauses (sleeps) between writes and reads on the device things work ... (much) better.

Aside: The nature of I2C is that each byte read or written by the master is acknowledged by the device on the other end of the wire (slave) - the pauses improving things encourage me to think of the driver as working asynchronously - something that I can't reconcile with how the bus works. Anyhoo ...

I'd either like to flush the write to be sure (rather than using fixed duration pause), or somehow test that the write/read transaction has completed in an multi-threaded friendly way.

The trouble with using fflush(fd); is that it requires 'fd' to be stream pointer (not a file descriptor) i.e.

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

My problem is that I require the use of the ioctl(), which doesn't use a stream pointer. i.e.

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

Suggestions?

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Jamie
  • 7,075
  • 12
  • 56
  • 86
  • 2
    In user-land code, the write() function is automatically non-buffered. You don't need to flush it -- it is self-flushing. Indeed, unless you use special system calls, it is also synchronous with device driver. – Jonathan Leffler Nov 03 '08 at 23:09
  • If you are writing a kernel-level module, the rules may be somewhat different, but then you should probably be using the file descriptor i/o and not standard (file pointer) i/o. – Jonathan Leffler Nov 03 '08 at 23:10

6 Answers6

45

I think what you are looking for may be

int fsync(int fd);

or

int fdatasync(int fd);

fsync will flush the file from kernel buffer to the disk. fdatasync will also do except for the meta data.

Shahbaz
  • 46,337
  • 19
  • 116
  • 182
Danke Xie
  • 1,757
  • 17
  • 13
  • 1
    The OP stated this is an i2c character device, not a disk. – Alnitak Apr 20 '14 at 22:45
  • 2
    Ok, i2c dev nodes are not page-buffered so it does not need to be flushed/sync'ed. The fops in device driver determine what are actually done. – Danke Xie May 22 '14 at 01:37
  • 8
    Though this is not the solution to the OP's question, it is useful to others asking the main title question. I **am** writing to a disk via an fd, so it was very helpful to see how to flush the kernel buffers. – Eponymous Aug 12 '15 at 14:27
30

You have two choices:

  1. Use fileno() to obtain the file descriptor associated with the stdio stream pointer

  2. Don't use <stdio.h> at all, that way you don't need to worry about flush either - all writes will go to the device immediately, and for character devices the write() call won't even return until the lower-level IO has completed (in theory).

For device-level IO I'd say it's pretty unusual to use stdio. I'd strongly recommend using the lower-level open(), read() and write() functions instead (based on your later reply):

int fd = open("/dev/i2c", O_RDWR);
ioctl(fd, IOCTL_COMMAND, args);
write(fd, buf, length);
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • Avoiding is what I've been doing; the "Aside" in my original post led me to thinking that the file descriptors _might_ have been buffered. Thanks for confirming that they aren't and that I should be looking for another reason pummelling the I2C bus causes trouble. – Jamie Nov 04 '08 at 14:19
  • 2
    Let's make the terminology clear - a UNIX file descriptor is an integer index and is supposed to be unbuffered so that a call to write/read is immediate. It is supposed to be very efficient for "large" writes and inefficient for single byte writes. fopen gives you buffered I/O. Use open/read/write. – plinth Nov 04 '08 at 14:31
13

fflush() only flushes the buffering added by the stdio fopen() layer, as managed by the FILE * object. The underlying file itself, as seen by the kernel, is not buffered at this level. This means that writes that bypass the FILE * layer, using fileno() and a raw write(), are also not buffered in a way that fflush() would flush.

As others have pointed out, try not mixing the two. If you need to use "raw" I/O functions such as ioctl(), then open() the file yourself directly, without using fopen<() and friends from stdio.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
unwind
  • 391,730
  • 64
  • 469
  • 606
5

Have you tried disabling buffering?

setvbuf(fd, NULL, _IONBF, 0);
rustyx
  • 80,671
  • 25
  • 200
  • 267
  • 1
    This worked for me. I'm not sure what my setup is, but I used this and "fsync" (using fileno to get the int instead of a FILE*) and I was able to seek and read from the file after closing and reopening it. – Grifball Jun 04 '17 at 01:57
  • 2
    This only works for fopen() and not for open(). The original question asks about disabling buffering for open() call. – Anupam Sep 23 '18 at 08:19
  • 2
    `open()` has no buffer, it goes straight to the device driver. – rustyx Sep 23 '18 at 08:27
3

It sounds like what you are looking for is the fsync() function (or fdatasync()?), or you could use the O_SYNC flag in your open() call.

jstedfast
  • 35,744
  • 5
  • 97
  • 110
2

If you want to go the other way round (associate FILE* with existing file descriptor), use fdopen() :

                                                          FDOPEN(P)

NAME

       fdopen - associate a stream with a file descriptor

SYNOPSIS

       #include <stdio.h>

       FILE *fdopen(int fildes, const char *mode);
bgw
  • 2,036
  • 1
  • 19
  • 28