Как и почему Golang Scheduler рекурсивно запускает программы в runtime / proc.go: execute? - PullRequest
0 голосов
/ 22 октября 2019

Я пытаюсь понять, как работает планировщик Go, и что я вижу в runtime / proc.go :

  1. Функция scheduleвызывает execute для запуска программы
  2. Комментарий для execute явно говорит, что эта функция никогда не вернется. Он вызывает функцию gogo, определенную в одном из файлов сборки.
  3. Функция gogo выполняет переход к адресу первой инструкции новой программы.
  4. После того, как эта процедураПосле завершения функция schedule вызывается снова, поэтому мы возвращаемся к шагу 1.

Если мое понимание верно, то как эта схема позволяет избежать переполнения стека? Это как-то связано с «бесконечными» стеками, которые автоматически увеличивают их размер, или я что-то здесь упускаю?

1 Ответ

0 голосов
/ 23 октября 2019

Итак, я потратил некоторое время на изучение предмета и теперь могу попытаться ответить на свой вопрос. Весь жизненный цикл программы оказался немного более сложным:

  1. Новые процедуры создаются в специальной программе под названием g0, которая является своего рода главной программой потока. Любой вызов go func изменяет стек с текущей процедуры, из которой он был вызван, на g0 (это делается в proc.go:newproc).
  2. Когда создается процедура (в proc.go:newproc1), еестек (и / или программный счетчик ПК) построен так, что он выглядит так, как он был вызван функцией goexit. Это сделано для того, чтобы гарантировать, что при завершении и возврате goroutine он возвращается к goexit.
  3. Когда вызывается schedule и выбирается запуск goroutine, функция execute выполняет его (==переходит на свой адрес с помощью gogo функции сборки).
  4. После завершения процедуры она возвращает функцию goexit, реализованную в сборке.
  5. Товызов функции сборки proc.go:goexit1 (не уверен, зачем нужен этот дополнительный шаг сборки).
  6. Функция goexit1 изменяет текущий стек на g0. Это делается с помощью вызова mcall («вызов машинного потока»), который выполняет любую функцию, полученную в аргументе. В этом случае функция, переданная в mcall, имеет вид goexit0.
  7. Реализованный в сборке mcall переходит на адрес стекового фрейма (SP) g0 и выполняет CALL до goexit0.
  8. Функция goexit0 выполняется в контексте g0. Он помещает завершенную процедуру в список бесплатных программ и освобождает свой стек, если он был ранее увеличен.
  9. Затем goexit0 снова вызывает schedule, что выбирает запуск программы, поэтому мы возвращаемся кШаг 3.

Так что, похоже, здесь нет рекурсии. Сама запланированная программа никогда не вызывает schedule: это делает специальная программа g0. Я до сих пор не уверен, что все подробности я уловил, поэтому комментарии и дополнительные ответы приветствуются.

...