在Golang中读取文件时如何跳过文件系统缓存?

时间:2015-10-13 05:50:06

标签: linux caching go system-calls

假设文件Foo.txt的内容如下。

Foo Bar Bar Foo

考虑以下简短程序。

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

当我们运行上面的程序时,我们没有错误,这是正确的行为。

现在,我将syscall.Open行修改为以下内容。

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

当我再次运行程序时,我得到以下(不合需要的)输出。

Failed on read:  invalid argument

如何正确传递open man page指定的标记syscall.O_SYNCsyscall.O_DIRECT以跳过文件系统缓存?

请注意,我直接使用syscall文件接口而不是os文件接口,因为我找不到将这些标志传递到os提供的函数的方法,但是我对使用os的解决方案持开放态度,条件是它们可以正常工作以禁用读取时的文件系统缓存。

另请注意,我正在Ubuntu 14.04上运行ext4作为我的文件系统。

更新:我尝试在下面的代码中使用@Nick Craig-Wood的软件包。

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

输出如下

Error on read:  unexpected EOF

3 个答案:

答案 0 :(得分:5)

您可以享受我为此目的而制作的directio package

来自网站

这是Go语言的库,可以在Go的所有受支持的操作系统下使用Direct IO(openbsd和plan9除外)。

直接IO在磁盘上执行IO而不在OS中缓冲数据。当您正在阅读或编写大量您不希望填充操作系统缓存的数据时,它非常有用。

请参阅此处查看包文档

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

答案 1 :(得分:1)

open手册页的NOTES:

  

O_DIRECT标志可能会对用户空间缓冲区的长度和地址以及I / O的文件偏移量施加对齐限制。在Linux中,对齐限制因文件系统和内核版本而异,可能完全不存在。

因此,您可能会遇到内存或文件偏移的对齐问题,或者您的缓冲区大小可能是错误的"。 应该的对齐和尺寸不明显。手册页继续:

  

但是,当前没有与文件系统无关的接口,应用程序可以发现给定文件或文件系统的这些限制。

即使是莱纳斯也会以他惯常的低调态度来衡量:

  

" O_DIRECT一直让我感到不安的是,整个界面都是愚蠢的,可能是由一群精神错乱的猴子在一些严肃的控制精神的物质上设计的。" -Linus

祝你好运!

P.S。在黑暗中刺:为什么不读512字节?

答案 2 :(得分:0)

你可以尝试使用fadvice和madvice,但不能保证。两者都可能更适用于更大的文件/数据,因为:

  

部分页面是故意保留的,期望保留所需的内存比丢弃不需要的内存更好。

查看linux源代码,什么会做什么,什么不做。例如,POSIX_FADV_NOREUSE没有做任何事情。

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 / time -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