ALT Linux Sisyphus discussions
 help / color / mirror / Atom feed
* [sisyphus] Q: Очень непростой вопрос на тему select() и сигналов
@ 2009-04-09 22:48 Michael Pozhidaev
  2009-04-09 23:49 ` Mikhail Efremov
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Pozhidaev @ 2009-04-09 22:48 UTC (permalink / raw)
  To: sisyphus

Привет всем!

Вопрос для мастеров тщательной обработки сигналов. Если у кого-нибудь
есть опыт, дайте рекомендации, пожалуйста.

Предположим, мне надо написать программу с обработкой SIGCHLD и данных
поступающих от разных файловых дескрипторов. Хочу сделать это тщательно
и продуманно. Оговорка: есть действия, которые должны быть выполнены
_*незамедлительно*_ после получения SIGCHLD. "Незамедлительно " имеется
в виду без заметной задержки пользователю. Допускается задержка 0.1-0.2
секунды.

Схематически код выглядит так:

int there_was_sigchld = 0;
void sigchld_handler(int)
{
there_was_sigchld = 1;
}

void check_sigchld()
{
if (there_was_sigchld)
do_what_we_need();
there_was_sigchld = 0;
}

void main_loop()
{
while(1)
{
check_sigchld();
select(required_fds);
check_data_from_fds_if_there_was_any();
}
}

Всё выглядит как будто бы нормально: select() просыпается всегда, как
только приходит сигнал или появляются данные в дескрипторах. Мы не уснём
на select() с непроверенным флагом получения SIGCHLD, а когда получим
его ещё раз, то проснёмся.

НО! Если сигнал придёт ПОСЛЕ вызова check_sigchld(), но ДО вызова
select() (вероятность ничтожная, но всё же), то действия, требуемые при
получении SIGCHLD окажутся выполненными только после того, как придут
очередные данные по дескрипторам, а это может быть долго и нас не
устраивает.
Читаем внимательно ман по select(), там есть интересное место:

--- начало цитаты ---
       Идея pselect в том, что если нужно подождать события: сигнала или активности  на
       файловом  дескрипторе, то требуется атомарная проверка, чтобы предотвратить race
       condition.  (Предположим, обработчик сигнала  устанавливает  глобальный  флаг  и
       возвращает  управление.   Тогда  проверка  этого  глобального  флага, за которой
       следует select(), может привести к  подвисанию,  если  сигнал  появляется  сразу
       после  проверки,  но  прямо  перед  вызовом  select.   С другой стороны, pselect
       позволяет сначала заблокировать сигналы, обработать пришедшие сигналы,  а  затем
       вызвать  pselect()  с  желаемой sigmask, тем самым избегая race condition.)  Так
       как Linux в настоящее время не содержит  системного  вызова  pselect(),  текущая
       реализация этой процедуры в glibc все еще содержит race condition.

--- конец цитаты ---

Почти наш случай. Вот тут и нужен совет: правильно я понимаю, что должно
быть так:

void main_loop()
{
while(1)
{
block_sigchld();
check_sigchld();
pselect(required_fds, and_signal_mask_with_enabled_sigchld);
check_data_from_fds_if_there_was_any();
}
}

Если должно быть так, то что произойдёт, если sigchld пришёл в тот
краткий момент, когда он был заблокирован? pselect() увидит, что пока
сигнал был заблокирован, он приходил, и нужно сразу прекратить ожидание,
вызвать его обработчик и дать программе его обработать? Если нет, то
тогда мы вообще теряем получение SIGCHLD. Ещё есть интересная функция
sigpending(), но уместна ли она здесь, вот это непонятно. 

Очень прошу совета и помощи. :) Придумать test case на этот случай почти
невозможно, т. к. нужно умудриться получить SIGCHDL в совершенно
невероятный момент.
-- 
Michael Pozhidaev. E-mail: msp@altlinux.ru.
Tomsk State University. http://www.csd.tsu.ru
ALT Linux Team. http://www.altlinux.org



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [sisyphus] Q: Очень непростой вопрос на тему select() и сигналов
  2009-04-09 22:48 [sisyphus] Q: Очень непростой вопрос на тему select() и сигналов Michael Pozhidaev
@ 2009-04-09 23:49 ` Mikhail Efremov
  2009-04-10  1:50   ` Michael Pozhidaev
  0 siblings, 1 reply; 7+ messages in thread
From: Mikhail Efremov @ 2009-04-09 23:49 UTC (permalink / raw)
  To: ALT Linux Sisyphus discussions

On Friday 10 April 2009 02:48:34 Michael Pozhidaev wrote:
> Схематически код выглядит так:
>
> int there_was_sigchld = 0;

Насколько я помню, единственный тип, который POSIX  разрешает использовать для 
этого - sig_atomic_t. Да, это скорее всего typedef int, но если уж говорить 
об аккуратности...

>
> void main_loop()
> {
> while(1)
> {
> block_sigchld();
> check_sigchld();
> pselect(required_fds, and_signal_mask_with_enabled_sigchld);
> check_data_from_fds_if_there_was_any();
> }
> }

Да, что-то вроде
if (pselect(required_fds, and_signal_mask_with_enabled_sigchld) <0)
	if(errno == EINTR)
		check_sigchld();

> Если должно быть так, то что произойдёт, если sigchld пришёл в тот
> краткий момент, когда он был заблокирован? pselect() увидит, что пока
> сигнал был заблокирован, он приходил, и нужно сразу прекратить ожидание,
> вызвать его обработчик и дать программе его обработать? Если нет, то
> тогда мы вообще теряем получение SIGCHLD. Ещё есть интересная функция
> sigpending(), но уместна ли она здесь, вот это непонятно.

Блокировка сигнала != игнорирование сигнала. Блокированный сигнал не 
потеряется, просто не будет вызван обработчик пока его не разблокируют, что и 
делает pselect. А sigpending() думаю тут не нужен.
По поводу реализации pselect в Linux не знаю, тут видимо надо сам его код 
смотреть, но man подсказывает альтернативное решение:
On systems that lack pselect(), reliable (and more portable) signal trapping 
can be achieved using the self-pipe trick (where a signal handler writes a 
byte to a pipe  whose  other  end  is monitored by select() in the main 
program.)

-- 
WBR, Mikhail Efremov

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [sisyphus] Q: Очень непростой вопрос на тему select() и сигналов
  2009-04-09 23:49 ` Mikhail Efremov
