2015-10-13 5 views
7

Angenommen, der Inhalt der Datei Foo.txt lautet wie folgt.Wie überspringe ich den Dateisystem-Cache beim Lesen einer Datei in Golang?

Foo Bar Bar Foo 

Betrachten Sie das folgende kurze Programm.

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

Wenn wir das Programm oben ausführen, erhalten wir keine Fehler, was das richtige Verhalten ist.

Jetzt ändere ich die syscall.Open Zeile, um die folgenden zu sein.

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

Wenn ich das Programm erneut ausführen, bekomme ich folgende (unerwünschte) Ausgabe.

Failed on read: invalid argument 

Wie kann ich die Flaggen korrekt passieren syscall.O_SYNC und syscall.O_DIRECT wie zum Überspringen der Dateisystem-Cache durch die der open man page angegeben?

Bitte beachte, dass ich bin mit der syscall File-Schnittstelle direkt anstelle des os Datei-Schnittstelle, weil ich nicht einen Weg finden könnte durch os diese Fahnen in die Funktionen zur Verfügung gestellt passieren, aber ich bin offen für Lösungen, die os Lizenz verwendet werden, Sie funktionieren ordnungsgemäß, um den Dateisystemcache bei Lesevorgängen zu deaktivieren.

Beachten Sie auch, dass ich auf Ubuntu 14.04 mit ext4 als mein Dateisystem läuft.


aktualisieren: Ich habe versucht, unter @ Nick Craig-Wood-Paket im Code zu verwenden.

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

Die Ausgabe ist die folgende

Error on read: unexpected EOF 
+1

Haben Sie versucht, die Open-Lese schließen mit denen genau zu tun Argumente in etwas anderes als gehen (C vielleicht)? Von dem, was ich auf der man-Seite sehen kann, betrifft O_SYNC nur Schreiboperationen und O_DIRECT benötigt einen genau bemessenen (?) Puffer im Benutzerbereich, dies könnte ein Go-spezifisches Problem sein, aber ich würde zuerst versuchen, das zu erreichen in C ... – mrd0ll4r

+0

@ mrd0ll4r, Tatsächlich lief ich [dieses Programm] (http://man7.org/tlpi/code/online/book/filebuff/direct_read.c.html) und bekam den gleichen Fehler 'EINVAL' auf die C-Seite. Also nein, es ist nicht spezifisch. – merlin2011

Antwort

1

Vom open Manpage unter HINWEISE:

Die O_DIRECT Flagge Ausrichtung Beschränkungen für die Länge und Anschrift des User-Space verhängen Puffer und der Dateioffset von I/Os. In Linux variieren die Ausrichtungseinschränkungen je nach Dateisystem und Kernel-Version und fehlen möglicherweise vollständig.

So könnten Sie Ausrichtungsprobleme haben, entweder der Speicher oder der Datei-Offset, oder Ihre Puffergröße könnte "falsch" sein. Was die Ausrichtungen und Größen sein sollen, ist nicht offensichtlich. Die man-Seite wird fortgesetzt:

Es gibt jedoch derzeit keine file-systemunabhängige Schnittstelle für eine Anwendung, um diese Einschränkungen für eine bestimmte Datei oder ein bestimmtes Dateisystem zu ermitteln.

Und selbst wiegt Linus in, in seiner gewohnten dezenten Art und Weise:

„Die Sache, die mich immer über O_DIRECT gestört hat, ist, dass die ganze Schnittstelle einfach nur dumm ist, und wurde wahrscheinlich von einem verwirrten entworfen Affe auf einigen schweren Geist-steuernden Substanzen. " -Linus

Viel Glück!

p.s. Stich in die Dunkelheit: Warum nicht 512 Bytes lesen?

+0

Sehr interessante Einsicht in die Komplikationen bei der Verwendung von O_DIRECT. Ist Ihnen eine Lösung für die Frage des Überspringens von Dateisystem-Cache in Go bekannt (auch wenn es sich um eine völlig andere Route handelt)? Ich brauche auch eine Lösung. – vastlysuperiorman

5

Sie können meine directio package genießen, die ich für genau diesen Zweck gemacht habe.

Von der Website

Dies ist Bibliothek für die Go Sprache Nutzung von Direct-IO unter allen unterstützten Betriebssystemen von Go (außer openbsd und plan9) zu ermöglichen.

Direct IO führt IO zu und von der Festplatte aus, ohne Daten im Betriebssystem zu puffern. Es ist nützlich, wenn Sie viele Daten lesen oder schreiben, mit denen Sie den Betriebssystem-Cache nicht füllen wollen.

Sehen Sie hier für Paket docs

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

+0

Ich habe versucht, Ihr Paket zu verwenden, aber ich bekomme "unerwartetes EOF", wenn ich versuche zu lesen. – merlin2011

+0

@ merlin2011 in Ihrem Beispiel ist '" Foo.txt "' größer als 'directio.BlockSize'? –

+0

Es ist kleiner als 'directio.BlockSize'. – merlin2011

0

können Sie versuchen, fadvice und madvice zu verwenden, aber es gibt keine Garantie. beide funktionieren wahrscheinlich mit größeren Dateien/Daten, weil:

Partialseiten werden absichtlich in der Erwartung beibehalten, dass es besser ist, benötigten Speicher zu bewahren, als nicht benötigten Speicher zu verwerfen.

siehe den Linux-Quellcode, was etwas tun wird und was nicht. POSIX_FADV_NOREUSE macht zum Beispiel nichts.

http://lxr.free-electrons.com/source/mm/fadvise.c#L62

http://lxr.free-electrons.com/source/mm/madvise.c

package main 

import "fmt" 
import "os" 
import "syscall" 

import "golang.org/x/sys/unix" 

func main() { 
    advise := false 
    if len(os.Args) > 1 && os.Args[1] == "-x" { 
     fmt.Println("setting file advise") 
     advise =true 
    } 

    data := make([]byte, 100) 
    handler, err := os.Open("Foo.txt") 
    if err != nil { 
     fmt.Println("Failed on open: ", err) 
    }; defer handler.Close() 

    if advise { 
     unix.Fadvise(int(handler.Fd()), 0, 0, 4) // 4 == POSIX_FADV_DONTNEED 
    } 

    read, err := handler.Read(data) 
    if err != nil { 
     fmt.Println("Failed on read: ", err) 
     os.Exit(1) 
    } 

    if advise { 
     syscall.Madvise(data, 4) // 4 == MADV_DONTNEED 
    } 

    fmt.Printf("read %v bytes\n", read) 
} 

/usr/bin/Zeit -v ./direct -x

Command being timed: "./direct -x" 
User time (seconds): 0.00 
System time (seconds): 0.00 
Percent of CPU this job got: 0% 
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.03 
Average shared text size (kbytes): 0 
Average unshared data size (kbytes): 0 
Average stack size (kbytes): 0 
Average total size (kbytes): 0 
Maximum resident set size (kbytes): 1832 
Average resident set size (kbytes): 0 
Major (requiring I/O) page faults: 2 
Minor (reclaiming a frame) page faults: 149 
Voluntary context switches: 2 
Involuntary context switches: 2 
Swaps: 0 
File system inputs: 200 
File system outputs: 0 
Socket messages sent: 0 
Socket messages received: 0 
Signals delivered: 0 
Page size (bytes): 4096 
Exit status: 0 
Verwandte Themen