mmap 原理图

不同进程见的内存是互相独立的,没办法直接互相操作对方内的数据,而 共享内存 则是靠操作系统提供的内存映射机制,让不同进程的一块地址空间映射到同一个虚拟内存区域上,使不同的进程可以操作到一块共用的内存块。共享内存是效率最高的进程间通讯机制,因为数据不需要在内核和程序之间复制。

共享内存用到的是系统提供的mmap函数,mmap是将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。

mmap最大的一个好处是操作系统会自己将磁盘上的文件映射到内存,当内存足够的时候,操作文件就像操作内存一样快,而当内存不足的时候,操作系统又会自己将一些页从内存中去掉,实现了一个类似缓存的东西。特别适合于对于巨大文件的读操作.一个巨大的文件mmap之后,文件读写操作的性能由系统内存决定,系统可用内存越大,那么读写文件的性能越好,因为操作系统的内存足够,系统会将更多的文件载入到内存,提高系统吞吐量。

在Go语言中,对应的MMAP调用是:(需要引入Syscall包)

func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error)

参数:文件描述符,偏移量,需要映射的长度,期望的内存保护标志【只读/只写/读写】,映射方式【是否同步到文件,还是只是副本修改等】

在utils.mmap中,提供一些基础的方法:

func NewMmap(file_name string, mode int) (*Mmap, error) 新建一个mmap

func (this *Mmap) ReadInt64(start int64) int64 //从指定位置读取一个int64的值

func (this *Mmap) WriteInt64(start, value int64) error //在指定位置写入一个int64的值

func (this *Mmap) ReadDocIdsArry(start, len uint64) []DocIdNode //从指定位置读取一个docid的链

介绍函数:

shm_open()函数

功能: 打开或创建一个共享内存区

头文件 : # include <sys/mman.h>

函数原形: int shm_open(const char *name,int oflag,mode_t mode);

返回值: 成功返回0,出错返回-1

参数:

name 共享内存区的名字

oflag 标志位

mode 权限位

参数解释:oflag参数必须含有O_RDONLY和O_RDWR标志,还可以指定如下标志:O_CREAT,O_EXCL或O_TRUNC.mode参数指定权限位,

它指定O_CREAT标志的前提下使用。shm_open的返回值是一个整数描述字,它随后用作mmap的第五个参数。

shm_unlink()函数

功能: 删除一个共享内存区

头文件: #include <sys/mman.h>

函数原形: int shm_unlink(const char *name);

参数: name 共享内存区的名字

返回值: 成功返回0,出错返回-1

shm_unlink函数删除一个共享内存区对象的名字,删除一个名字仅仅防止后续的open,mq_open或sem_open调用取得成功。

示例:分为读和写两个程序,这样我们可以观察到读进程可以读到写进程写入共享内存的信息

shm_writer.go代码示例:

package main

import “C”

import (

“fmt”

“unsafe”

)

const SHM_NAME = “my_shm”

const SHM_SIZE = 4 * 1000 * 1000 * 1000

type MyData struct {

Col1 int

Col2 int

Col3 int

}

func main() {

fd, err := C.my_shm_new(C.CString(SHM_NAME))

if err != nil {

fmt.Println(err)

return

}

C.ftruncate(fd, SHM_SIZE)

ptr, err := C.mmap(nil, SHM_SIZE, C.PROT_READ|C.PROT_WRITE, C.MAP_SHARED, fd, 0)

if err != nil {

fmt.Println(err)

return

}

C. close (fd)

data := (*MyData)(unsafe.Pointer(ptr))

data.Col1 = 100

data.Col2 = 876

data.Col3 = 8021

}

shm_reader.go代码示例:

package main

import “C”

import (

“fmt”

“unsafe”

)

const SHM_NAME = “my_shm”

const SHM_SIZE = 4 * 1000 * 1000 * 1000

type MyData struct {

Col1 int

Col2 int

Col3 int

}

func main() {

fd, err := C.my_shm_open(C.CString(SHM_NAME))

if err != nil {

fmt.Println(err)

return

}

ptr, err := C.mmap(nil, SHM_SIZE, C.PROT_READ|C.PROT_WRITE, C.MAP_SHARED, fd, 0)

if err != nil {

fmt.Println(err)

return

}

C.close(fd)

data := (*MyData)(unsafe.Pointer(ptr))

fmt.Println(data)

}

更多内容请关注每日编程,每天进步一点。