Нереентерабельные функции в обработчиках сигналов

Нереентерабельные функции в обработчиках сигналов

17.04.2012 06:09:10 Просмотров 20 Источник

Всем добрый вечер! Есть задача освобождения ресурсов, используемых дочерними потоками, после их завершения. Меня очень просили реализовать данную операцию асинхронно, но, насколько я понимаю, функция free(), как и malloc(), не является реентерабельной и не рекомендуется к использованию в обработчиках сигналов. Я пока что вижу 2 пути :

  1. выставлять флаг в обработчике сигналов, проверять его в основном потоке и освобождать память там,
  2. блокировать сигнал перед каждым обращением к free() во всех потоках.

Если кто знает более интересные варианты решения, поделитесь пожалуста.


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

У вопроса есть решение - Посмотреть?

https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2#comment101783_101781
malloc() и free() безусловно в обработчике сигнала нельзя. Времени, нет. Вечером м.б. подробно напишу.
https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2#comment101879_101781
Какая-то тут смесь получилась: потоки и обработчики сигналов. Уточните, пожалуйста, что Вы хотите сделать.
https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2#comment101896_101781
Внимательно прочитал вопрос и понял, что мне тоже необходимо уточнение. Не понимаю связи между завершением потока (thread ?) и сигналом, в обработчике которого надо освобождать ресурсы. -- Вообще, какая-либо серьезная работа в signal handler не приветствуется. -- По поводу "безусловно" в своем предыдущем комментарии - я несколько погорячился. В принципе можно написать программу (используя блокировку сигналов) так, что malloc/free в обработчике и синхронной (в т.ч. в потоках) части не будут пересекаться. Но тут легко наделать ошибок и не учесть косвенные вызовы malloc/free >>>
https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2#comment101897_101781
>>> продолжение По поводу функций, которые считается можно вызывать из обработчика сигнала см. здесь есть их список Про malloc/free Обычно при вызове из обработчика (один раз на миллион) процесс зависает на "самоблокировании" вызывая pthread_mutex_lock(). (Лично наблюдал из gdb).
https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2#comment101936_101781
Ситуация следующая : в основном потоке выполнения для каждого дочернего потока создается некоторая структура данных, она помещается в список, ее указатель передается дочернему потоку. По завершении дочернего потока необходимо удалить структуру из списка и освободить из-под нее память. Можно было бы делать это прямо в дочернем потоке, но, насколько я понимаю, хорошим тоном считается освобождение памяти, выделенной для дочернего потока, в основном потоке, где она и выделялась.
https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2#comment101937_101781
Основной поток в данном случае производит дополнительную работу, и меня просили не включать в его основной цикл отслеживание состояния завершения дочерних потоков, а сделать это асинхронно, в обработчике сигналов, поэтому по завершении дочернего потока я посылаю сигнал SIGUSR1

Ответы - Нереентерабельные функции в обработчиках сигналов / Нереентерабельные функции в обработчиках сигналов

Является ответом!
avp

18.04.2012 11:23:18

@margosh, лимит комментариев у меня подошел к концу, поэтому переключаюсь на "ответ".

Не нравится мне идея делать free() в обработчике. Более того, я не понимаю, как Вы узнаете по получению SIGUSR1 что именно освобождать.

В подобной задаче (при Ваших условиях) я сделал бы что-то в таком духе:

  1. Создал pipe, который разделяют все потоки.
  2. Создал поток, читающий этот pipe и освобождающий память.
  3. Перед завершением потока (вместо SIGUSR1) писал в pipe, что освобождать.

Собственно как-то так. Без сигналов и асинхронно к основному циклу обработки. Если отбросить "правила хорошего тона", то проще всего (хотя я не знаю ряда тонкостей Вашей задачи) освобождать ресурсы в завершающемся потоке.

https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2/101973#comment101986_101973
@avp, дочерний поток перед завершением выставляет в своей момнопольной структуре данных флаг о завершении, после получения сигнала я проверяю наличие установленных флагов по списку, хотя возможно это не самое удачное решение. Однако без сигналов не получится - так как есть еще и порожденные процессы, которые тоже используют области памяти, которые необходимо освободить по завершении, а процессы посылают SIGCHLD. Хотелось бы использовать идентичное решение для освобождения ресурсов как процессов, так и потоков.
https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2/101973#comment101995_101973
Вы уверены, что на самом деле надо асинхронно обрабатывать завершение процессов ? А зачем освобождать память после завершения процесса ? Это можно сделать сразу после fork(). В принципе, обработчик SIGCHILD может безопасно делать write() в pipe (например для logger или статистики, посылая данные, полученные из waitpid()).
https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2/101973#comment102080_101973
@avp,>>"А зачем освобождать память после завершения процесса ?" Потому что эта память используется в основном потоке, но хранит данные о порожденном процессе, такие как pid, адрес клиента, инициировавшего порождение данного процесса и тп. Соответственно, когда процесс по какой-то причине завершился, эти данные выводятся в log и больше не нужны. Согласна, завести еще один поток для освобождения ресурсов, и слушать в нем pipe можно. Спасибо!
https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2/101973#comment102097_101973
Успехов. Добавить мьютекс для списка с данными теперь не забудьте.
https://ru.stackoverflow.com/questions/101781/%d0%9d%d0%b5%d1%80%d0%b5%d0%b5%d0%bd%d1%82%d0%b5%d1%80%d0%b0%d0%b1%d0%b5%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%84%d1%83%d0%bd%d0%ba%d1%86%d0%b8%d0%b8-%d0%b2-%d0%be%d0%b1%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b0%d1%85-%d1%81%d0%b8%d0%b3%d0%bd%d0%b0%d0%bb%d0%be%d0%b2/101973#comment102130_101973
Не забуду :)
Закрыть X