【问题标题】:How do I skip the filesystem cache when reading a file in Golang?在 Golang 中读取文件时如何跳过文件系统缓存?
【发布时间】:2016-01-10 18:06:18
【问题描述】:

假设文件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

【问题讨论】:

  • 您是否尝试过在 go(可能是 C)中使用这些确切的参数来执行 open-read-close?从我在手册页上可以看到,O_SYNC 只影响写入操作,而 O_DIRECT 需要用户空间中某种精确大小的(?)缓冲区,这可能是一个特定于 go 的问题,但我首先尝试让它工作C...
  • @mrd0ll4r,实际上我在 C 端运行了this program 并得到了相同的错误EINVAL。所以不,它不是特定的。

标签: linux caching go system-calls


【解决方案1】:

来自 open 手册页,在 NOTES 下:

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

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

但是,目前没有独立于文件系统的接口供应用程序发现给定文件或文件系统的这些限制。

就连 Linus 也以他一贯低调的方式参与进来:

“O_DIRECT 一直困扰着我的一点是,整个界面简直是愚蠢的,而且很可能是由一只发疯的猴子在一些严重的精神控制物质上设计的。” ——莱纳斯

祝你好运!

附言暗中刺探:为什么不读取 512 字节?

【讨论】:

  • 关于使用 O_DIRECT 的复杂性的非常有趣的见解。您是否知道在 Go 中跳过文件系统缓存问题的任何解决方案(即使它涉及完全不同的路线)?我也需要解决方案。
【解决方案2】:

您可能会喜欢我为此目的而制作的directio package

来自网站

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

Direct IO 在不缓冲 OS 中的数据的情况下与磁盘进行 IO。当您读取或写入大量不想填满操作系统缓存的数据时,它很有用。

请参阅此处获取软件包文档

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

【讨论】:

  • 我尝试使用你的包,但是当我尝试阅读时得到unexpected EOF
  • @merlin2011 在您的示例中是 "Foo.txt" 大于 directio.BlockSize
  • 小于directio.BlockSize
  • @merlin2011 它需要大于directio.BlockSize 否则你会得到那个错误。
【解决方案3】:

您可以尝试使用 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-20
    • 1970-01-01
    相关资源
    最近更新 更多