Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mmap 代码补充 #1

Open
wathenjiang opened this issue Oct 29, 2021 · 5 comments
Open

mmap 代码补充 #1

wathenjiang opened this issue Oct 29, 2021 · 5 comments
Assignees
Labels
documentation Improvements or additions to documentation

Comments

@wathenjiang
Copy link
Owner

mmap 机制可以很大程度上简化磁盘文件操作,通过一定的封装,磁盘上的文件读写操作可以简化为内存的读写操作。
(1)直接读写内存-简洁明了

简单地读写内存非常简单,例如下面向数组的第三个元素进行赋值:

a := make([]int, 10)
a[0] = 511

内存中字段的读写操作相当自然,你甚至可能没有注意到在写内存这件事情。

(2)读写磁盘文件-步骤复杂

但是读写磁盘上的文件就相对复杂很多了,例如我们要在磁盘上读写的文件内容如下:

Hello World! This project is for learning mmap.

如果我们要读取文件开始 6 个偏移量后的 5 个字节数据,那么如下代码是一个方式:

package main

import (
	"fmt"
	"os"
)

func main() {
	f, _ := os.Open("./article.txt")
	defer f.Close()
	buffer := make([]byte, 5)
	f.Seek(6, 0)
	_, _ = f.Read(buffer)
  fmt.Println(string(buffer)) // 输出:world
}

读取磁盘上文件对比直接读取内存麻烦很多,步骤如下:

  • 打开文件,并且注意文件资源的及时释放
  • 创建一个 []byte 数组,用于存储从磁盘上读取的字节
  • 利用 Seek 方法设置文件读取的偏移量
  • 利用 Read 方法读取文件中的字节数据到 []byte 数组中

磁盘上文件读写比内存读写麻烦很多的原因就是 CPU 只能读写内存,而不能直接读写磁盘。磁盘文件上的字节数据需要首先读取到内存中,才能够被内存操作。

(3)mmap-读写磁盘文件就像读写内存

mmap 会负责将磁盘上不一定连续存储的数据按 block 读取到内核空间中,内核空间中的磁盘数据是连续的。然后,将内核空间中的数据映射到用户空间中,这个过程不涉及拷贝。

利用 mmap 机制,操作用户空间中的数据相当于操作磁盘上文件,它们之间的一致性依赖于缺页异常以及异步的 flush 机制实现。

案例代码如下:

package main

import (
	"fmt"
	"os"
	"syscall"
	"unsafe"
)

const defaultMaxFileSize = 1 << 30        // 假设文件最大为 1G
const defaultMemMapSize = 128 * (1 << 20) // 假设映射的内存大小为 128M

type Demo struct {
	file    *os.File                  // 文件
	data    *[defaultMaxFileSize]byte // 该数组负责指向 mmap 读取数据的 []byte 首地址
	dataRef []byte                    // 引用了 Mmmap 调用返回的 []byte 数组
}

// 根据 condition 状态位进行断言
func _assert(condition bool, msg string, v ...interface{}) {
	if !condition {
		panic(fmt.Sprintf(msg, v...))
	}
}

func (demo *Demo) mmap() {
	b, err := syscall.Mmap(int(demo.file.Fd()), 0, defaultMemMapSize, syscall.PROT_WRITE|syscall.PROT_READ, syscall.MAP_SHARED)
	_assert(err == nil, "failed to mmap", err)
	demo.dataRef = b
	demo.data = (*[defaultMaxFileSize]byte)(unsafe.Pointer(&b[0]))
}

// grow 的作用是尝试将文件扩容至 size 大小,如果已经大于 size,那么不做任何操作
func (demo *Demo) grow(size int64) {
	if info, _ := demo.file.Stat(); info.Size() >= size {
		return
	}
	_assert(demo.file.Truncate(size) == nil, "failed to truncate")
}

// 资源释放
func (demo *Demo) munmap() {
	_assert(syscall.Munmap(demo.dataRef) == nil, "failed to munmap")
	demo.data = nil
	demo.dataRef = nil
}

func main() {
	_ = os.Remove("tmp.txt")
	// open file
	f, _ := os.OpenFile("tmp.txt", os.O_CREATE|os.O_RDWR, 0644) // 644 只有拥有者有读写权限;而属组用户和其他用户只有读权限
	demo := &Demo{file: f}
	demo.grow(1)
	// mmap
	demo.mmap()
	defer demo.munmap()
	// write into file on the disk like write into array in memory
	str := "helloworld"
	demo.grow(int64(len(str)))
	for i := 0; i < len(str); i++ {
		demo.data[i] = str[i]
	}
}

案例来源说明:Go Mmap 文件内存映射简明教程

@wathenjiang wathenjiang added the documentation Improvements or additions to documentation label Oct 29, 2021
@wathenjiang wathenjiang self-assigned this Oct 29, 2021
@jingxuantju
Copy link

您好 您这个系列的Mysql的零拷贝技术好像看不到了

@wathenjiang
Copy link
Owner Author

您好 您这个系列的 Mysql 的零拷贝技术好像看不到了

你能给出一个 URL 吗?我看看问题出在哪里。

@jingxuantju
Copy link

您好 您这个系列的 Mysql 的零拷贝技术好像看不到了

你能给出一个 URL 吗?我看看问题出在哪里。

https://spongecaptain.cool/zerocopyofmysql

也就是这一篇文章的底部的引用的第8个文章https://spongecaptain.cool/SimpleClearFileIO/2.%20DMA%20%E4%B8%8E%E9%9B%B6%E6%8B%B7%E8%B4%9D%E6%8A%80%E6%9C%AF.html

@wathenjiang
Copy link
Owner Author

您好 您这个系列的 Mysql 的零拷贝技术好像看不到了

你能给出一个 URL 吗?我看看问题出在哪里。

https://spongecaptain.cool/zerocopyofmysql

也就是这一篇文章的底部的引用的第 8 个文章 https://spongecaptain.cool/SimpleClearFileIO/2.%20DMA%20%E4%B8%8E%E9%9B%B6%E6%8B%B7%E8%B4%9D%E6%8A%80%E6%9C%AF.html

谢谢。
已经修正。
页面刷新后应该能路由至正确的文章。

@jingxuantju
Copy link

您好 您这个系列的 Mysql 的零拷贝技术好像看不到了

你能给出一个 URL 吗?我看看问题出在哪里。

https://spongecaptain.cool/zerocopyofmysql
也就是这一篇文章的底部的引用的第 8 个文章 https://spongecaptain.cool/SimpleClearFileIO/2.%20DMA%20%E4%B8%8E%E9%9B%B6%E6%8B%B7%E8%B4%9D%E6%8A%80%E6%9C%AF.html

谢谢。 已经修正。 页面刷新后应该能路由至正确的文章。

您客气了。我阅读了您大部分博客的文章,感觉质量都很高,很深入,希望您多更新,也希望您有空可以介绍一下自己的学习方式。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

2 participants