Golang scheduling Two: The principle of the co-process switching

Source: Internet
Author: User
Tags call back
This is a creation in Article, where the information may have evolved or changed.

Overview

The Golang is a lightweight thread in the perfectly formed, while the Golang management process is bound to involve the transition between the threads: the blocking process is switched out, and the running coprocessor is switched in. In this section, we will carefully analyze how the process is switched.

Tls

Thread Local Storage:

GETG ()

GoGet () is used to get the current thread being executed by the process G. The process g is stored in TLS.

McAll ()

McAll is called when Golang needs to perform a co-process switchover to hold information that is being switched out and to execute a new function on the G0 thread stack of the current threads. In general, schedule () is executed once in the new function to pick a new thread to run. Next we'll look at the implementation of McAll.

Call timing

System Call Back

When the thread executing the system call returns from the system call, it may be necessary to execute a new schedule, at which point the McAll may be called to complete the work, as follows:

func exitsyscall(dummy int32) {    ......    // Call the scheduler.     mcall(exitsyscall0)    ......}

In Exitsyscall0, if the current coprocessor is likely to be discarded and executed once schedule, the new thread is picked to occupy M.

Due to blocking abort execution

For some reason, the current execution of the process may be blocked, such as when the pipeline read and write conditions are not met, the current process will be blocked until the condition is satisfied.

In the Gopark () function, the mcall is called to discard the current coprocessor and perform a co-scheduling of the process.

func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason string, traceEv byte, traceskip int) {    mp := acquirem()    gp := mp.curg     status := readgstatus(gp)    if status != _Grunning && status != _Gscanrunning {        throw("gopark: bad g status")    }    mp.waitlock = lock    mp.waitunlockf = *(*unsafe.Pointer)(unsafe.Pointer(&unlockf))    gp.waitreason = reason    mp.waittraceev = traceEv    mp.waittraceskip = traceskip    releasem(mp)    // can't do anything that might move the G between Ms here.     mcall(park_m)}

The Park_m function, which we'll analyze later, discards the previously executed coprocessor and invokes a schedule () to pick a new coprocessor to execute.

Principle of execution

We mainly describe the timing of the mcall being called, and now we want to look at the implementation principle of McAll.

The function prototypes for McAll are:

func mcall(fn func(*g))

The FN parameter here refers to the process that is running before calling McAll.

As we said earlier, the primary role of McAll is the co-process switchover, which saves the currently executing coprocessor state and then m->g0 the new function on the stack of the heap. In the new function, the previously running coprocessor is discarded and a schedule () is called once to pick up the new run.

Func McAll (fn func (*g))//Switch to M->g0 ' s stack, call FN (g).  Fn must never return. It should Gogo (&g->sched)//To keep running G. TEXT runtime McAll (SB), nosplit, $0-8//di stored parameters fn movq F N+0 (FP), DI get_tls (CX)//Get the currently running coprocessor G Info//save its status in g.sched variable movq g (CX), AX/save state in G->SC Hed movq 0 (SP), BX//caller ' s PC movq BX, (G_SCHED+GOBUF_PC) (AX) Leaq fn+0 (FP), BX//caller ' s P movq BX, (g_sched+gobuf_sp) (ax) movq ax, (g_sched+gobuf_g) (ax) movq BP, (G_SCHED+GOBUF_BP) (AX)//    Switch to M->g0 & it stack, call FN movq g (CX), BX movq g_m (BX), BX movq m_g0 (BX), SI CMPQ Si, ax//if G = = M->g0 call Badmcall JNE 3 (PC) movq $runtime Badmcall (SB), ax JMP ax Movq SI, G ( CX)//G = m->g0//switch to M->G0 stack movq (g_sched+gobuf_sp) (SI), SP//SP = M->G0->SCHED.SP//Parameter Ax for the previously run Pushq Ax Movq DI, DX MOVQ 0 (di), di//Execute function on M->G0 stack fn call DI popq ax movq $runtime badmcall2 (SB), Ax JMP Axret

How to get current coprocessor execution information

The first two sentences may be more obscure to understand:

Buf+0 (FP) is actually the first parameter to get Gosave (gobuf address), reference A Quick Guide to Go ' s assembler

The
FP Pseudo-register is a virtual frame pointer used to refer to function arguments. The compilers maintain a virtual frame pointer and refer to the arguments on the stack as offsets from that Pseudo-registe R. Thus 0 (FP) is the first argument to the function, 8 (FP) was the second (on a 64-bit machine), and so on. However, when referring to a function argument this, it's necessary to place a name at the beginning, as in First_arg +0 (FP) and Second_arg+8 (FP).

Leaq buf+0 (FP), BX is to get to the first parameter of the storage address, and according to the Golang stack layout, this address is actually the caller's SP, as follows:

The next few sentences are easier to understand, and after the first sentence gets the address of the gobuf, some of the relevant members are then set to the appropriate value. The key is the following sentence

get_tls(CX)MOVQ    g(CX), BX MOVQ BX, gobuf_g(AX)

The purpose of these sentences is to get the G that the current thread is running from TLS, and then store it in the GOBUF member G.

Gosave ()

The gosave is called during the Golang process switch to hold the information that is being switched out, so that the execution context of the process can be quickly resumed the next time the process is re-dispatched.

The data structures associated with the scheduling are as follows:

type g struct {    stack       stack      stackguard0 uintptr     stackguard1 uintptr     ......    sched       gobuf    ......}// gobuf记录与协程切换相关信息 type gobuf struct {    sp   uintptr     pc   uintptr     g    guintptr    ctxt unsafe.Pointer     ret  uintreg    lr   uintptr     bp   uintptr }

Gosave is written in assembly language, the performance is relatively high, but it is not so easy to understand.

What is the call path for Todo:gosave ()?

// void gosave(Gobuf*)// save state in Gobuf; setjmp TEXT runtime·gosave(SB), NOSPLIT, $0-8     MOVQ    buf+0(FP), AX           // gobuf    LEAQ    buf+0(FP), BX           // caller's SP    MOVQ    BX, gobuf_sp(AX)    MOVQ    0(SP), BX               // caller's PC    MOVQ BX, gobuf_pc(AX)    MOVQ $0, gobuf_ret(AX)    MOVQ $0, gobuf_ctxt(AX)    MOVQ BP, gobuf_bp(AX)    get_tls(CX)    MOVQ    g(CX), BX     

The first two sentences may be more obscure to understand:

Buf+0 (FP) is actually the first parameter to get Gosave (gobuf address), reference A Quick Guide to Go ' s assembler

The
FP Pseudo-register is a virtual frame pointer used to refer to function arguments. The compilers maintain a virtual frame pointer and refer to the arguments on the stack as offsets from that Pseudo-registe R. Thus 0 (FP) is the first argument to the function, 8 (FP) was the second (on a 64-bit machine), and so on. However, when referring to a function argument this, it's necessary to place a name at the beginning, as in First_arg +0 (FP) and Second_arg+8 (FP).

Leaq buf+0 (FP), BX is to get to the first parameter of the storage address, and according to the Golang stack layout, this address is actually the caller's SP, as follows:

The next few sentences are easier to understand, and after the first sentence gets the address of the gobuf, some of the relevant members are then set to the appropriate value. The key is the following sentence

get_tls(CX)MOVQ    g(CX), BX MOVQ BX, gobuf_g(AX)

The purpose of these sentences is to get the G that the current thread is running from TLS, and then store it in the GOBUF member G.

Gogo ()

The Gogo function is the reverse, which is used to recover the execution state from the gobuf and jump to the last command to continue execution. Therefore, the code is relatively easy to understand, we will not be too much to repeat, as follows:

Gogo () Main call path: Schedule () –>execute () –>googo ()

// void gogo(Gobuf*)// restore state from Gobuf; longjmp TEXT runtime·gogo(SB), NOSPLIT, $0-8 MOVQ    buf+0(FP), BX           // gobufMOVQ    gobuf_g(BX), DX MOVQ 0(DX), CX get_tls(CX)MOVQ DX, g(CX)MOVQ    gobuf_sp(BX), SP        // restore SP MOVQ    gobuf_ret(BX), AX MOVQ    gobuf_ctxt(BX), DX MOVQ    gobuf_bp(BX), BP MOVQ $0, gobuf_sp(BX)MOVQ $0, gobuf_ret(BX)MOVQ $0, gobuf_ctxt(BX)MOVQ $0, gobuf_bp(BX)// 恢复出上一次执行指令,并跳转至该指令处MOVQ    

The last sentence here jumps to the statement that the association is dispatched to continue execution, and it is important to note that the function no longer returns the caller.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.