匯出私有函式與私有變數
在Go語言中, package中包含函式與變數通過identifier的首字母是否大寫來決定它是否可以被其它package所訪問。當一個函式或變數名稱為小寫字母時,預設是無法被其他package引用的.
有沒有辦法突破這個限制呢?
實際上在go官方文件中已有說明, 這需要用到一個編譯器指令
//go:linkname localname importpath.name
官方的解釋是:
The //go:linkname directive instructs the compiler to use “importpath.name” as the object file symbol name for the variable or function declared as “localname” in the source code. Because this directive can subvert the type system and package modularity, it is only enabled in files that have imported "unsafe".
//go:linkname指令指示編譯器使用importpath.name作為原始碼中宣告為localname的變數或函式的物件檔案符號名。因為這個指令可以破壞型別系統和包的模組化,所以它只在匯入“不安全”的檔案中啟用。
我的理解就是使用localname作為當前package本地函式或變數名稱,關聯到importpath.name實際的符號引用.實際上locaname只是一個作為連結符號,這個跟WINDOWS中的DLL符號關聯有點類似.
示例
以下示例引用了runtime.sysMmap一系列私有函式與變數,用於建立一個huagepage共享記憶體區域
package main /* #cgo CFLAGS: #include <sys/types.h> #include <sys/mman.h> #include <sys/signal.h> #include <sys/time.h> #include <sys/ucontext.h> #include <sys/unistd.h> #include <errno.h> #include <signal.h> */ import "C" import ( "fmt" "os" "encoding/hex" "syscall" "reflect" _ "runtime" "unsafe" "os/exec" ) const ( PROT_NONE = C.PROT_NONE PROT_READ = C.PROT_READ PROT_WRITE = C.PROT_WRITE PROT_EXEC = C.PROT_EXEC MAP_SHARED = 0x1 MAP_ANON = C.MAP_ANONYMOUS MAP_PRIVATE = C.MAP_PRIVATE MAP_FIXED = C.MAP_FIXED ) //go:linkname physPageSize runtime.physPageSize //go:linkname callCgoMmap runtime.callCgoMmap //go:linkname callCgoMunmap runtime.callCgoMunmap //go:linkname sysMmap runtime.sysMmap //go:linkname sysMunmap runtime.sysMunmap //go:linkname Mmap runtime.mmap //go:linkname Munmap runtime.munmap // sysMmap calls the mmap system call. It is implemented in assembly. func sysMmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (p unsafe.Pointer, err int) // callCgoMmap calls the mmap function in the runtime/cgo package // using the GCC calling convention. It is implemented in assembly. func callCgoMmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) uintptr // sysMunmap calls the munmap system call. It is implemented in assembly. func sysMunmap(addr unsafe.Pointer, n uintptr) // callCgoMunmap calls the munmap function in the runtime/cgo package // using the GCC calling convention. It is implemented in assembly. func callCgoMunmap(addr unsafe.Pointer, n uintptr) func Mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (p unsafe.Pointer, err int) func Munmap(addr unsafe.Pointer, n uintptr) var physPageSize uintptr const ( ENOMEM = 12 ) func GetPhysPageSize() uintptr { return physPageSize } func TestHugePage() { file := "/dev/hugepages/hp" f, err := os.OpenFile(file, os.O_CREATE|os.O_RDWR, 0644) if err != nil { fmt.Printf("Open: %v", err) return } var length uintptr = 2 * 1024 * 1024; var flags int32 = MAP_SHARED; var proto int32 = PROT_READ|PROT_WRITE; addr, e := sysMmap(nil, length, proto, flags, int32(f.Fd()), 0) if e != 0 { fmt.Printf("Mmap occur error %d\n", e); return } fmt.Printf("Mmap %p\n", addr); var x reflect.SliceHeader x.Len = 512 x.Cap = 512 x.Data = uintptr(addr) var payload []byte = *(*[]byte)(unsafe.Pointer(&x)) stdoutDumper := hex.Dumper(os.Stdout) defer stdoutDumper.Close() stdoutDumper.Write(payload) // Munmap(addr, length) } func main() { var pid int pid = syscall.Getpid() fmt.Println("GetPhysPageSize:", GetPhysPageSize()) TestHugePage() cmd := exec.Command("cat", str) out, err := cmd.CombinedOutput() if err != nil { fmt.Printf("cmd.Run() failed with %s\n", err) return } fmt.Printf("combined out:\n%s\n", string(out)) }