最近想认真学习一下golang, 最好的学习方法当然是做一个项目咯.。这个项目服务端用GO编写,前端打算用VUE来搞。项目的功能呢,大致就是go能定期通过爬虫获取一些网络数据, 然后通过http接口vue展示数据。 

一开始,想要解决的问题是,想通过共享内存的方式进行各子进程间的通信,因为这种方式最快。因为之前C++有用过 CreateFileMapping和 OpenFileMapping,所以也研究了一下GO有没有相关的库。发现有个syscall库有一些系统调用相关函数,syscall库的相关介绍如下:

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[:]))

    }

}