@ 2009-04-10  1:50   ` Michael Pozhidaev
  2009-04-10  9:40     ` Mikhail Efremov
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Pozhidaev @ 2009-04-10  1:50 UTC (permalink / raw)
  To: ALT Linux Sisyphus discussions

Hello, Mikhail Efremov!

> Да, что-то вроде
> if (pselect(required_fds, and_signal_mask_with_enabled_sigchld) <0)
> 	if(errno == EINTR)
> 		check_sigchld();
Угу, это понятно.

>
>> Если должно быть так, то что произойдёт, если sigchld пришёл в тот
>> краткий момент, когда он был заблокирован? pselect() увидит, что пока
>> сигнал был заблокирован, он приходил, и нужно сразу прекратить ожидание,
>> вызвать его обработчик и дать программе его обработать? Если нет, то
>> тогда мы вообще теряем получение SIGCHLD. Ещё есть интересная функция
>> sigpending(), но уместна ли она здесь, вот это непонятно.
>
> Блокировка сигнала != игнорирование сигнала. Блокированный сигнал не 
> потеряется, просто не будет вызван обработчик пока его не разблокируют, что и 
> делает pselect. А sigpending() думаю тут не нужен.
Ага, спасибо! Но переспрошу ещё раз:
1. я заблокировал SIGCHLD;
2. после этого SIGCHLD был послан моему процессу;
3. я вызвал pselect() у которого в маске SIGCHLD разрешён;
4. pselect() увидит, что за период блокировки сигнала он был пойман и
СРАЗУ ЖЕ ЗАВЕРШИТ свою работу с EINTR. 
При этом обработчик сигнала будет вызван, просто немного отложен.
Правильно? Это гарантируется системой? Трюк с pipe(), чтобы
проинформировать о сигнале пока трогать не буду. Потом может как-нибудь.
-- 
Michael Pozhidaev. E-mail: msp@altlinux.ru.
Tomsk State University. http://www.csd.tsu.ru
ALT Linux Team. http://www.altlinux.org



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [sisyphus] Q: Очень непростой вопрос на тему select() и сигналов
  2009-04-10  1:50   ` Michael Pozhidaev
@ 2009-04-10  9:40     ` Mikhail Efremov
  2009-04-10 12:36       ` Sergey Vlasov
  0 siblings, 1 reply; 7+ messages in thread
From: Mikhail Efremov @ 2009-04-10  9:40 UTC (permalink / raw)
  To: ALT Linux Sisyphus discussions

On Friday 10 April 2009 05:50:01 Michael Pozhidaev wrote:

> Ага, спасибо! Но переспрошу ещё раз:
> 1. я заблокировал SIGCHLD;
> 2. после этого SIGCHLD был послан моему процессу;
> 3. я вызвал pselect() у которого в маске SIGCHLD разрешён;
> 4. pselect() увидит, что за период блокировки сигнала он был пойман и
> СРАЗУ ЖЕ ЗАВЕРШИТ свою работу с EINTR.
> При этом обработчик сигнала будет вызван, просто немного отложен.
> Правильно? Это гарантируется системой? Трюк с pipe(), чтобы
> проинформировать о сигнале пока трогать не буду. Потом может как-нибудь.

Все так, это гарантируется стандартом. Вот только стандарт стандартом, а его 
конкретная реализация - это конкретная реализация, в той или иной степени 
этот стандарт поддерживающая. Раньше в Linux не было системного вызова  
pselect, была эмуляция в glibc, проблемы не решающая. Начиная с 2.6.какой-то 
версии ядра такой вызов есть, но я не знаю как он работает. Впрочем, не 
думаю, что есть смысл делать системный вызов, не решающий проблему с race. 
Может кто из знатоков ядра скажет по этому поводу больше.

-- 
WBR, Mikhail Efremov

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [sisyphus] Q: Очень непростой вопрос на тему select() и сигналов
  2009-04-10  9:40     ` Mikhail Efremov
