Cómo cadena Defer es atravesado

Comenzó a escribir artículos el año pasado, el primer capítulo es sobre el Defer, literatura comparada y el nombre del arte: "fáciles de resolver diferir la Golang la trampa tierna" , también Tucao. Debido a este artículo, a "Ir lectura de la noche" dijo uno. Pero en el momento en el plano puramente aplicación, todavía no ha saltado a la fosa código fuente Go, el artículo parecía relativamente fresca, sin fuente también una gran parte de la resolución.

Desde Cao después de escuchar "Ir lectura de la noche" Ir compilación de intercambio, así como la lectura de artículos de análisis de código fuente programador de Ir Awa Zhang, una variedad de código fuente, compilar todas partes ......

Última Europa Dios escribió "Ir GC 20 preguntas" , no hay una línea de texto de origen, leído en su conjunto hacia abajo muy divertido. Esto también hoy para tratar este tipo de enfoque, sin embargo, empezamos con un tema de inicio pequeña: Defer cómo se recorre la lista y ejecutado.

Acerca de aplazar el análisis de código fuente del artículo, hay muchos en la red. Sin embargo, pocos entienden plenamente que este tema, además de Awa Zhang.

Sabemos que algunos de los recursos con el fin de realizar operaciones de limpieza antes de salir de la función, como el cierre de archivos, la liberación de las conexiones. Será escrito en la declaración Defer múltiple en una función, la función se defered a fin "último out" en el RETque se ejecutarán antes de instrucciones.

En una cadena de llamadas a funciones, funciones múltiples en múltiples declaraciones de aplazamiento aparecen. Por ejemplo: a() -> b() -> c()en cada función en tanto declaración Defer, que aplazar instrucción crea un número correspondiente de _deferlas estructuras, las estructuras en forma de una lista enlazada de goroutinela estructura. Se ve así:

Defer colgante g

En compilador bendición, la función primera llamada aplazar la declaración deferporc, una nueva nueva _deferestructura, vinculada a la g. Por supuesto, las nuevas prioridades que aquí se toman de la actual piscina aplazar la unión de P, no llegó a ir a tomar mundial voluntad de aplazar la piscina, no hay una palabra en una nueva, muy familiarizado con la rutina.

Después de hacerlo, espera a que la ejecución del cuerpo de la función, antes de la instrucción RET (nota de devolución no antes), llama a los deferreturnacabados de función _deferRecorrido de la lista, se realizaron en todo esto la cadena deferedde función (por ejemplo, archivos, conexiones, etc. liberación final). El problema aquí es que, en deferreturnel extremo, utilizará la función jmpdeferantes de ser defered salto a la función, entonces el control se transfiere a la función definida por el usuario. Es sólo la ejecución de una función que se defered, esta cadena se defered otras funciones, cómo forzada?

La respuesta es que el control será entregado a Runtime de nuevo y llevar a cabo la función deferreturn de nuevo, a aplazar el recorrido completo de la lista. Que todo esto es cómo hacerlo?

Esta compilación del marco de pila Ir hablando. Mira una declaración de la función de montaje:

TEXT runtime·gogo(SB), NOSPLIT, $16-8

Los dos últimos números indican el tamaño del marco de pila para una función gogo 16B, es decir, una función de las variables locales y parámetros para los valores de llamada de subrutina y volver necesidad de preparar el espacio 16B de la pila; parámetros de tamaño de complemento y los valores de retorno son 8B. De hecho la declaración de función gogo como esto:

// func gogo(buf *gobuf)

Los parámetros y el tamaño del valor de retorno es a la persona que llama "ver", la persona que llama puede apilar estructura de acuerdo con esta figura: la necesidad de estar preparados para ajustar los parámetros de función y los valores de retorno.

Un escenario típico de los parámetros de la llamada función de diseño del diagrama a continuación:

Función de llamada de diseño de parámetros

