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

Linux 在Golang中读取文件时,如何跳过文件系统缓存?,linux,caching,go,system-calls,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 := m

假设文件
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)
当我再次运行该程序时,我会得到以下(不需要的)输出

如何正确传递由指定的用于跳过文件系统缓存的标志
syscall.O_SYNC
syscall.O_DIRECT

请注意,我直接使用
syscall
文件接口而不是
os
文件接口,因为我找不到将这些标志传递到
os
提供的函数中的方法,但我对使用
os
的解决方案持开放态度,只要这些解决方案能够正确地在读取时禁用文件系统缓存

还请注意,我运行的是
ubuntu14.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

打开
手册页,在注释下:

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

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

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

甚至连莱纳斯也以他一贯低调的方式表示:

“关于O_DIRECT,一直困扰我的事情是整个界面都很愚蠢,很可能是由一只精神错乱的猴子在一些严重的精神控制物质上设计的。”-莱纳斯

祝你好运

p、 美国《暗中捅:为什么不读512字节?

你可能会喜欢我的书,我正是为了这个目的而写的

从网站

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

直接IO在不缓冲操作系统中的数据的情况下执行与磁盘之间的IO操作。当您正在读取或写入大量不希望用其填充操作系统缓存的数据时,它非常有用

请参见此处以获取包文档


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

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

请参阅linux源代码,什么可以做什么不可以。例如,POSIX_FADV_NOREUSE什么都不做

/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

除了go(可能是C)之外,你有没有尝试过用这些确切的参数来进行开-读-关操作?从我在手册页上看到的情况来看,O_SYNC只影响写操作,O_DIRECT需要用户空间中某种大小准确的(?)缓冲区,这可能是一个特定于go的问题,但我会首先尝试在C中实现这一点。@mrd0ll4r,实际上我在C端运行并得到了相同的错误
EINVAL
。因此,不,它不是针对特定目标的。关于使用O_DIRECT的复杂性的非常有趣的见解。对于在Go中跳过文件系统缓存的问题(即使涉及完全不同的路径),您知道有什么解决方案吗?我还需要一个解决方案。我试图使用您的软件包,但当我尝试阅读时,我得到了
意外的EOF
。@merlin2011在您的示例中是
“Foo.txt”
大于
directio.BlockSize
?它小于
directio.BlockSize
@merlin2011它需要大于
directio.BlockSize
,否则您将得到该错误。
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)
}
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