This is a creation in Article, where the information may have evolved or changed.
Goroutine scheduling mechanism of Golang
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Directory (?) [-]
- has been on the Goroutine scheduling mechanism very curious recently in the rain Mark Golang Source analysis based on GO14
- This article was compiled last year on the record company internal Wiki
has been on the Goroutine scheduling mechanism is very curious, recently in the rain Mark Golang source Analysis, (based on go1.4)
Feeling enlightened, benefited greatly;
To the complexity of Jane, coupled with some of their own understanding, organized a bit
~~
Scheduler
Mainly based on three basic objects, g,m,p (defined in the source code of the Src/runtime/runtime.h file)
1. g represents a Goroutine object that creates a G object each time the go is called
2. m represents a thread, and each time a m is created, a bottom thread is created, and all G-tasks are eventually executed on M
3. P represents a processor, each running m must bind a p, as if the thread must be executed on a CPU core.
The number of P is gomaxprocs (maximum 256), fixed at startup, generally not modified, m number and p number is not necessarily the same (there will be dormant m or do not need too much m) (Max 10000); Each p holds a local G task queue and a global G task queue;
As shown
The Global G task queue will be exchanged with each local G task queue according to a certain policy (full, then half of the local queue is sent to the global queue)
P is saved with a global array (255) and maintains a global P idle list
each time the go is called, it will:
1. Create a G object, join to local queue or global queue
2. If there is a free p, create a M
3. M launches a bottom-level thread that loops through the G tasks that can be found
4. G Task execution order is, first from the local queue to find, not local from the global queue (one-time transfer (global G number/P number), and then to other p (half-time transfer),
5. The above G-Task execution is performed in order of queue (that is, the order of Go calls). (Does this place feel strange??) )
For the 第2-3 step above, create a M, whose process:
1. First find a free p, if not the direct return, (haha, this place will ensure that the process will not occupy more than the number of CPU set by itself)
2. Call the system API to create a thread, different operating systems, the call is not the same, in fact, and the C language creation process is consistent, (Windows uses the Createthread,linux is the clone system call), (*^__^*) hehe ...
3. Then create the thread inside is really doing, loop to perform G task
There is a problem, if a system call or the G task is too long, he will always occupy this thread, because the local queue of the G task is executed sequentially, the other G task will be blocked, how to abort the long task? (This place I have been looking for a long time ~o (╯-╰) o)
Such drops, when launched, will be dedicated to creating a thread Sysmon, which is used to monitor and manage in-house is a loop:
1. Log all P's G task Count Schedtick (Schedtick will increment after each G-task performed)
2. If the check to Schedtick has not been incremented, indicating that the P has been performing the same G task, if more than a certain amount of time (10ms), in this G-Task stack information and a tag
3. The G task is then executed, and if it encounters a non-inline function call, it checks the tag once, then interrupts itself, adds itself to the end of the queue, and executes the next G
4. O (∩_∩) o haha ~, if there is no non-inline function (sometimes the normal small function will be optimized to an inline function) call, it is miserable, will continue to carry out the G task until its own end, if it is a dead loop, and Gomaxprocs=1 words, congratulations, tamping! It's true.
For a G-task, the post-outage recovery process:
1. Save the stack information in the register to your G object when interrupted
2. When it is time to execute itself again, copy the stack information that you saved to the register so that it runs after the last time. ~\ (≧▽≦)/~
But there is also a problem, is the system startup process, rain scar did not say too clear, I have been a lot of problems are hard to doubt (the first m how to come?) , g how to find the corresponding p? And so on), this makes my egg ache for a long time ~
But I made a little bit of a fantasy, and I'm glad to add it to you.
1. When the system starts, the first running is the main thread, that the first m should be the main thread (according to the C language understanding, hehe), here is called M1, you can see the previous figure
2. The main thread then binds to the first P1
3. The main function that we write is actually performed as a goroutine (rain Scar said)
4. That is, the first P1 has a G1 task, and then the first M1 executes the G1 task (that is, the main function), creating this G1 without creating m, because there is a M1
5. All the goroutine in this main function are bound to the current M1 corresponding P1, O (∩_∩) o haha ~
6. Then create the goroutine in main (such as G2), will create a new M2, the new M2 in the initial P2 of the local task queue is empty, will take some from the P1, haha
7. So two m1,m2 each carry out their own G-task, and then back and forth, this is the perfect ~ ~ ~
Comprehensive:
So Goroutine is in accordance with preemptive scheduling, a goroutine up to 10ms will be changed to the next
This is similar to the current CPU schedule for the mainstream system (fragmented by Time)
Windows:20ms
Linux:5ms-800ms
Here are almost, these in the rain mark note inside have more detailed description, but a lot of places more messy, more complex, here screening a lot, convenient for the reader to understand
Attention:
1. In Golang, the compiler will also try to inline, copy and compile the small function directly, in order to inline, as far as possible to eliminate the compiler cannot detect the dead code, with the GOBUILD-GCFLAGS=-M compile command to see the program inline State, Have to say that Golang's compiler tool chain is still very powerful, is very advantageous to the program optimization.
If you have any questions, please ask,
Keep up to date
(This article was collated last year, recorded in the company's internal wiki ~)