Я работаю над проектом, который имеет дело с несколькими процессами и потоками, обрабатывающими одни и те же данные. У меня есть строка кода, которая может привести к ошибке сегментации, поскольку данные могут обновляться откуда угодно. Для этой конкретной строки, если она вызывает ошибку сегментации, я как-то хочу обработать ее, а не допустить сбоя программы. Например, я могу просто обновить местоположение памяти, если предыдущее вызывало ошибку сегментации. Есть ли способ сделать это?
ОБНОВЛЕНИЕ (краткое изложение моего дела):
Мне нужен очень быстрый доступ к файлу. С этой целью я вызываю mmap(2), чтобы сопоставить этот файл со всеми процессами, обращающимися к нему. Данные, которые я записываю в файл, имеют форму определенной структуры данных и потребляют много памяти. Поэтому, если наступит момент, когда размера, который я сопоставил, недостаточно, мне нужно увеличить размер файла и снова выполнить mmap(2) этот файл с новым размером. Для увеличения размера я вызываю ftruncate(2). ftruncate(2) может вызываться из любого процесса, поэтому вместо этого он может сжать файл. Поэтому мне нужно проверить, не приводит ли память, к которой я обращаюсь, к ошибкам seg. Я работаю на macOS.
Этот подход неверен. Вам нужно избегать segfault. Как только вы это получите, обычно уже слишком поздно. Segfault означает примерно: где-то в вашей программе есть ошибка, и все пошло не так.
Я бы предложил использовать базу данных для того же, например sqlite
Если ваши «данные могут быть обновлены откуда угодно», вам необходимо реализовать надлежащий механизм синхронизации.
@ЕвгенийШ. Я не уверен, правильно ли я это понял. Пожалуйста, скажите мне, если я ошибаюсь. Означает ли это, что после вызова обработчика сигнала строка кода, вызвавшая появление этого сигнала, выполняется снова, и в следующий раз, если она не вызовет ошибку установки, программа не рухнет?
Также в моем случае мне действительно нужна такая функциональность. Я обновлю свой вопрос, чтобы объяснить его правильно.
Я обновил вопрос. Есть ли лучший способ добиться того же?
mmap() будет иметь серьезные проблемы с производительностью, поскольку страницы все равно придется выгружать в память и извлекать из нее.
@AndrewHenle, это недоступно в macOS. Извините, что не упомянул об этом в моем вопросе. Я обновлю его прямо сейчас.
Другая проблема с общей памятью SysV заключается в том, что она не поддерживает изменение размера объекта общей памяти. Большинство систем, отличных от SysV (я полагаю, включая OSX), в любом случае эмулируют общую память SysV с файлами mmaped. Использование mmap в файловой системе tmpfs (память) работает настолько быстро, насколько это возможно.





Этот могу должен работать, но, добавляя обработчики сигналов в картину, вы значительно усложняете свои проблемы межпроцессной и межпоточной блокировки. Я хотел бы предложить альтернативный подход: зарезервируйте поле на первой странице файла mmapped, чтобы указать ожидаемый размер структуры данных. Используйте блокировки файлов fcntl для доступа к этому полю.
Когда какой-либо процесс хочет обновить размер, он берет блокировку записи, считывает текущее значение, увеличивает его, msync просматривает страницу (использования MS_ASYNC|MS_INVALIDATE должно быть достаточно), тогда использует ftruncate для увеличения файла, затем увеличивает свое отображение файла, и только потом снимает блокировку записи. Если после снятия блокировки записи вы обнаружите, что файл уже больше нужного вам размера, просто увеличьте отображение и снимите блокировку, не вызывайте ftruncate и не изменяйте поле.
Это гарантирует, что взаимодействующие процессы никогда не сделают файл меньше, а область памяти, отображаемая каждым процессом, всегда поддерживается выделенным хранилищем, поэтому вы никогда не должны получать никаких SIGBUSes. Обратите внимание, что благодаря волшебству разреженные файлы размер файла на диске будет увеличиваться только тогда, когда вы на самом деле записываете во вновь выделенное пространство.
Да, вы можете заставить это работать с обработчиком сигнала, который ловит SIGSEGV или SIGBUS, настраивает mmap и возвращает. Когда обработчик сигнала возвращается, он возобновляет работу с того места, где произошел сигнал, что означает, что для синхронного сигнала, такого как SIGSEGV или SIGBUS, он повторно запустит инструкцию, вызвавшую ошибку.
Вы можете увидеть это в моей реализации общая память malloc — найдите shm_segv в malloc.c, чтобы увидеть обработчик сигнала; это довольно просто. Я никогда не пробовал этот код на MacOS, но я думаю, что он будет работать на OSX, поскольку он работает на всех других UNIX-системах, производных от BSD, на которых я его пробовал. Есть проблема, что, согласно спецификации POSIX, mmap не является асинхронно безопасным, поэтому его нельзя вызывать из обработчика сигналов, но на всех системах, которые фактически поддерживают реальное отображение памяти (а не его эмуляцию с помощью malloc+read), это должно быть отлично.
Вы можете сделать это: stackoverflow.com/questions/2663456/…, но это будет очень плохой дизайн.