本文執行個體分析了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語言程式設計有所協助。