This is a creation in Article, where the information may have evolved or changed.
The following three data structures are used to implement the scheduling algorithm in Golang, it is these three structures plus some algorithms to form the Golang scheduling algorithm, of course, these data structures are constantly evolving, and the future will be added to other structures to improve the performance of the scheduler.
The main data structure of the co-process scheduling
which
m: On behalf of the operating system thread, which is the thread we often understand, is a unit that really participates in OS scheduling, and each goroutine can be executed only if it is attached to an M-side;
G: representative of the process, that is, we often use the goroutine;
P: The basic dispatch unit of the co-scheduler, the Shelter of G, the bridge connecting G and M.
The key to understanding the scheduler is to understand the role of P: it is a key structure to implement M:N scheduling. Introduced in the Golang1.1 release, resolves the scalability issues caused by the global scheduling queue in 1.0.
The relationship between the three data structures is as follows:
- Each g must be attached to a certain p;
- When each m wants to run, it must first obtain a p, and then execute the G in P;
- Each P maintenance can run the G queue, avoiding the problem of lock competition caused by global G queue, and improving the scalability of the scheduler;
- If an M enters the syscall when it executes G, then the M is occupied, and the p associated with it is then free, and can be obtained by the same idle m, and G in P will be given the opportunity to continue executing;
- The number of P can be set by Gomaxprocs () in go, which is generally recommended as the number of system cores, and the number of P also represents the current active m number (due to entering syscall and block m not counted).
struct G { ...... m *m}struct M { ...... G* curg; // current running goroutine P* p; // attached P for executing Go code (nil if not executing Go code) ......}struct P { ...... M* m; // back-link to associated M (nil if idle) ...... // Queue of runnable goroutines. uint32 runqhead; uint32 runqtail; G* runq[256]; // Available G's (status == Gdead) G* gfree; ......};
Read the data structure above to find out the relationship between the three of them:
- G: There is a pointer to the m that executes it, i.e. G is subordinate to m;
- M: Save the currently executing G, but also point to P, that is, M belongs to P;
- P: Saves the M that is currently executing the G in P, along with the list of the G that belongs to it