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

oxosxuxt  于 2023-11-14  发布在  Go
关注(0)|答案(3)|浏览(131)

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

gcxthw6b

gcxthw6b1#

你可能会喜欢我的directio package,我正是为此目的而做的。
从现场
这是Go语言的库,可以在所有支持的Go操作系统(除了openbsd和plan9)下使用Direct IO。
直接IO在操作系统中不缓冲数据的情况下从磁盘进行IO。当您阅读或写入大量不想填满操作系统缓存的数据时,它很有用。
请参阅此处的软件包文档
https://pkg.go.dev/github.com/ncw/directio

0yg35tkg

0yg35tkg2#

open手册页的“NOTES”下:
O_DIRECT标志可能会对用户空间缓冲区的长度和地址以及I/O的文件偏移量施加对齐限制。在Linux中,对齐限制因文件系统和内核版本而异,可能完全不存在。
因此,您可能会遇到内存或文件偏移的对齐问题,或者您的缓冲区大小可能“错误”。对齐和大小 * 应该 * 是什么并不明显。手册页继续:
然而,目前没有独立于文件系统的接口,用于应用程序发现给定文件或文件系统的这些限制。
甚至连莱纳斯也以他一贯低调的方式加入进来:
“O_DIRECT一直困扰着我的是,整个界面都很愚蠢,可能是一只精神错乱的猴子用一些严重的精神控制物质设计的。
祝你好运!
p.s.黑暗中的刺:为什么不读512字节?

2vuwiymt

2vuwiymt3#

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

相关问题