欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Go语言共享内存读写实例分析

程序员文章站 2022-04-29 09:13:25
本文实例分析了go语言共享内存读写的方法。分享给大家供大家参考。具体分析如下: 前面分析了go语言指针运算和内嵌c代码的方法,做了一个go语言共享内存读写的实验。 先大...

本文实例分析了go语言共享内存读写的方法。分享给大家供大家参考。具体分析如下:

前面分析了go语言指针运算和内嵌c代码的方法,做了一个go语言共享内存读写的实验。

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

共享内存用到的是系统提供的mmap函数,它可以将一个文件映射到虚拟内存的一个区域中,程序使用指针引用这个区域,对这个内存区域的操作会被回写到文件上,go内置的syscall包中有mmap函数,但是它是经过封装的,返回的是[]byte,没办法做我需求的指针运算,所以我还是用cgo来调用原生的mmap。

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

下面是shm_writer.go的代码:

复制代码 代码如下:
package main
/*
#cgo linux ldflags: -lrt
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#define file_mode (s_irusr | s_iwusr | s_irgrp | s_iroth)
int my_shm_new(char *name) {
    shm_unlink(name);
    return shm_open(name, o_rdwr|o_creat|o_excl, file_mode);
}
*/
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
/*
#cgo linux ldflags: -lrt
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#define file_mode (s_irusr | s_iwusr | s_irgrp | s_iroth)
int my_shm_open(char *name) {
    return shm_open(name, o_rdwr);
}
*/
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)
}

上面的程序映射了一块4g的虚拟内存,用来证明mmap没有实际占用4g内存,而是用到了虚拟内存。

shm_writer创建好共享内存以后,往内存区域写入了一个结构体,shm_reader则读出一个结构体。

内嵌的c代码中有一行 :

复制代码 代码如下:
#cgo linux ldflags: -lrt

因为mmap在mac上不需要连接librt,在linux上则需要,所以做了一个条件链接,这是cgo提供的功能。

上面代码中还用到一个cgo的技巧,像shm_open和mmap函数在错误时会返回errno,如果我们在go中使用多返回值语法,cgo会自己把错误码转换成错误信息,很方便的功能。

希望本文所述对大家的go语言程序设计有所帮助。