最近想認真學習一下golang, 最好的學習方法當然是做一個項目咯.。這個專案服務端用GO編寫,前端打算用VUE來搞。項目的功能呢,大致就是go能定期通過爬蟲擷取一些網路資料, 然後通過http介面vue展示資料。
一開始,想要解決的問題是,想通過共用記憶體的方式進行各子進程間的通訊,因為這種方式最快。因為之前C++有用過 CreateFileMapping和 OpenFileMapping,所以也研究了一下GO有沒有相關的庫。發現有個syscall庫有一些系統調用相關函數,syscall庫的相關介紹如下:
來源:https://blog.csdn.net/erlib/article/details/50264341
Go 語言庫對Syscall的封裝我們知道Go是一門面向系統級開發的Native程式設計語言,與C/C++ 類似,Go的編譯器會直接將程式編譯、連結成本地可執行檔。理論上,它可以完成任何C/C++語言能完成的。作為支撐該特性的重要方面,Go以標準庫形式提供了syscall包,用來支援OS級系統調用。首先,Go對各種系統調用介面進行了封裝,提供給使用者一組Go語言函數,方便在程式中直接調用,如:func Read(fd int, p []byte) (n int, err error)func Write(fd int, p []byte) (n int, err error)同時,Go還通過以下函數提供了對Syscall的直接調用支援:func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)其中,帶有Raw首碼的一組動作表示直接調用syscall (註:以Linux為例,在AMD64中是通過syscall指令實現,在X86中是int 0x80非強制中斷,而ARM中則是採用SWI非強制中斷實現系統調用),而不帶Raw首碼的操作則在真正調用syscall前會先調用runtime·entersyscall,並在syscall返回後插入runtime·exitsyscall。這兩個輔助函數的功能我們在前面介紹調度器時已經說過了,後面還會再提。這4個函數全都是用組合語言實現的,並且和具體的硬體架構及OS相關,比如Linux下ARM架構的相應實現,在 src/pkg/syscall/asm_linux_arm.s中。至於其他的如Read/Write這類的函數,其內部基本上都調用上面的4個函數實現的。
果然發現syscall下面有CreateFileMapping函數,但是卻沒有OpenFileMapping, 不過網上搜了一通資料以後,只發現有人使用載入 kernel32.dll 系統DLL庫的方式,調用其中的OpenFileMapping方法,我覺得這個方法略顯麻煩並且可能以後遷移linux不好弄,so放棄這種方式。 後來發現使用CreateFileMapping函數一樣可以,只需要讀取的時候使用唯讀模式即可, 那就先這樣用著吧
使用CreateFileMapping範例程式碼如下:
服務端(寫記憶體)
import ( "bytes" "fmt" "log" "syscall" "time" "unicode/utf16" "unicode/utf8" "unsafe")
func main() { file, _ := syscall.UTF16PtrFromString("ShareMemory") size := 100000 // I’ve tried unsafe.Sizeof(MumbleData{}) but that didn’t work. handle, err := syscall.CreateFileMapping(0, nil, syscall.PAGE_READWRITE, 0, uint32(size), file) if err != nil { log.Fatal(err) } defer syscall.CloseHandle(handle) addr, err := syscall.MapViewOfFile(handle, syscall.FILE_MAP_WRITE, 0, 0, 0) if err != nil { log.Fatal(err) } var i byte = 0x30 for { data := (*Test)(unsafe.Pointer(addr)) data.str[0] = i i++ data.str[1] = i time.Sleep(1 * time.Second) // fmt.Printf("ava %v cam %v id %v\n", data.Avatar.Position, data.Camera, data.Identity) fmt.Printf("str: %s\n", string(data.str[:])) i++ }}
用戶端(讀記憶體)
func main() { file, _ := syscall.UTF16PtrFromString("ShareMemory") size := 100000 // I’ve tried unsafe.Sizeof(MumbleData{}) but that didn’t work. handle, err := syscall.CreateFileMapping(0, nil, syscall.PAGE_READONLY, 0, uint32(size), file) if err != nil { log.Fatal(err) } defer syscall.CloseHandle(handle) fmt.Println(syscall.GetLastError())
addr, err := syscall.MapViewOfFile(handle, syscall.FILE_MAP_READ, 0, 0, 0) if err != nil { log.Fatal(err) } defer syscall.UnmapViewOfFile(addr) for { data := (*Test)(unsafe.Pointer(addr))
time.Sleep(1 * time.Second) // fmt.Printf("ava %v cam %v id %v\n", data.Avatar.Position, data.Camera, data.Identity) fmt.Printf("str: %s\n", string(data.str[:])) }}