golang中syscall.Mmap()教程-對共用記憶體的操作

來源:互聯網
上載者:User

不同進程間的記憶體是互相獨立的,沒辦法直接互相操作對方內的資料,而共用記憶體則是靠作業系統提供的記憶體映射機制,讓不同進程的一塊地址空間映射到同一個虛擬記憶體地區上,使不同的進程可以操作到一塊共用的記憶體塊。共用記憶體是效率最高的進程間通訊機制,因為資料不需要在核心和程式之間複製。

共用記憶體用到的是系統提供的mmap函數,它可以將一個檔案對應到虛擬記憶體的一個地區中,程式使用指標引用這個地區,對這個記憶體地區的操作會被回寫到檔案上,其函數原型如下:

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
  • 參數fd為即將映射到進程空間的檔案描述字,一般由open()返回,同時,fd可以指定為-1,此時須指定flags參數中的MAP_ANON,表明進行的是匿名映射(不涉及具體的檔案名稱,避免了檔案的建立及開啟,很顯然只能用於具有親緣關係的處理序間通訊)。
  • len是映射到調用進程地址空間的位元組數,它從被對應檔開頭offset個位元組開始算起。
  • prot參數指定共用記憶體的存取權限。可取如下幾個值的或:PROT_READ(可讀),PROT_WRITE(可寫),PROT_EXEC(可執行),PROT_NONE(不可訪問)。
  • flags由以下幾個常值指定:MAP_SHARED, MAP_PRIVATE, MAP_FIXED。其中,MAP_SHARED,MAP_PRIVATE必選其一,而MAP_FIXED則不推薦使用。 如果指定為MAP_SHARED,則對映射的記憶體所做的修改同樣影響到檔案。如果是MAP_PRIVATE,則對映射的記憶體所做的修改僅對該進程可見,對檔案沒有影響。
  • offset參數一般設為0,表示從檔案頭開始映射。
  • 參數addr指定檔案應被映射到進程空間的起始地址,一般被指定一個null 指標,此時選擇起始地址的任務留給核心來完成。函數的傳回值為最後檔案對應到進程空間的地址,進程可直接操作起始地址為該值的有效地址。

順帶介紹一下shm_open和shm_unlink兩個函數:

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調用取得成功。

可以參考此文章的介紹來進一步瞭解mmap等函數:http://www.cnblogs.com/polestar/archive/2012/04/23/2466022.html

可以利用golang調用cgo的方法實現c中的mmap。實驗分為讀和寫兩個程式,這樣我們可以觀察到讀進程可以讀到寫進程寫入共用記憶體的資訊。

shm_writer.go程式碼範例:

<pre class="brush:cpp; toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; display: block; padding: 10px; margin: 0px 0px 10.5px; line-height: 1.42857; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">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 = 100data.Col2 = 876data.Col3 = 8021

}</pre>

shm_reader.go程式碼範例:

<pre class="brush:cpp; toolbar: true; auto-links: false;" style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; display: block; padding: 10px; margin: 0px 0px 10.5px; line-height: 1.42857; word-break: break-all; word-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">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, 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_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)

}</pre>

上面的程式映射了一塊4G的虛擬記憶體,用來證明mmap沒有實際佔用4G記憶體,而是用到了虛擬記憶體。shm_writer建立好共用記憶體以後,往記憶體地區寫入了一個結構體,shm_reader則讀出一個結構體。

上面代碼中還用到一個cgo的技巧,像shm_open和mmap函數在錯誤時會返回errno,如果我們在go中使用多傳回值文法,cgo會自己把錯誤碼轉換成錯誤資訊,很方便的功能。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.