为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕运维8079593 2021-11-15 16:06:50
假设文件内容Foo.txt如下。Foo Bar Bar Foo考虑以下短程序。package mainimport "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如何正确传递标志syscall.O_SYNC并syscall.O_DIRECT按照open手册页的指定跳过文件系统缓存?请注意,我syscall直接使用文件接口而不是os文件接口,因为我找不到将这些标志传递给 提供的函数的方法os,但我对使用的解决方案持开放态度,os前提是它们可以正常工作以禁用读。还要注意,我正在上Ubuntu 14.04与ext4我的文件系统。更新:我尝试在下面的代码中使用@Nick Craig-Wood 的包。package mainimport "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 回答

?
呼啦一阵风

TA贡献1802条经验 获得超6个赞

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

从网站

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

直接 IO 执行与磁盘的 IO 操作,而无需在操作系统中缓冲数据。当您正在读取或写入大量不想填满操作系统缓存的数据时,它很有用。

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

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


查看完整回答
反对 回复 2021-11-15
?
FFIVE

TA贡献1797条经验 获得超6个赞

open手册页,在 NOTES 下:

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

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

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

甚至 Linus 也以他一贯低调的方式表示:

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

祝你好运!

ps 暗中刺痛:为什么不读取 512 字节?


查看完整回答
反对 回复 2021-11-15
?
神不在的星期二

TA贡献1963条经验 获得超6个赞

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


查看完整回答
反对 回复 2021-11-15
  • 3 回答
  • 0 关注
  • 318 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信