Почему в Go нет stackoverflow

Я прочитал в этой презентации http://golang.org/doc/ExpressivenessOfGo.pdf стр. 42:

Безопасно

– без переполнения стека

Как это возможно? и/или как Go работает, чтобы избежать этого?


person OscarRyz    schedule 19.11.2010    source источник


Ответы (5)


Это функция под названием «сегментированные стеки»: каждая горутина имеет свой собственный стек, размещенный в куче.

В простейшем случае реализации языка программирования используют один стек для каждого процесса/адресного пространства, обычно управляемый специальными инструкциями процессора, называемыми push и pop (или что-то в этом роде), и реализованный в виде динамического массива кадров стека, начинающихся с фиксированного адреса (обычно , вершина виртуальной памяти).

Это (или раньше было) быстро, но не особенно безопасно. Это вызывает проблемы, когда много кода выполняется одновременно в одном и том же адресном пространстве (потоках). Теперь каждому нужен свой стек. Но тогда все стеки (за исключением, возможно, одного) должны быть фиксированного размера, чтобы они не перекрывались друг с другом или с кучей.

Однако любой язык программирования, использующий стек, также можно реализовать, управляя стеком другим способом: используя структуру данных списка или аналогичную структуру, которая содержит кадры стека, но фактически размещается в куче. Переполнения стека не будет, пока куча не будет заполнена.

person Fred Foo    schedule 19.11.2010
comment
У каждого потока есть собственный стек, поэтому возникают проблемы, когда много кода выполняется одновременно в одном и том же адресном пространстве (потоках). не может быть правдой. - person dan_waterworth; 19.11.2010
comment
Многое из этого неверно, Go по-прежнему использует инструкции push/pop (ну, на самом деле они этого не делают, но GCC тоже не использует [mov off(%esp)]), он просто устанавливает стек и базовые регистры стека в выделенную кучу куча. - person cthom06; 19.11.2010
comment
Вот это да. Я никогда не слышал об этом. Отличный ответ! - person Matt Joiner; 19.11.2010
comment
@dan_waterworth, @cthom06, я сильно упростил. Будет редактировать. - person Fred Foo; 19.11.2010
comment
и stackless — это проект по переносу тасклетов на python. тасклеты — это потоки, которые начинаются с небольшого стека. - person dan_waterworth; 19.11.2010
comment
@dan_waterworth, первоначальной целью Stackless было избавиться от стека C: zope.stackless.com/index_old .htm Я удалил немного о Stackless, так как они действительно не такие антистековые, как раньше. - person Fred Foo; 19.11.2010
comment
Я исправлен, однако в свою защиту скажу, что основная функция без стеков — тасклеты. - person dan_waterworth; 19.11.2010
comment
вы можете заменить стек исполняемого потока изнутри потока. - person dan_waterworth; 19.11.2010
comment
Однако я не думаю, что это особенно важно для безопасного качества. Независимо от того, находятся ли кадры в простом старом стеке или динамически распределяются внутри кучи, в конечном итоге они могут закончиться. Если вы переполняете объект в сегментированном стеке, вы все равно можете вызвать проблемы с безопасностью, как если бы это было переполнение кучи. - person poolie; 22.11.2010
comment
@poolie: исключения переполнения стека - это боль в C или C ++. Это заставляет вас вручную создавать итеративные версии рекурсивных функций или, как это делал CLang за последние две недели, перемещать выполнение в отдельный поток... и это если вы можете предвидеть проблему. Чтобы обойти это, многие люди просто устанавливают большой стек (где я работаю, это 8 МБ на поток), но это все еще требует настройки и догадок. Отсутствие необходимости беспокоиться о размере стека повышает безопасность (но не безопасность). - person Matthieu M.; 22.11.2010
comment
Судя по комментариям здесь, этому ответу нужно немного больше. Сегментированные стеки — это больше, чем просто выделенная куча. Среда выполнения Go гарантирует, что стек достаточно велик (см. runtime·morestack) в начале функции, а если это не так, она выделяет больше места для стека (если памяти недостаточно, она паникует). - person cthom06; 22.11.2010
comment
@poolie: этот подход заменяет исключение переполнения стека общей ошибкой нехватки памяти. Нет никакого способа обойти это, кроме как изобрести бесконечную память. Разница в том, что при использовании стеков с кучей у вас есть одна область памяти, о которой нужно беспокоиться, а не одна на поток. - person Fred Foo; 22.11.2010
comment
Я понимаю выгоду, я просто не уверен, что они имели в виду под переполнением стека в этом контексте. Кстати, на 64-битных машинах это преимущество несколько размыто, где адресного пространства так много, что можно хранить разнесенные стеки: дайте им по 4 ГБ и будьте счастливы. (Очевидно, что не все машины 64-битные.) - person poolie; 22.11.2010
comment
Является ли сегментированные стеки правильным термином для описания этого? Разве это не должно быть фреймы стека, размещенные в куче? - person Noob Saibot; 03.11.2014
comment
@NoobSaibot Наверное. У людей Го есть привычка заново изобретать велосипед и давать пощечины на своих условиях. (Хотя с довольно хорошими результатами.) - person Fred Foo; 03.11.2014

