* Re: [Comm] pthread, gcc vs g++: seg fault
@ 2009-11-05 13:04 ` Sergey Vlasov
2009-11-05 19:20 ` Rinat Bikov
0 siblings, 1 reply; 2+ messages in thread
From: Sergey Vlasov @ 2009-11-05 13:04 UTC (permalink / raw)
To: community
[-- Attachment #1: Type: text/plain, Size: 4971 bytes --]
On Thu, Nov 05, 2009 at 03:02:57PM +0400, Rinat Bikov wrote:
> Передо мной стоит задача: написать динамическую библиотеку, которая
> могла бы загружать и выгружать другие динамические библиотеки из
> главной функции таким образом, чтобы загружаемые динамические
> библиотеки имели доступ к интерфейсу загружающей их динамической
> библиотеки (:-)).
> Притом загружаемые динамические библиотеки могут зацикливаться,
> соответственно необходимо, чтобы они убивались по таймауту.
> Для решения этой задачи я решил воспользоваться pthreads.
> Проблему того, что потоки с зацикливающейся функцией не убиваются
> стандартно pthread_cancel решил при помощи отправки
> pthread_kill(SIGTERM) + добавил обработчик этого сигнала в потоке:
> signal(SIGTERM, sig_handler);
Обработчики сигналов на самом деле устанавливаются глобально для всего
процесса (а вот маска заблокированных сигналов, управляемая через
pthread_sigmask(), действительно своя у каждого потока). Кроме того,
по стандарту POSIX.1-2001 функция signal() не должна использоваться в
многопоточных программах - нужно использовать sigaction() (впрочем,
это стоит делать в любом случае, поскольку поведение signal() может
отличаться в зависимости от параметров, используемых при сборке).
Однако на самом деле это "решение" создаёт новые проблемы.
По умолчанию все потоки используют режим PTHREAD_CANCEL_DEFERRED, в
котором вызовы pthread_cancel() для потока обрабатываются только в
момент выполнения потоком функций, перечисленных в pthreads(7) в
списке "Cancellation Points" (точнее, на самом деле этот список
несколько шире - в этой документации перечислены только функции,
входящие в стандарты POSIX). Если поток зацикливается без вызова
таких функций, его действительно не получится прервать вызовом
pthread_cancel().
Однако вызовом функции pthread_setcanceltype() с параметром
PTHREAD_CANCEL_ASYNCHRONOUS можно включить асинхронную обработку
pthread_cancel() - в этом случае вызов pthread_cancel() прерывает
выполнение потока в любом состоянии. Проблема в том, что после
установки режима PTHREAD_CANCEL_ASYNCHRONOUS можно вызывать только те
функции libc, которые отмечены в документации как async-cancel-safe -
а по стандарту POSIX.1-2001 в эту группу входят только 3 функции:
pthread_cancel(), pthread_setcancelstate(), pthread_setcanceltype().
В результате такой режим можно устанавливать только для кода, который
выполняет только вычисления, при этом не обращаясь к файлам, не
используя динамическое распределение памяти и других общих ресурсов; в
случае C++ нужно учитывать, что безобидно выглядящий код может на
самом деле вызывать функции STL и прочих библиотек, где используется
operator new и т.п.
В случае использования pthread_kill() обработчик указанного сигнала
вызывается в контексте указанного потока асинхронно (если в этот
момент сигнал в потоке не заблокирован). В приложенном примере кода
этот обработчик вызывает fprintf() (чего, кстати, делать нельзя -
список async-signal-safe функций есть в signal(7); в принципе можно
вызывать write()), это cancellation point, в результате обрабатывается
отложенный вызов pthread_cancel(). Однако таким образом фактически
происходит преобразование режима PTHREAD_CANCEL_DEFERRED в
PTHREAD_CANCEL_ASYNCHRONOUS, со всеми его проблемами (посланный через
pthread_kill() сигнал мог прервать выполнение потока, например, в
середине функции типа malloc(), и прерывание работы потока в этом
состоянии приведёт к повреждению общих структур данных и нарушению
работы других потоков).
(На самом деле реализация функции pthread_cancel() в glibc использует
сигналы для прерывания потоков, но в режиме PTHREAD_CANCEL_DEFERRED
соответствующий сигнал большую часть времени заблокирован, и
разблокируется только перед выполнением системных вызовов в функциях,
являющихся cancellation point; в режиме PTHREAD_CANCEL_ASYNCHRONOUS
этот сигнал разблокирован.)
Для решения исходной задачи (прерывание операций по таймауту) можно
предложить следующие варианты:
1) Аккуратно писать код, который нужно прерывать: в участках, где
выполняются длительные вычисления без обращения к функциям, не
допускающим прерывания, включать PTHREAD_CANCEL_ASYNCHRONOUS, в
остальном коде использовать PTHREAD_CANCEL_DEFERRED и пары
pthread_cleanup_push()/pthread_cleanup_pop() для освобождения
ресурсов при прерывании выполнения потока (впрочем, деструкторы
C++ для автоматических объектов при этом всё-таки вызываются).
2) Создавать для выполнения кода, который необходимо прерывать, не
поток, а полноценный процесс - в этом случае нет проблем с порчей
структур в памяти (за исключением случая, когда для обмена
информацией между процессами используется разделяемая память).
[...]
> Также прилагается strace удачного и неудачного запуска.
strace для многопоточных программ надо запускать с опцией -f, иначе
отслеживается только выполнение главного потока.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [Comm] pthread, gcc vs g++: seg fault
2009-11-05 13:04 ` [Comm] pthread, gcc vs g++: seg fault Sergey Vlasov
@ 2009-11-05 19:20 ` Rinat Bikov
0 siblings, 0 replies; 2+ messages in thread
From: Rinat Bikov @ 2009-11-05 19:20 UTC (permalink / raw)
To: ALT Linux Community general discussions
5 ноября 2009 г. 17:04 пользователь Sergey Vlasov написал:
[...]
> 1) Аккуратно писать код, который нужно прерывать[...]
Это решение мне точно не подходит, так как по задумке прерываться
должен не мой код... + код на любом языке программирования,
поддерживающий создание динамических библиотек и как минимум для 2-х
платформ: Linux и Windows.
> 2) Создавать для выполнения кода, который необходимо прерывать, не
> поток, а полноценный процесс [..]
Ммм... В таком случае будет сложнее взаимодействие: для того, чтобы
освободить модулеписателей от необходимости изучать межпроцессовое
взаимодействие, мне нужно будет написать как минимум ещё 2 модуля:
один со стороны моей программы, вызывающий процесс, другой -
вызываемый процесс, используемый для загрузки динамической
библиотеки... .oO(интересно, это решение будет портируемо под Win?..)
Спасибо большое за подробное разъяснение! Буду иметь ввиду...
P.S. А пока я написал какой-то хак для GCC, так как работает то, что
не должно работать :-). Если убрать вывод в поток ошибок из
обработчика сигнала SIGTERM, то программа, скомпилированная под GCC
также завершается с segmentation fault... :-)
P.P.S. А всё это я задумал только для того, чтобы научиться выгружать
динамические библиотеки из Java-программы :-)... Хотя, для этого есть
ещё один вариант: убивать из Java поток вместе с моей библиотекой, а в
самой библиотеке не использовать pthread... (чего хотелось было
избежать для красоты решения, однако сейчас этот способ выглядит для
меня наиболее простым и безопасным, но решение с процессами интересное
с точки зрения изучения :-)).
--
С уважением, Ринат Биков.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2009-11-05 19:20 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-11-05 13:04 ` [Comm] pthread, gcc vs g++: seg fault Sergey Vlasov
2009-11-05 19:20 ` Rinat Bikov
ALT Linux Community general discussions
This inbox may be cloned and mirrored by anyone:
git clone --mirror http://lore.altlinux.org/community/0 community/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 community community/ http://lore.altlinux.org/community \
mandrake-russian@linuxteam.iplabs.ru community@lists.altlinux.org community@lists.altlinux.ru community@lists.altlinux.com
public-inbox-index community
Example config snippet for mirrors.
Newsgroup available over NNTP:
nntp://lore.altlinux.org/org.altlinux.lists.community
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git