Izquierda, listo para llamar a una subrutina llamada parámetros de la función y los valores de retorno, ejecutar CALLla instrucción, la dirección de retorno en la pila, el equivalente a la aplicación PUSH IP, después de que el valor de la pila de registros de BP, realiza el equivalente PUSH BP, a continuación, JMP a la función llamada.

La figura return addressrepresenta Después de la terminación de la sub-función de ejecución vuelve a la declaración de instrucciones para llamar a una subrutina para ser ejecutada en la función de capa superior, pertenece a la pila de llamadas. BP es el marco de pila de llamadas que pertenece a la función llamada.

Después de que se complete la subrutina, la ejecución RETinstrucciones: primer valor de subfunción en la parte inferior de la pila asignado a la BP se registran en la CPU, lo que la función superior de punto BP BP; a continuación, return addressasigna al registro de IP, a continuación, de nuevo a la izquierda como se muestra en SP ubicación. Corresponde a la reducción de todo el campo de las llamadas a subrutinas, como todo lo que había sucedido; entonces, la CPU continúa ejecutando la siguiente instrucción en el registro IP.

Volver a aplazar hasta, de hecho, en la construcción _deferestructura de tiempo, es necesario para salvar la función actual de la SP, fue defered puntero de función a _deferla estructura. Defered y será una función de los parámetros requeridos para copiar _defer estructura adyacente posición. La llamada final a la función se defered de las veces, esta vez con es la copia del valor, equivalente a usar una instantánea de la misma, si este parámetro no es un tipo de puntero o de referencia, se producirá un número de error inesperado.

Finalmente funciones, en función de deferreturn, éstos, se desplazan a realizar, _deferla lista será gradual "consumido" acabado.

Use un Awa Zhang artículo en el ejemplo:

package main

import "fmt"

func sum(a, b int) {
    c := a + b
    fmt.Println("sum:" , c)
}

func f(a, b int) {
    defer sum(a, b)

    fmt.Printf("a: %d, b: %d\n", a, b)
}

func main() {
    a, b := 1, 2
    f(a, b)
}

La ejecución de la ffunción, la función será finalmente entrar en deferreturn:

func deferreturn(arg0 uintptr) {
    gp := getg()
	d := gp._defer
	if d == nil {
		return
	}
	
	......
	
	switch d.siz {
	case 0:
		// Do nothing.
	case sys.PtrSize:
		*(*uintptr)(unsafe.Pointer(&arg0)) = *(*uintptr)(deferArgs(d))
	default:
		memmove(unsafe.Pointer(&arg0), deferArgs(d), uintptr(d.siz)) // 移动参数
	}
	fn := d.fn
	d.fn = nil
	gp._defer = d.link
	freedefer(d)
	
	_ = fn.fn
	jmpdefer(fn, uintptr(unsafe.Pointer(&arg0)))
}

Inevitablemente depende del código, de lo contrario es difícil hablar con claridad.

Debido a que estamos atravesando la _deferlista enlazada, así que tenemos que tener una condición de terminación:

d := gp._defer
if d == nil {
		return
}

Es decir, cuando _defer lista está vacía, terminar el recorrido. Veremos más adelante en el código, después de cada final ejecutar una función que se defered, será _defer estructura se elimina de la lista y se recuperó, por lo _defer lista se hace más corta.

switchDo frase Lane está listo para ser defered una función de a, b dos parámetros de tipo int (ejemplo de ello es la función suma) necesarios. Parámetros provienen de ella? De _defer estructura adyacente a la ubicación, recuerde, esta es la función de copia en el pasado en deferproc. deferArgs(d)La vuelta se copia a continuación, la dirección de destino. Y ahora que vaya en la que desea copiar? La respuesta unsafe.Pointer(&arg0)es: . Sabemos, arg0 es la función de parámetros deferreturn, sabemos que en la compilación Go, una función de los parámetros es por su función de llamada de preparar. Así dirección arg0 es en realidad puso en la función de parámetro de localización pila de su capa superior (donde f es la función).

Por último la función de jmpdefersaltar a la función suma está defered:

jmpdefer(fn, uintptr(unsafe.Pointer(&arg0)))

