Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I skip the filesystem cache when reading a file in Golang?

Assume that the contents of the file Foo.txt are as follows.

Foo Bar Bar Foo

Consider the following short program.

package main

import "syscall"
import "fmt"


func main() {
    fd, err := syscall.Open("Foo.txt", syscall.O_RDONLY, 0)
    if err != nil {
        fmt.Println("Failed on open: ", err)
    }
    data := make([]byte, 100)
    _, err = syscall.Read(fd, data)
    if err != nil {
        fmt.Println("Failed on read: ", err)
    }
    syscall.Close(fd)
}

When we run the program above, we get no errors, which is correct behavior.

Now, I modify the syscall.Open line to be the following.

fd, err := syscall.Open("Foo.txt", syscall.O_RDONLY | syscall.O_SYNC | syscall.O_DIRECT, 0)

When I run the program again, I get the following (undesirable) output.

Failed on read:  invalid argument

How can I correctly pass the flags syscall.O_SYNC and syscall.O_DIRECT as specified by the the open man page for skipping the filesystem cache?

Note that I am using the syscall file interface directly instead of the os file interface because I could not find a way to pass those flags into the functions provided by os, but I am open to solutions that use os provided that they work correctly to disable the filesystem cache on reads.

Note also that I am running on Ubuntu 14.04 with ext4 as my filesystem.


Update: I tried to use @Nick Craig-Wood's package in the code below.

package main

import "io"
import "github.com/ncw/directio" 
import "os"
import "fmt"


func main() {
    in, err := directio.OpenFile("Foo.txt", os.O_RDONLY, 0666)
    if err != nil {
        fmt.Println("Error on open: ", err)
    }

    block := directio.AlignedBlock(directio.BlockSize)
    _, err = io.ReadFull(in, block)
    if err != nil {
        fmt.Println("Error on read: ", err)
    }
}

The output is the following

Error on read:  unexpected EOF
like image 369
merlin2011 Avatar asked Oct 13 '15 05:10

merlin2011


2 Answers

You may enjoy my directio package which I made for exactly this purpose.

From the site

This is library for the Go language to enable use of Direct IO under all supported OSes of Go (except openbsd and plan9).

Direct IO does IO to and from disk without buffering data in the OS. It is useful when you are reading or writing lots of data you don't want to fill the OS cache up with.

See here for package docs

http://go.pkgdoc.org/github.com/ncw/directio

like image 127
Nick Craig-Wood Avatar answered Nov 06 '22 16:11

Nick Craig-Wood


From the open man page, under NOTES:

The O_DIRECT flag may impose alignment restrictions on the length and address of user-space buffers and the file offset of I/Os. In Linux alignment restrictions vary by file system and kernel version and might be absent entirely.

So you could have alignment issues, of either the memory or the file offset, or your buffer size could be "wrong". What the alignments and sizes should be is not obvious. The man page continues:

However there is currently no file system-independent interface for an application to discover these restrictions for a given file or file system.

And even Linus weighs in, in his usual understated manner:

"The thing that has always disturbed me about O_DIRECT is that the whole interface is just stupid, and was probably designed by a deranged monkey on some serious mind-controlling substances." —Linus

Good luck!

p.s. Stab in the dark: why not read 512 bytes?

like image 2
Rhythmic Fistman Avatar answered Nov 06 '22 16:11

Rhythmic Fistman