This is a creation in Article, where the information may have evolved or changed.
At the beginning of last year, I wrote an article about how to get Goroutine ID: How to get Goroutine ID? At that time, some methods of acquiring Goid were investigated. There are three basic methods:
- Parse out ID with stack information
- Get
runtime·getg
The call result of a method by assembly
- Direct modification of the runtime code, export a Goid () method that can be called externally
There are some problems in every way, #1比较慢, #2因为是hack的方式 (go team does not want to expose the information of Go ID), for different go version need special hack means, #3需要定制Go运行时, not universal. At the time, Petermattis/goid provided #2 method, but only in go 1.3, so you can only select # # to get the go ID.
Over the past year, Petermattis has updated his code to add support for Go 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, as well as the # # method, as an alternative when the # # method doesn't work. So we can use stable's method of getting go ID in all the current versions.
You may encounter scenarios where you need to use Go ID, for example, when multiple goroutine are running for a long time, we track the execution of the task through the log, and the Go ID can be used to roughly track the state of the program's concurrency execution.
12345678910111213141516171819202122 |
Package mainimport ("Log""Time""github.com/petermattis/goid") Func Main () { for i: = 0; i < ; i++ {gofunc() { J: = 0; J < 1000000; J + + {log. Printf ("[#%d]%d", goid. Get (), j) time. Sleep(10e9)}} ()}select {}} |
According to the document hacking in the Go Code, a method is implemented in the Go runtime to getg()
get the current goroutine:
getg()
Alone returns the currentg
Of course, this method is an internal method, not a exported, cannot be called externally, and the data structure returned is not exported. If there is a way to expose this method, the problem is solved.
Petermattis/goid Imitation runtime.getg
exposes a getg
method
Https://github.com/petermattis/goid/blob/master/goid_go1.5plus.s
12345678910 |
+build amd64 amd64p32//+build go1. 5"textflag.h"//Func GETG () uintptrtext GETG (SB),nosplit
,$8movqbxmovqbxret+0(FP )RET |
The above code actually returns the pointer (TLS) of the structure of the current goroutine.
Reference: Golang Internals and Chinese translation Go Language Insider
TLS is actually the abbreviation for thread local storage (thread locally Storage). This technique is useful in many programming languages (refer to here). Simply put, it provides one such variable for each thread, with different variables used to point to different areas of memory.
In the Go language, TLS stores a pointer to a G struct. The structure that the pointer points to includes the internal details of the Go routine (which is discussed in more detail later). Therefore, when you access the variable in a different routine, you actually access the struct to which the corresponding variable of the routine is pointing. The linker knows where the variable is located, and the previous instruction moves to the CX register, which is the variable. For AMD64,TLS is implemented with FS registers, where the commands we see earlier can actually be translated into Movq FS, CX.
Different go versions of the data structure may be different, so petermattis/goid
for 1.5, 1.6, 1.9 has a changed version of the different data structure, because we just need to get Goroutine ID, so just implement:
1234 |
func Int64 {GG: = (*g) (unsafe. Pointer (GETG ()))return gg.goid} |
I compare the performance of the two implementations of # # and # #, and the gap is very large:
123456789101112131415161718192021222324 |
Package pkgimport ("Runtime""testing""github.com/petermattis/goid") func Benchmarkasm (b *testing. B) {B.reportallocs () for i: = 0; i < B.N; i++ {goid. Get ()}}func benchmarkslow (b *testing. B) {B.reportallocs ()var[+]byteb.resettimer () for i: = 0 false)]}} |
Performance Comparison results:
12 |
BenchmarkASM-4 300000000 3.70 ns/op 0 b/op 0 allocs/opbenchmarkslow-4 300000 4071 ns/op 1 b/op 1 allocs/op |
1000 times the gap.
petermattis/goid
This hack approach exposes more details of the runtime, such as the ability to expand, get m
information about which is currently running, and even get the current thread:
1234567891011121314151617181920212223 |
type m struct {G0 * Gmorebuf gobufdivmod uint32 procid uint64 gsignal *gsi Gmask sigsettls [6 ]uintptr mstartfn func () Curg *gcaughtsig uintptr P uintptr N EXTP uintptr ID int32 }func Getm () int32 {gg: = (*g) (unsafe. Pointer (GETG ())) M: = (*m) (unsafe. Pointer (GG.M)) return m.id} |
Sigset
is not the same size on different platforms, you can refer to the definitions of each platform in Os_*.go. The above is the ID of the m
, and the structure of the more complete m
defines the sea as including thread
.