1. 程式人生 > >一個golang項目筆記 (一)

一個golang項目筆記 (一)

linu courier hit 匯編語言 println int32 ack 編譯器 cal

最近想認真學習一下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[:])) } }

一個golang項目筆記 (一)