在Go語言中, package中包含函數與變量經過identifier的首字母是否大寫來決定它是否能夠被其它package所訪問。當一個函數或變量名稱爲小寫字母時,默認是沒法被其餘package引用的.
有沒有辦法突破這個限制呢?
實際上在go官方文檔中已有說明, 這須要用到一個編譯器指令 安全
//go:linkname localname importpath.name ide
官方的解釋是: 模塊化
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的變量或函數的對象文件符號名。由於這個指令能夠破壞類型系統和包的模塊化,因此它只在導入「不安全」的文件中啓用。ui
個人理解就是使用localname做爲當前package本地函數或變量名稱,關聯到importpath.name實際的符號引用.實際上locaname只是一個做爲連接符號,這個跟WINDOWS中的DLL符號關聯有點相似.this
示例
如下示例引用了runtime.sysMmap一系列私有函數與變量,用於建立一個huagepage共享內存區域
code
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)) }