он использует сегментированный стек. Что в основном означает, что он использует связанный список вместо массива фиксированного размера в качестве стека. Когда ему не хватает места, он делает стек немного больше.

редактировать:

Вот дополнительная информация: http://golang.org/doc/go_faq.html#goroutines

Причина, по которой это так здорово, не в том, что он никогда не переполнится (это приятный побочный эффект), а в том, что вы можете создавать потоки с очень небольшим объемом памяти, то есть их может быть много.

person dan_waterworth    schedule 19.11.2010

Я не думаю, что они могут «полностью» избежать переполнения стека. Они предоставляют способ предотвратить наиболее типичные ошибки, связанные с программированием, которые приводят к переполнению стека.

Когда память заканчивается, невозможно предотвратить переполнение стека.

person fabrizioM    schedule 19.11.2010
comment
но любой, кто пишет программу, которая переполняет стек на основе кучи, делает это неправильно. - person dan_waterworth; 19.11.2010
comment
а это 90% людей, поэтому разработчики го постарались предотвратить это - person fabrizioM; 19.11.2010
comment
90% людей переполнят стек на основе heap (например, go)? - person dan_waterworth; 19.11.2010
comment
Вы не можете по определению использовать сегментированный стек на основе кучи. Переполнение стека — это stack_growth->*collision*‹-heap_growth. С сегментированными стеками просто не хватает памяти. (и все еще не переполнение в Go, вместо этого паникует распределитель) - person cthom06; 19.11.2010
comment
Для языка C размер стека по умолчанию составляет от 1 до 8 МБ, что обычно намного меньше любой памяти компьютера. На самом деле это может заставить вас избегать рекурсии, когда рекурсия является самым простым решением. - person Muhammad Annaqeeb; 20.11.2014

Даже C может сделать это с несколькими ограничениями, которые в основном влияют на компилятор.

Это впечатляющий подвиг инженерии, но не дизайна языка.

person Joshua    schedule 19.11.2010
comment
У меня их нет. Я в основном изобрел технику. С небольшой сборкой и немного воображения вы тоже можете. Это не так сложно. - person Joshua; 19.11.2010
comment
Установить esp где-то в куче? - person Matt Joiner; 21.11.2010

Я думаю, что они имеют в виду здесь то, что доступ к массивам всегда сверяется с фактической длиной массива, тем самым отключая один из наиболее распространенных способов случайного или злонамеренного сбоя программ на C.

Например:

package main

func main() {
    var a [10]int

    for i:= 0; i < 100; i++ {
        a[i] = i
    }
}

будет panic с ошибкой времени выполнения при попытке обновить несуществующий 11-й элемент массива. C будет чиркать по куче и, возможно, тоже рухнет, но неконтролируемым образом. Каждый массив знает свою длину. В некоторых случаях у компилятора будет возможность оптимизировать проверки, если он сможет доказать, что в них нет необходимости. (Или достаточно умный компилятор мог бы статически обнаружить проблему в этой функции.)

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

По сути, указатели Go всегда должны быть типобезопасными, с массивами и другими типами, если только вы специально не используете пакет unsafe.

person poolie    schedule 22.11.2010
comment
Я думаю, вы путаете переполнение буфера и переполнение стека. Хотя ты прав. - person cthom06; 22.11.2010
comment
То, что вы описываете, - это проверка границ, она не имеет ничего общего с переполнением стека, о котором спрашивал OP. Паскаль тоже делает это, но (в типичных реализациях) уязвим к переполнению стека. - person Fred Foo; 22.11.2010
comment
Я не могу понять, говорится ли в комментарии о переполнении буфера стека или о переполнении стека. Если бы я описывал ключевые особенности Go (для аудитории, знающей C), я бы обязательно упомянул проверенные массивы, прежде чем упомянул стеки почти неограниченного размера. С другой стороны, это упоминание находится в разделе о параллелизме, поэтому, возможно, они означают, что вы можете добавлять потоки, не создавая для них небольшие стеки. - person poolie; 23.11.2010