Реализация std :: malloc в соответствии со стандартом C ++ - PullRequest
0 голосов
/ 18 февраля 2019

Быстрый мысленный эксперимент, прежде чем перейти к вопросу.Представьте, что кто-то реализует std :: malloc (скажем, один из людей JEMalloc или TCMalloc).Одной из самых основных вещей, которые им понадобятся, является способность знать, что программа не будет вызывать обратно в malloc после того, как выполнение вступит в реализацию std :: malloc.

Например,

void* malloc(...) {
    auto lck = std::unique_lock{malloc_mutex};
    // .. memory allocation business logic
}

Теперь, если есть сигнал между блокировкой и бизнес-логикой для распределения, мы можем заблокировать, если обработчик сигнала вызывает обратно в std :: malloc,Он не предназначен для повторного входа, стандарт C ++ требует, чтобы обработчик сигнала, зарегистрированный с помощью std :: signal, не вызывал обратно в оператор new (который, возможно, может вызывать обратно в malloc, поэтому требуется, чтобы определяемый пользователем сигналобработчик не перезванивает в malloc, если он считается переносимым во всех реализациях языка).

§ [support.signal]p3 в самой последней версии стандарта излагается это требование

Оценка безопасна для сигнала, если она не включает одно из следующего:

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


Однако стандарт C ++, по-видимому, ничего не говорит о том, как должны быть реализованы стеки функций для потоков выполнения (см. Этот вопрос: Диапазон адресов стека потоков C ++ ), это означает, что функция диспетчеризируется в пределах std :: mallocреализация может вызвать operator new, если программа скомпилирована с сегментированными стеками.

Как можно реализовать такую ​​функцию, как std::malloc в этом случае?Если действительно, стандарт C ++ не дает таких гарантий, то что же?Как мы можем знать, что реализация обычной функции проходит через процесс выделения обычного стека (приращение указателя стека)?Какой стандарт (например, ABI, компилятор, POSIX) покрывает это?

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019

По логике Стандарта C ++ реализация рассматривается как единое целое.В частности, любая часть реализации может предполагать что-либо о любой другой части реализации.

Для этого вопроса это означает, что std::malloc и обработчик сигнала могут предполагать что-то друг о друге.Некоторые реализации могут решить, что их реализация std::malloc является асинхронной, другие могут решить, что это не так.Но существует множество других предположений, которые могут существовать - выравнивание, смежность, переработка свободных адресов и т. Д. Поскольку это все внутреннее для реализаций, нет Стандарта, описывающего это.

Это проблема для"замена mallocs".Вы можете реализовать JE::malloc, но std:: особенный.C ++ по крайней мере признал возможность замены operator new, но даже это никогда не указывалось для этого подробного уровня.

0 голосов
/ 18 февраля 2019

Реализация должна использовать сигнально-безопасный распределитель для своих кадров стека.Это следует из того факта, что вызовы функций (для небиблиотечных функций) в обработчиках сигналов разрешены.Реализация может использовать malloc или operator new, но только если эти распределители сами по себе безопасны по сигналу.

...