El núcleo es que las cosas hacen jmpdefer:

TEXT runtime·jmpdefer(SB), NOSPLIT, $0-16
    MOVQ	fv+0(FP), DX	// fn // defer 的函数的地址
    MOVQ	argp+8(FP), BX
    LEAQ	-8(BX), SP	// caller sp after CALL
    MOVQ	-8(SP), BP	// restore BP as if deferreturn returned (harmless if framepointers not in use)
    SUBQ	$5, (SP)	// return to CALL again
    MOVQ	0(DX), BX
    JMP	BX	// but first run the deferred function

En primer lugar, la función suma en registros de dirección DX, para finalmente realizado por la instrucción JMP.

MOVQ	argp+8(FP), BX
LEAQ	-8(BX), SP	// caller sp after CALL // 执行 CALL 指令后 f 函数的栈顶

Estas dos líneas en realidad ajustar el valor de la corriente en el registro SP, porque argp + 8 (FP) es en realidad la segunda jmpdefer parámetro (que en función deferreturn), la función f que puntos simplemente copia el marco de pila función de los parámetros de suma sobre. Y -8(BX)que representa el retorno a la función f llamando dirección deferreturn es en realidad la dirección de la siguiente instrucción de la función deferreturn.

A continuación, MOVQ -8(SP), BPse restablece la instrucción registro BP, BP F a punto para el marco de pila. De esta manera, SP, BP se registra de nuevo al estado antes de la llamada a la función f deferreturn: f deferreturn simplemente listo para llamar a los parámetros y el valor de retorno en la pila. Equivalente, dejando el uso deferreturn marco de pila, sin embargo, y de hecho inútil.

A continuación, SUBQ $5, (SP)la dirección de retorno se reduce 5B, exactamente la longitud de la instrucción es una llamada. ¿Qué significa esto? Después de ejecutar la función deferreturn, el flujo vuelve a la ejecución de CALL deferreturnla siguiente instrucción, este valor disminuirá 5B, que regresó a la CALL deferreturnorden, con el fin de lograr un efecto llamada de función deferreturn "recursiva". Por supuesto, la pila no estaba creciendo!

jmpdefer ejecución

jmpdefer La última función se ejecutará la función suma, parece que una función f personalmente llamar a la función suma como parámetros, valores de retorno están listos.

Hasta que la función suma ejecutado, el flujo de la ejecución salta a call deferreturnla instrucción a la función deferreturn volver a entrar, a través de toda la estructura acabada _defer, después de ejecutar todas las funciones, se desplazan, función muy completa ejecución deferretrun.

Recordemos deferreturn

A continuación, el texto completo ha terminado. Podemos ver que la clave para lograr aplazar para recorrer la lista es jmpdefer función hace algún trabajo "sombra", llama a la dirección de retorno reducido en un 5 bytes función deferreturn manera que la función se defered la aplicación de este último, regresó la CALL deferreturninstrucción que, con el fin de lograr "recursiva" invocar la función deferreturn _defer recorrido completo de la lista.

material de referencia

[Análisis] Fuente Awa Zhang Defer https://mp.weixin.qq.com/s/iEtMbRXW4yYyCG0TTW5y9g

[Awa Zhang pánico y recuperar] https://mp.weixin.qq.com/s/0JTBGHr-bV4ikLva-8ghEw

[Base] Awa Zhang Defer https://mp.weixin.qq.com/s/QmeQTONUuWlr_sRNP8b5Tw

[Análisis] compilación https://segmentfault.com/a/1190000019804120?utm_medium=referral&utm_source=tuicool

[Cao] Ir recopilatorio Compartir https://github.com/cch123/asmshare/blob/master/layout.md

[Recopilación] Go Cao https://xargin.com/plan9-assembly

[Cao Dali puedan ser escritas en ensamblador gobierno de Indonesia] https://github.com/cch123/goroutineid

Supongo que te gusta

Origin www.cnblogs.com/qcrao-2018/p/12550380.html
Recomendado
Clasificación