Как идти php э, я считаю, что вы правы насчет паники
и восстановления
Конечно, не странно, но вы когда-нибудь думали об этом? Когда мы выполняем эти два утверждения. Что случилось внизу? Несколько дней назад я просто поболтал со своими коллегами на соответствующие темы и обнаружил, что на самом деле понимание этого у всех все еще расплывчатое. Я надеюсь, что эта статья сможет рассказать вам, почему и что она сделала более подробно.
Первоначальный адрес: Глубокое понимание Паники и восстановления
Отражение
1. Почему операция приостановлена?
func main() { panic("EDDYCJY.") }
Выходные результаты:
$ go run main.go panic: EDDYCJY. goroutine 1 [running]: main.main() /Users/eddycjy/go/src/github.com/EDDYCJY/awesomeProject/main.go:4 +0x39 exit status 2
Подумайте о том, почему. паника
Приведет ли это к остановке работы приложения? (не просто казнь) паника
Так что на этом двусмысленности конец.
2. Почему операция не будет приостановлена?
func main() { defer func() { if err := recover(); err != nil { log.Printf("recover: %v", err) } }() panic("EDDYCJY.") }
Выходные результаты:
$ go run main.go 2019/05/11 23:39:47 recover: EDDYCJY.
Подумайте о том, почему. отложить
+ восстановить
Могут ли комбинации защитить приложения?
3. Не откладывайте, нет
Второй вопрос: отложить
+ восстановить
Комбинацию, тогда я избавлюсь от нее. отличается
Все в порядке? Следующим образом:
func main() { if err := recover(); err != nil { log.Printf("recover: %v", err) } panic("EDDYCJY.") }
Выходные результаты:
$ go run main.go panic: EDDYCJY. goroutine 1 [running]: main.main() /Users/eddycjy/go/src/github.com/EDDYCJY/awesomeProject/main.go:10 +0xa1 exit status 2
Не совсем, ах, в конце концов, все вводные курсы написаны. отложить
+ восстановить
Комбинация “всемогущий” захват. Но почему? Удалить отложить
Тогда почему мы не можем его захватить?
Подумайте, почему вам нужно настроить его отложить
после восстановления
Может ли он работать?
В то же время вам нужно хорошенько подумать об этом. Давайте все устроим. отложить
+ восстановить
Можете ли вы быть беззаботным после комбинации? Вы написали все виды “хаоса”?
4. Почему мы не можем запустить гороутину?
func main() { go func() { defer func() { if err := recover(); err != nil { log.Printf("recover: %v", err) } }() }() panic("EDDYCJY.") }
Выходные результаты:
$ go run main.go panic: EDDYCJY. goroutine 1 [running]: main.main() /Users/eddycjy/go/src/github.com/EDDYCJY/awesomeProject/main.go:14 +0x51 exit status 2
Подумайте о том, почему у нас появился новый. Goroutine
Разве вы не можете перехватывать исключения? Что случилось?
Исходный код
Затем мы начнем анализировать и анализировать исходный код с помощью вышеуказанных 4 + 1 небольших вопросов для размышлений, пытаясь найти ответы на вопросы для размышлений и многое другое, почему, прочитав исходный код.
структура данных
type _panic struct { argp unsafe.Pointer arg interface{} link *_panic recovered bool aborted bool }
останься паника
В использовании _паник
Как его основная единица, он выполняется каждый раз. паника
Заявление, создаст _panic
. Оно содержит некоторые основные поля для хранения текущего паника
В случае вызова используются следующие поля:
- Argp: указание
отложить
Указатель на параметры для отложенных вызовов - арг:
паника
Причина в звонке.паника
Параметры, прошедшие через время - Ссылка: ссылка на последний звонок
_panic
- восстановлено:
паника
Было ли оно обработано, то есть было ли оно обработано?восстановление
- прервано:
паника
Это приостановлено?
Кроме того, просмотрев ссылку
Поля, вы можете увидеть, что это связанный список структуры данных, как показано ниже:
Паника
func main() { panic("EDDYCJY.") }
Выходные результаты:
$ go run main.go panic: EDDYCJY. goroutine 1 [running]: main.main() /Users/eddycjy/go/src/github.com/EDDYCJY/awesomeProject/main.go:4 +0x39 exit status 2
Давайте вернемся и проверим. паника
Где иметь дело с конкретной логикой, заключается в следующем:
$ go tool compile -S main.go "".main STEXT size=66 args=0x0 locals=0x18 0x0000 00000 (main.go:23) TEXT "".main(SB), ABIInternal, $24-0 0x0000 00000 (main.go:23) MOVQ (TLS), CX 0x0009 00009 (main.go:23) CMPQ SP, 16(CX) ... 0x002f 00047 (main.go:24) PCDATA $2, $0 0x002f 00047 (main.go:24) MOVQ AX, 8(SP) 0x0034 00052 (main.go:24) CALL runtime.gopanic(SB)
Очевидно, что ассемблерный код указывает непосредственно на внутреннюю реализацию runtime.go panic
Давайте посмотрим, что делает этот метод, следующим образом (опущенная часть):
func gopanic(e interface{}) { gp := getg() ... var p _panic p.arg = e p.link = gp._panic gp._panic = (*_panic)(noescape(unsafe.Pointer(&p))) for { d := gp._defer if d == nil { break } // defer... ... d._panic = (*_panic)(noescape(unsafe.Pointer(&p))) p.argp = unsafe.Pointer(getargp(0)) reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz)) p.argp = nil // recover... if p.recovered { ... mcall(recovery) throw("recovery failed") // mcall should not return } } preprintpanics(gp._panic) fatalpanic(gp._panic) // should not return *(*int)(nil) = 0 // not reached }
- Получите указание на текущую
Подпрограмму
Руководящие принципы - Инициализировать
панику
Базовые блоки_panic
Используется в качестве последующей операции - Получение текущей
Подпрограммы
Загруженной_defer
(Структура данных также является связанным списком) - Если он существует в настоящее время
отложите
Вызов, затем вызовитеreflectcall
Метод для выполнения предыдущегоотложите
Код, выполнение которого задерживается, если его необходимо запустить во время выполнениявосстановление
Вызоветgorecover
Метод - Перед закрытием используйте
предпечатную подготовку
Задействованный метод печатипаника
новости - Последний звонок
смертельная паника
Прервать заявку на самом деле означает выполнитьвыход(2)
Проведение окончательного вывода средств
Благодаря анализу выполнения приведенного выше кода мы видим, что паника
Этот метод на самом деле имеет дело с текущей ситуацией. Goroutine(g)
Тот, который на нем ._panic
Связанный список (поэтому больше ничего нельзя сделать) Goroutine
Ответ на событие аномалии, а затем, к которому он принадлежит отложить
Список ссылок и восстановить
Для обнаружения и обработки и, наконец, вызвать команду выхода для завершения приложения
Неизлечимая паника, смертельная паника
func fatalpanic(msgs *_panic) { pc := getcallerpc() sp := getcallersp() gp := getg() var docrash bool systemstack(func() { if startpanic_m() && msgs != nil { ... printpanics(msgs) } docrash = dopanic_m(gp, pc, sp) }) systemstack(func() { exit(2) }) *(*int)(nil) = 0 }
Мы видим, что этот метод будет выполнен в конце обработки исключений, и, похоже, он выполняет всю заключительную работу. На самом деле, это окончательное выполнение программы. выход
Инструкции используются для остановки выполнения, но они проходят до завершения печатные панели
Все сообщения об исключениях и параметры выводятся рекурсивно. Код выглядит следующим образом:
func printpanics(p *_panic) { if p.link != nil { printpanics(p.link) print("\t") } print("panic: ") printany(p.arg) if p.recovered { print(" [recovered]") } print("\n") }
Поэтому не предполагайте, что все исключения могут быть восстановлены
, на самом деле, как фатальная
ошибка
и время выполнения.бросок
Этого не может быть. восстановление
Да, даже oom завершает программу напрямую, и какой-нибудь удар слева даст вам его. выход(2)
Учите людей, как себя вести. Поэтому, когда вы пишете код, вам следует уделять больше внимания тому факту, что паника-это сценарий, который невозможно восстановить.
Восстановление
func main() { defer func() { if err := recover(); err != nil { log.Printf("recover: %v", err) } }() panic("EDDYCJY.") }
Выходные результаты:
$ go run main.go 2019/05/11 23:39:47 recover: EDDYCJY.
Как и ожидалось, исключение было успешно перехвачено. однако восстановление
Как восстановиться паника
Что с этим делать? Посмотрите на код сборки еще раз, как показано ниже:
$ go tool compile -S main.go "".main STEXT size=110 args=0x0 locals=0x18 0x0000 00000 (main.go:5) TEXT "".main(SB), ABIInternal, $24-0 ... 0x0024 00036 (main.go:6) LEAQ "".main.func1·f(SB), AX 0x002b 00043 (main.go:6) PCDATA $2, $0 0x002b 00043 (main.go:6) MOVQ AX, 8(SP) 0x0030 00048 (main.go:6) CALL runtime.deferproc(SB) ... 0x0050 00080 (main.go:12) CALL runtime.gopanic(SB) 0x0055 00085 (main.go:12) UNDEF 0x0057 00087 (main.go:6) XCHGL AX, AX 0x0058 00088 (main.go:6) CALL runtime.deferreturn(SB) ... 0x0022 00034 (main.go:7) MOVQ AX, (SP) 0x0026 00038 (main.go:7) CALL runtime.gorecover(SB) 0x002b 00043 (main.go:7) PCDATA $2, $1 0x002b 00043 (main.go:7) MOVQ 16(SP), AX 0x0030 00048 (main.go:7) MOVQ 8(SP), CX ... 0x0056 00086 (main.go:8) LEAQ go.string."recover: %v"(SB), AX ... 0x0086 00134 (main.go:8) CALL log.Printf(SB) ...
Анализируя базовые вызовы, мы видим, что основные методы заключаются в следующем:
- Анализируя базовые вызовы, мы видим, что основные методы заключаются в следующем:
- Анализируя базовые вызовы, мы видим, что основные методы заключаются в следующем:
- Анализируя базовые вызовы, мы видим, что основные методы заключаются в следующем:
- Анализируя базовые вызовы, мы видим, что основные методы заключаются в следующем:
В последнем разделе мы описали простой процесс. перейти в панику
Метод вызывает текущую Подпрограмму
Понизить отложить
Список ссылок, если reflectcall
Обнаружен при выполнении восстановление
Он будет называться gorecover
Код этого метода выглядит следующим образом:
func gorecover(argp uintptr) interface{} { gp := getg() p := gp._panic if p != nil && !p.recovered && argp == uintptr(p.argp) { p.recovered = true return p.arg } return nil }
Этот код, кажется, очень прост, суть в том, чтобы его модифицировать. восстановленные
Поля. Это поле используется для определения текущей паники
Была ли она восстановлена
Обработана. Но это не то же самое, что мы себе представляли. Как работает программа? паника
А как насчет тех, кто возвращается? Рассматривался ли он в рамках основного подхода? Давайте посмотрим еще раз. начинай паниковать
Код выглядит следующим образом:
func gopanic(e interface{}) { ... for { // defer... ... pc := d.pc sp := unsafe.Pointer(d.sp) // must be pointer so it gets adjusted during stack copy freedefer(d) // recover... if p.recovered { atomic.Xadd(&runningPanicDefers, -1) gp._panic = p.link for gp._panic != nil && gp._panic.aborted { gp._panic = gp._panic.link } if gp._panic == nil { gp.sig = 0 } gp.sigcode0 = uintptr(sp) gp.sigcode1 = pc mcall(recovery) throw("recovery failed") } } ... }
Давай вернемся давай паниковать
Более пристальный взгляд на метод показывает, что он на самом деле содержит пару восстановления
Обработки кода для потока. Процесс восстановления выглядит следующим образом:
- Судя по настоящему
_panic
Носительвосстановлен
Был ли он помечен как обработанный? - из
_panic
Удалить аннотированные завершенные элементы из спискапаника
События, т. е. удаления, которые были восстановленыпаника
Событие - Передайте информацию о кадре стека, которую необходимо восстановить, в
восстановление
Методическиеgp
Параметры (каждый кадр стека соответствует незавершенной функции. Обратный адрес и локальные переменные функции сохраняются во фрейме стека. - осуществить
восстановление
Провести восстановительные действия
С точки зрения процесса, ядром является метод восстановления
. Он берет на себя ответственность за управление ненормальным потоком. Код выглядит следующим образом:
func recovery(gp *g) { sp := gp.sigcode0 pc := gp.sigcode1 if sp != 0 && (sp < gp.stack.lo || gp.stack.hi < sp) { print("recover: ", hex(sp), " not in [", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n") throw("bad recovery") } gp.sched.sp = sp gp.sched.pc = pc gp.sched.lr = 0 gp.sched.ret = 1 gogo(&gp.sched) }
На первый взгляд кажется, что некоторые ценности были установлены очень просто. Но на самом деле задается значение псевдорегистратора в компиляторе, которое часто используется для поддержания контекста и так далее. Здесь нам нужно объединить гопанический
Метод наблюдения Вместе восстановление
Метод. Указатель стека, который он использует sp
И счетчик программ pc
По настоящему отложить
В процессе вызова отложить
Это передается по наследству, так что на самом деле это наконец-то прошло. gogo
Метод отскочил назад deferproc
Метод. Кроме того, мы отмечаем, что:
gp.sched.ret = 1
В нижней части программа gp.sched.ret
Установит значение 1, то есть Не будет фактического вызова | Метода deferproc , напрямую изменяя его возвращаемое значение. Это означает, что по умолчанию он был обработан. Прямая передача
отсрочка Следующая инструкция метода идет. До сих пор управление потоком в ненормальном состоянии закончилось. Следующий шаг-идти дальше.
отложить Процесс
Чтобы проверить эту идею, мы можем взглянуть на основной метод прыжка. gogo
Код выглядит следующим образом:
// void gogo(Gobuf*) // restore state from Gobuf; longjmp TEXT runtime·gogo(SB),NOSPLIT,$8-4 MOVW buf+0(FP), R1 MOVW gobuf_g(R1), R0 BL setg<>(SB) MOVW gobuf_sp(R1), R13 // restore SP==R13 MOVW gobuf_lr(R1), LR MOVW gobuf_ret(R1), R0 MOVW gobuf_ctxt(R1), R7 MOVW $0, R11 MOVW R11, gobuf_sp(R1) // clear to help garbage collector MOVW R11, gobuf_ret(R1) MOVW R11, gobuf_lr(R1) MOVW R11, gobuf_ctxt(R1) MOVW gobuf_pc(R1), R11 CMP R11, R11 // set condition codes for == test, needed by stack split B (R11)
Посмотрев на код, вы можете увидеть, что его основная функция находится в Gobuf
Восстановление. Проще говоря, значение регистра изменяется в соответствии с ним. Goroutine(g)
Значение значения, которое много раз обсуждалось в этой статье. Гобуф
Следующим образом:
type gobuf struct { sp uintptr pc uintptr g guintptr ctxt unsafe.Pointer ret sys.Uintreg lr uintptr bp uintptr }
Разумно, на самом деле, то, что он хранит Goroutine
Некоторые вещи, которые вам нужны для переключения контекста
Расширять
const( OPANIC // panic(Left) ORECOVER // recover() ... ) ... func walkexpr(n *Node, init *Nodes) *Node { ... switch n.Op { default: Dump("walk", n) Fatalf("walkexpr: switch 1 unknown op %+S", n) case ONONAME, OINDREGSP, OEMPTY, OGETG: case OTYPE, ONAME, OLITERAL: ... case OPANIC: n = mkcall("gopanic", nil, init, n.Left) case ORECOVER: n = mkcall("gorecover", n.Type, init, nod(OADDR, nodfp, nil)) ... }
Фактически вызывая панику
и восстановление
, когда ключевые слова переводятся в соответствующий код операции на этапе компиляции, компилятор переводит их в соответствующие методы выполнения. Это не так, как ты думаешь, это один шаг за раз. Заинтересованные партнеры могут изучить его.
резюме
Эта статья в основном направлена на панику
и восстановление
Глубокий анализ исходного кода по ключевым словам, а первые 4 + 1 мыслящих вопроса-это надежда на то, что вы сможете изучить вопросы, достичь вдвое большего результата с половиной усилий.
Кроме того, эта статья и отложите
Есть определенная актуальность, поэтому нам нужны некоторые базовые знания. Если вы не поняли эту часть, когда только что прочитали ее, вы можете прочитать ее снова после изучения, чтобы углубить свое впечатление.
В конце концов, можете ли вы ответить на эти вопросы сейчас? Высказаться-это действительно понять:)