entersyscall/exitsyscall
Сергей принёс на разбор профиль потоков, снятый во время дисковой нагрузки. По нему стало видно, где потоки застревают в ядре и когда планировщик успевает их подхватывать.
перед системным вызовом горутина переводится в состояние _Gsyscall, а P отсоединяется от потока и помечается «в syscall»; sysmon следит за такими P и отбирает их при долгом syscall, если есть работа.
Два пути входа в syscall
Перед вызовом горутина переводится в состояние _Gsyscall, а P — тот самый логический процессор с очередью горутин — отсоединяется от M и помечается как «в syscall»: так устроено в Go ≤1.25. С Go 1.26 P формально остаётся при M, а «в syscall» определяется по состоянию горутины. Дальше два пути:
- entersyscallblock — для вызовов, которые заведомо блокируют: P отвязывается от M сразу, его подхватывает свободный поток, а если свободных нет — runtime заводит новый.
- entersyscall — для вызовов, которые обычно быстрые: P отсоединяется от M, но не отдаётся другому потоку. Вернулся syscall быстро — тот же M подхватывает свой P обратно, и работа продолжается без лишних телодвижений.
Sysmon отбирает P
Если P завис в syscall дольше примерно 10 мс и в очереди есть готовая работа, sysmon — фоновый поток runtime — отбирает этот P и передаёт свободному M. При пустой очереди и свободных M отбор пропускается — спешить некуда. Так очередь горутин не стоит на месте из-за одного потока, застрявшего в ядре.
Асимметрия с парковкой
Разница с сетевым ожиданием принципиальная. При сетевом I/O горутина паркуется, а поток свободен. При блокирующем syscall — горутина и поток блокируются вместе: поток реально ждёт возврата из ядра. И есть предел, о котором говорил урок: когда M ушёл в блокирующий syscall, вытеснить горутину нельзя. Сигнал до потока дойдёт — он прервёт syscall (обработчики Go ставятся с SA_RESTART), обработчик выполнится, а ядро перезапустит вызов. Но горутину не перепланировать, пока syscall не вернётся.
Вы узнаете:
- как
SA_RESTARTвлияет на прерванный syscall - как выбирается, какой из двух входов использовать
Проверьте себя
Чем entersyscall отличается от entersyscallblock?
Что изменилось с Go 1.26 в отсоединении P?
Можно ли перепланировать горутину, пока её поток застрял в syscall?
правильных ответов
Вход в syscall переводит G в _Gsyscall и отсоединяет P (быстрый возврат подхватывает его обратно; в Go ≥1.26 P формально остаётся при M); sysmon отбирает P, зависший дольше ~10 мс, если есть работа.
10 000 ₽ · 60 минут · онлайн