@ 2009-04-10 12:36       ` Sergey Vlasov
  2009-04-10 16:00         ` Michael Pozhidaev
  0 siblings, 1 reply; 7+ messages in thread
From: Sergey Vlasov @ 2009-04-10 12:36 UTC (permalink / raw)
  To: sisyphus

[-- Attachment #1: Type: text/plain, Size: 2656 bytes --]

On Fri, Apr 10, 2009 at 01:40:09PM +0400, Mikhail Efremov wrote:
> On Friday 10 April 2009 05:50:01 Michael Pozhidaev wrote:
> > Трюк с pipe(), чтобы проинформировать о сигнале пока трогать не буду.

Тем не менее этот "трюк" обеспечивает совместимость с наибольшим
количеством систем (не только различных версий Linux).

> Все так, это гарантируется стандартом. Вот только стандарт стандартом, а его 
> конкретная реализация - это конкретная реализация, в той или иной степени 
> этот стандарт поддерживающая. Раньше в Linux не было системного вызова  
> pselect, была эмуляция в glibc, проблемы не решающая. Начиная с 2.6.какой-то 
> версии ядра такой вызов есть, но я не знаю как он работает. Впрочем, не 
> думаю, что есть смысл делать системный вызов, не решающий проблему с race. 
> Может кто из знатоков ядра скажет по этому поводу больше.

Для архитектуры i386 системные вызовы pselect6 и ppoll были добавлены в
ядро в версии 2.6.16, для x86_64 - только в версии 2.6.19 (хотя номера
системных вызовов в asm/unistd.h были зарезервированы ещё в 2.6.16).
Однако имеет значение ещё и версия и детали сборки glibc - совсем старые
версии glibc (до 2.5) предоставляли только бесполезную эмуляцию pselect,
начиная с glibc-2.5, предусмотрена поддержка настоящего системного вызова,
но с автоматическим переключением на эмуляцию, если ядро слишком старое и
возвращает ошибку ENOSYS; кроме того, во время сборки должен быть доступен
asm/unistd.h с определением __NR_pselect6.  В результате всего этого
определить, что при вызове программой функции pselect() действительно
используется этот системный вызов, затруднительно (даже при наличии
достаточно свежего ядра можно получить эмуляцию либо на древней версии
glibc, либо даже на новой, но собранной со старыми заголовками ядра).

В Linux есть и другие способы работы с сигналами совместо с select(), не
имеющие проблем с эмуляцией, не гарантирующей правильную работу:

 - Вариация на тему pipe() - для оповещения основного цикла о пришедшем
   сигнале можно использовать дескриптор eventfd, требующий несколько
   меньше ресурсов, чем pipe; системный вызов доступен начиная с ядра
   2.6.22 (для i386 и x86_64); можно достаточно легко написать код,
   использующий eventfd при наличии его поддержки, но автоматически
   переключающийся на pipe при обнаружении устаревшего ядра или glibc.

 - signalfd - позволяет перевести сигналы в готовность файловых
   дескрипторов; системный вызов доступен начиная с ядра 2.6.22 (для i386
   и x86_64), поддержка в glibc - начиная с версии 2.8; в ядре 2.6.25 была
   исправлена обработка сигналов, переданных через sigqueue().

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [sisyphus] Q: Очень непростой вопрос на тему select() и сигналов
  2009-04-10 12:36       ` Sergey Vlasov
@ 2009-04-10 16:00         ` Michael Pozhidaev
  2009-04-10 16:22           ` Mikhail Efremov
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Pozhidaev @ 2009-04-10 16:00 UTC (permalink / raw)
  To: ALT Linux Sisyphus discussions

Hello, Sergey Vlasov!

Сергей, спасибо за такое подробное и точное объяснение. Сейчас ещё
приглянулся signalfd(), но как-то пока вызывает сомнение необходимость
иметь свежее ядро. Думаю, что могут ещё найтись желающие пожить, скажем,
на 2.6.18. А в остальном оно прямо в точку. :)

> Для архитектуры i386 системные вызовы pselect6 и ppoll были добавлены в
> ядро в версии 2.6.16, для x86_64 - только в версии 2.6.19 (хотя номера
> системных вызовов в asm/unistd.h были зарезервированы ещё в 2.6.16).
> Однако имеет значение ещё и версия и детали сборки glibc - совсем старые
> версии glibc (до 2.5) предоставляли только бесполезную эмуляцию pselect,
> начиная с glibc-2.5, предусмотрена поддержка настоящего системного вызова,
> но с автоматическим переключением на эмуляцию, если ядро слишком старое и
> возвращает ошибку ENOSYS; кроме того, во время сборки должен быть доступен
> asm/unistd.h с определением __NR_pselect6.  В результате всего этого
> определить, что при вызове программой функции pselect() действительно
> используется этот системный вызов, затруднительно (даже при наличии
> достаточно свежего ядра можно получить эмуляцию либо на древней версии
> glibc, либо даже на новой, но собранной со старыми заголовками ядра).
>
> В Linux есть и другие способы работы с сигналами совместо с select(), не
> имеющие проблем с эмуляцией, не гарантирующей правильную работу:
>
>  - Вариация на тему pipe() - для оповещения основного цикла о пришедшем
>    сигнале можно использовать дескриптор eventfd, требующий несколько
>    меньше ресурсов, чем pipe; системный вызов доступен начиная с ядра
>    2.6.22 (для i386 и x86_64); можно достаточно легко написать код,
>    использующий eventfd при наличии его поддержки, но автоматически
>    переключающийся на pipe при обнаружении устаревшего ядра или glibc.
>
>  - signalfd - позволяет перевести сигналы в готовность файловых
>    дескрипторов; системный вызов доступен начиная с ядра 2.6.22 (для i386
>    и x86_64), поддержка в glibc - начиная с версии 2.8; в ядре 2.6.25 была
>    исправлена обработка сигналов, переданных через sigqueue().
> _______________________________________________
> Sisyphus mailing list
> Sisyphus@lists.altlinux.org
> https://lists.altlinux.org/mailman/listinfo/sisyphus

-- 
Michael Pozhidaev. E-mail: msp@altlinux.ru.
Tomsk State University. http://www.csd.tsu.ru
ALT Linux Team. http://www.altlinux.org



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [sisyphus] Q: Очень непростой вопрос на тему select() и сигналов
  2009-04-10 16:00         ` Michael Pozhidaev
@ 2009-04-10 16:22           ` Mikhail Efremov
  0 siblings, 0 replies; 7+ messages in thread
From: Mikhail Efremov @ 2009-04-10 16:22 UTC (permalink / raw)
  To: ALT Linux Sisyphus discussions

On Friday 10 April 2009 20:00:35 Michael Pozhidaev wrote:
> Сергей, спасибо за такое подробное и точное объяснение.

Присоединяюсь, спасибо. Про eventfd и signalfd я и не знал.

-- 
WBR, Mikhail Efremov

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2009-04-10 16:22 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-04-09 22:48 [sisyphus] Q: Очень непростой вопрос на тему select() и сигналов Michael Pozhidaev
2009-04-09 23:49 ` Mikhail Efremov
2009-04-10  1:50   ` Michael Pozhidaev
2009-04-10  9:40     ` Mikhail Efremov
2009-04-10 12:36       ` Sergey Vlasov
2009-04-10 16:00         ` Michael Pozhidaev
2009-04-10 16:22           ` Mikhail Efremov

ALT Linux Sisyphus discussions

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://lore.altlinux.org/sisyphus/0 sisyphus/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 sisyphus sisyphus/ http://lore.altlinux.org/sisyphus \
		sisyphus@altlinux.ru sisyphus@altlinux.org sisyphus@lists.altlinux.org sisyphus@lists.altlinux.ru sisyphus@lists.altlinux.com sisyphus@linuxteam.iplabs.ru sisyphus@list.linux-os.ru
	public-inbox-index sisyphus

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://lore.altlinux.org/org.altlinux.lists.sisyphus


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git