#multithreading #gcc #free
Вопрос:
проблема:
Мое приложение случайным образом выходит из строя при вызове на бесплатный().
Вопрос
У кого-нибудь есть какие-либо намеки на то, что может привести free()
к такому провалу? должен ли я добавить некоторые операции по ограничению памяти? какие-либо параметры GCC или детали потоковой передачи, которые я, возможно, пропустил?
контекст:
Я опытный программист на языке Си, и у меня никогда не было такой проблемы. это в основном убивает 4 месяца работы. В среде с модульным тестированием и постоянной очисткой кода.
это TCP — сервер, использующий winsock 2 select()
.
компиляция с помощью gcc в Windows 10: gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
Тот же код, только завершается сбоем при запуске в под — потоке. все попытки воссоздать проблему потерпели неудачу в циклах тестирования с одним потоком и в тяжелых испытаниях на пытки.
Интересным фактом является то, что приложение не аварийно завершает работу при запуске в GDB.
У меня даже есть уровень управления распределением/удалением (используемый моим кодом и библиотеками), который сбрасывает каждый вызов в calloc()
таблицу и free()
внутри нее. Дополнительный инструмент анализа дампа проверяет все управление памятью, и в результате все они free()
правильно сбалансированы (у меня нет двойного удаления или освобождения случайного указателя).
с помощью моего инструмента анализа фактический указатель представлял собой смесь свежего адреса памяти и переработанного указателя. так что, похоже, это не связано с повторным использованием mem из списка бесплатных (у меня есть десятки повторно используемых адресов до сбоя, после анализа памяти).
при выполнении в потоке этот код не использует общую память с основным потоком, он в основном запускает работника, который выполняет ВСЮ работу. в настоящее время существует только один вспомогательный поток, поэтому это не может быть столкновением рабочего потока. основной поток ничего не делает для ожидания tcp-порта и добавления узла в диспетчер пула потоков.
Код публикации бесполезен, так как ошибка не воспроизводима за пределами ее выполнения в подпространстве в моем основном приложении… т. е. это не кажется логической проблемой.
запуск того же кода из основного потока не приведет к сбою!
Сбой free()
всегда происходит в определенной комбинации обрабатываемых данных, но он может быть успешным неопределенное количество раз до сбоя, и одна и та же функция вызывается МНОГО раз для эквивалентных данных и не завершается сбоем.
Комментарии:
1. Похоже, вы рассмотрели обычные ситуации. Другие ситуации заключаются в том, что вы записали в память и повредили данные. Поэтому следующим шагом было бы поиск переполнения буфера (в обоих основных потоках a). Попробуйте использовать valgrind
2. Насколько сложна/надежна репликация сбоев? Не могли бы вы использовать такие методы, как удаление некоторых вызовов
free
и проверка того, исчезнет ли сбой, или добавление «буферов безопасности» к распределениям и проверка того, исчезнет ли сбой?3. @MartinYork в последний раз, когда я проверял, использование valgrind в Windows казалось довольно сложным. могу ли я использовать valgrind через WSL для тестирования своего приложения для Windows?
4. @DavidSchwartz добавление буферов было бы легко сделать (через мой слой mem mgt)… было бы по крайней мере интересно посмотреть, изменит ли это симптомы.
5. У вас есть возможность запустить такой
valgrind
или подобный инструмент, чтобы проверить, нет ли здесь какой-то ошибки в памяти? Это может быть основной причиной.
Ответ №1:
наконец-то нашел проблему.
В моем коде возникла странная проблема, когда был сгенерирован отрицательный размер выделения памяти, и мой malloc()
код использования не выявил проблему и вернул поддельный адрес.
фактическая malloc()
неудача, попытка освободить его, была ошибкой.
Я смог поймать его, выполнив прямое распределение виртуальных машин и защитив страницы между каждым распределением.
Когда мой код попытался memmove()
ввести ошибочный адрес, он сгенерировал исключение доступа к ОС, и я смог отследить ошибку сразу, когда это произошло.
Наконец, насколько я могу судить, это не имело никакого отношения к потоковому коду. Я все еще чувствую, что странно, что он не разбился в gdb.
спасибо всем, кто дал мне идеи для изучения.