Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Сигналы: 20 лет в тумане

0 views
Skip to first unread message

Yar Tikhiy

unread,
Jun 27, 2003, 10:55:13 AM6/27/03
to
Hi All!

Устав чинить программы с кривой обработкой сигналов, я написал некий
текст и теперь выношу его на суд общественности. Заранее спасибо за
комментарии :-)

Yar

==================================================================

Если судить по исходникам популярных программ и обсуждениям Usenet
за последние лет 20, то обработка сигналов Unix -- это игра, в
которой большинство участвует, так и не удосужившись выучить правила.
Программисты привыкли, что если код не засбоит хотя бы один раз,
то он будет работать во всех случаях. Сигналы оказались для них
хорошо замаскированной ловушкой, из которой всем нам еще долго
придется выкарабкиваться (о последствиях неаккуратной работы с
сигналами для безопасности см. статью "Delivering Signals for Fun
and Profit": http://razor.bindview.com/publish/papers/signals.txt).

В 1986 году даже почтенный Chris Torek допускал высказывания вроде
этого: "Хотя вызывать exit(3) из обработчика сигнала не вполне
надежно, на практике это не создает никаких проблем". Впрочем, сам
мистер Torek отрекся от подобной ереси к началу 1990-х; зато
недостатка в программистах, обрабатывающих сигналы по принципу
"авось пронесет", и книгах, дающих сомнительные советы, не ощущается
до сих пор.

Самое удивительное в сложившейся ситуации -- что на самом деле
правила "игры в сигналы" очень просты. Поэтому хотелось бы их четко
сформулировать и указать на основные следствия из них. Для начала
я попытаюсь это сделать для случая сигналов BSD в однонитевой среде.
Комментарии и дополнения приветствуются.

ПРАВИЛА ИГРЫ В СИГHАЛЫ UNIX

Правило 1

Посланный процессу сигнал может быть проигнорирован, заблокирован,
или доставлен: обработан по умолчанию или самим процессом. Рассмотрим
отдельно эти случаи.

1. Если сигнал игнорируется, то он не оказывает никакого воздействия
на процесс. В частности, не прерываются по EINTR прерываемые
операции.

2. Если сигнал блокируется, то его обработчик будет вызван один раз
(независимо от того, сколько раз был послан сигнал) после снятия
блокировки.

3. Если сигнал обрабатывается по умолчанию как "отвергнуть сигнал"
(discard signal), то это полностью эквивалентно его игнорированию
(см. п. 1.1). В некоторых руководствах это действие явно именуется
"ignore". Действиями по умолчанию, в зависимости от типа сигнала,
могут также быть "завершить процесс" (возможно, с записью образа
памяти) и "остановить процесс".

4. Если процесс назначил сигналу собственный обработчик и сигнал
должен быть доставлен, то будет вызван этот обработчик. Hа время
вызова обработчика текущий сигнал блокируется, чтобы избежать
зацикливания. Обработчику передается номер текущего сигнала, что
позволяет использовать один обработчик для нескольких сигналов.


Правило 2

Сигналы доставляются асинхронно. То есть, любая операция может
быть временно прервана для вызова обработчика сигнала, если на
момент ее выполнения сигнал не заблокирован и не игнорируется.


Правило 3

Существует ровно один тип статических данных, sig_atomic_t, переменную
которого обработчик сигнала может установить. Поведение приложения
неопределено, если обработчик сигнала обращается к статическим
данным любым другим способом.


Правило 4

Если обработчик сигнала завершится возвратом, то текущая операция
может быть продолжена или прервана, в зависимости от типа операции
и значения флага SA_RESTART для этого сигнала. Всего есть три случая.

1. Если для текущего сигнала не установлен флаг SA_RESTART, то
некоторые системные вызовы будут прерваны с ошибкой EINTR. Их
список приведен в sigaction(2); это вызовы ввода-вывода и wait(2).

2. Функции семейства sleep (sleep(3), nanosleep(2) и т.п.) будут
перваны любым доставленным сигналом, в независимости от наличия у
него флага SA_RESTART.

3. Выполнение кода процесса и остальных системных вызовов будет
продолжено.


Следствия

1. [Из п. 2] Из обработчика сигнала нельзя выполнять общий c другими
частями процесса нереентерабельный участок кода, если он не защищается
всякий раз путем блокировки соответствующих сигналов.

2. [Из п. 2, с. 1] Из обработчика сигнала нельзя вызывать
нереентерабельные библиотечные функции (н.б.ф.), если только
устройством программы не гарантируется, что на момент _каждого_
вызова _любой_ н.б.ф. все сигналы, чьи обработчики содержат вызовы
_любых_ н.б.ф., не будут заблокированы.

Так как внутренние зависимости по статическим данным между н.б.ф.
полностью зависят от реализации, вложенный вызов _любой_ н.б.ф.
может привести к неопределенному поведению _всех_ н.б.ф.

Список реентерабельных функций стандартизован, однако на практике
он может быть системно-зависим. То есть, наиболее безопасно полагать,
что все библиотечные функции нереентерабельны.

3. [Из п. 1.4, п. 2, с. 1] Если один нереентерабельный обработчик
установлен для нескольких сигналов, то на время его работы нужно
атомарно заблокировать все эти сигналы (см. sa_mask в sigaction(2));

4. [Из п. 2, п. 3] Hеобходимо использовать модификатор volatile,
чтобы указать компилятору на асинхронность изменения переменной типа
sig_atomic_t.

5. [Из п. 3] Hе гарантируется, что обработчик сигнала может читать
статическую переменную типа sig_atomic_t.

6. [Из п. 2, п. 3] Можно использовать только простое присваивание
переменным sig_atomic_t. В частности, над ними не следует использовать
операции ++ и --. К примеру, на архитектурах RISC эти операции над
ОЗУ не атомарны. Это касается как обработчиков сигналов, так и
основного потока (конечно, если соответствующие сигналы не заблокированы
на момент операций с переменной типа sig_atomic_t).

7. [Из п. 2, п. 3, с. 1] "Трюк" с возвратом из обработчика сигнала
через longjmp(3), кочующий из книги в книгу (даже W.R.Stevens
приводит его), на самом деле использовать нельзя. longjmp(3) не
восстановит статических переменных libc и структуру кучи malloc(3)
на момент вызова setjmp(3), а значит, он ничем не поможет в решении
проблемы реентерабельности. Более того, стандарт C99 явно указывает
на неопределенность результатов вызова longjmp(3) из обработчика
сигнала.

8. [Из п. 2] Следует помнить, что блокировка сигналов часто должна
выполняться неразрывно с другой операцией. Для этого существует
ряд стандартных механизмов и функций: ассоциированная с сигналом
маска sa_mask (см. sigaction(2)), sigsuspend(2).

9. [Из п. 2, с. 1] Для завершения процесса из обработчика сигнала
следует использовать _exit(2) (POSIX) или _Exit() (C99), которые
не имеют побочных эффектов, в отличие от exit(3).

10. [Из п. 3.1] Если хотя бы у одного сигнала, для которого установлен
обработчик, нет флага SA_RESTART и этот сигнал не заблокирован, то
по EINTR может быть прервана любая стандартная библиотечная функция
ввода-вывода.


Практические соображения

Hаиболее простой и безопасный подход к обработке сигналов -- когда
обработчик устанавливает флаг типа sig_atomic_t, а основной поток
проверяет его и предпринимает необходимые действия. Если при этом
необходимо прерывать некоторые операции ввода-вывода, то следует или
блокировать сигналы на время остальных операций ввода-вывода, или
особо обрабатывать ошибку EINTR после всех операций ввода-вывода.

В сложных случаях может оказаться удобнее эмулировать синхронные
сигналы. Для этого нужно создать два связанных каналом (pipe,
socketpair) процесса и принимать сигналы в одном из них, передавать
номер сигнала через канал в другой процесс, где и обрабатывать
сигнал в обычном цикле вокруг select(2)/poll(2).

Hе надо относиться к сигналам как к грому и молнии. В любой точке
аккуратно написанной программы всегда известно, какие сигналы сейчас
стоит обрабатывать, а какие -- нет. Соответственно, ненужные сигналы
блокируются или игнорируются, а позднее, когда в них возникнет
необходимость, разблокируются или снабжаются обработчиком.

Valentin Nechayev

unread,
Jun 28, 2003, 2:34:21 AM6/28/03
to

>>> Yar Tikhiy wrote:

YT> Самое удивительное в сложившейся ситуации -- что на самом деле
YT> правила "игры в сигналы" очень просты. Поэтому хотелось бы их четко
YT> сформулировать и указать на основные следствия из них. Для начала
YT> я попытаюсь это сделать для случая сигналов BSD в однонитевой среде.
YT> Комментарии и дополнения приветствуются.

Надо добавить, что это для асинхронной обработки сигналов. Хотя она и так
подразумевается в большинстве случаев.
Кстати, это не сигналы BSD. Это сигналы POSIX. BSD - это там, где sigvec().

YT> 4. Если процесс назначил сигналу собственный обработчик и сигнал
YT> должен быть доставлен, то будет вызван этот обработчик. Hа время
YT> вызова обработчика текущий сигнал блокируется, чтобы избежать
YT> зацикливания.

Если не было SA_NODEFER в спецификации обработчика.

YT> Правило 3
YT> Существует ровно один тип статических данных, sig_atomic_t, переменную
YT> которого обработчик сигнала может установить. Поведение приложения
YT> неопределено, если обработчик сигнала обращается к статическим
YT> данным любым другим способом.

Хотел бы я увидеть платформу, где это несправедливо для int или char. ;)

YT> Правило 4
YT> Если обработчик сигнала завершится возвратом, то текущая операция
YT> может быть продолжена или прервана, в зависимости от типа операции
YT> и значения флага SA_RESTART для этого сигнала. Всего есть три случая.
YT> 1. Если для текущего сигнала не установлен флаг SA_RESTART, то
YT> некоторые системные вызовы будут прерваны с ошибкой EINTR. Их
YT> список приведен в sigaction(2); это вызовы ввода-вывода и wait(2).

Их список там неполон, по крайней мере если судить по FreeBSD'шному ману.
Я вот с ходу решил проверить semctl(). Таки да:

eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH,
"semwait", 0);
suptr = NULL; /* sem_undo may have been reallocated */
if (eval != 0)
return(EINTR);

В мане его не было. Причём тут явный EINTR, а не ERESTART - то есть
даже при SA_RESTART перезапуска не будет.

YT> 2. Функции семейства sleep (sleep(3), nanosleep(2) и т.п.) будут
YT> перваны любым доставленным сигналом, в независимости от наличия у
YT> него флага SA_RESTART.

Везде?

YT> 3. Выполнение кода процесса и остальных системных вызовов будет
YT> продолжено.

Нет. Как минимум для select и poll SUS оговаривает возможность невозобновления,
и FreeBSD это делает.

/* poll is not restarted after signals... */
if (error == ERESTART)
error = EINTR;

YT> Следствия

YT> 2. [Из п. 2, с. 1] Из обработчика сигнала нельзя вызывать
YT> нереентерабельные библиотечные функции (н.б.ф.), если только
YT> устройством программы не гарантируется, что на момент _каждого_
YT> вызова _любой_ н.б.ф. все сигналы, чьи обработчики содержат вызовы
YT> _любых_ н.б.ф., не будут заблокированы.

Я бы добавил, что это будет называться синхронной доставкой сигналов;))
Так как без НБФ делать невозможно практически ничего, то для такого режима
работы придётся открывать короткие "окна" для доставки.

YT> 4. [Из п. 2, п. 3] Hеобходимо использовать модификатор volatile,
YT> чтобы указать компилятору на асинхронность изменения переменной типа
YT> sig_atomic_t.
YT> 5. [Из п. 3] Hе гарантируется, что обработчик сигнала может читать
YT> статическую переменную типа sig_atomic_t.

Есть такое. /me shocked;)) А какие основания для такого ограничения?
Почему коду в нормальной фазе выполнения можно читать такие переменные,
а в обработчике сигнала - нет?

YT> 6. [Из п. 2, п. 3] Можно использовать только простое присваивание
YT> переменным sig_atomic_t. В частности, над ними не следует использовать
YT> операции ++ и --. К примеру, на архитектурах RISC эти операции над
YT> ОЗУ не атомарны. Это касается как обработчиков сигналов, так и
YT> основного потока (конечно, если соответствующие сигналы не заблокированы
YT> на момент операций с переменной типа sig_atomic_t).

Ну, можно, конечно, использовать всякие atomic_inc. Где они есть.
Кстати, это относится и к чтению sig_atomic_t, если оно действительно
проблемно.

YT> 7. [Из п. 2, п. 3, с. 1] "Трюк" с возвратом из обработчика сигнала
YT> через longjmp(3), кочующий из книги в книгу (даже W.R.Stevens
YT> приводит его), на самом деле использовать нельзя. longjmp(3) не
YT> восстановит статических переменных libc и структуру кучи malloc(3)
YT> на момент вызова setjmp(3), а значит, он ничем не поможет в решении
YT> проблемы реентерабельности. Более того, стандарт C99 явно указывает
YT> на неопределенность результатов вызова longjmp(3) из обработчика
YT> сигнала.

Из этого не следует, что longjmp использовать совсем нельзя. Но его
использование в асинхронно доставленных сигналах сильно ограничивается.

YT> Практические соображения

YT> В сложных случаях может оказаться удобнее эмулировать синхронные
YT> сигналы. Для этого нужно создать два связанных каналом (pipe,
YT> socketpair) процесса и принимать сигналы в одном из них, передавать
YT> номер сигнала через канал в другой процесс, где и обрабатывать
YT> сигнал в обычном цикле вокруг select(2)/poll(2).

Слово "процесс" тут явно неуместно. Просто создать пайп и писать в него
нужные данные (не обязательно голый номер сигнала). В основном цикле
(если есть цикл) проверять и ожидать чтения.

YT> Hе надо относиться к сигналам как к грому и молнии. В любой точке
YT> аккуратно написанной программы всегда известно, какие сигналы сейчас
YT> стоит обрабатывать, а какие -- нет.

К сожалению, это неактуально из-за библиотек. Которые написаны неизвестно
как (даже если код есть, в нём копаться - себе дороже).

YT> Соответственно, ненужные сигналы
YT> блокируются или игнорируются, а позднее, когда в них возникнет
YT> необходимость, разблокируются или снабжаются обработчиком.

А вообще - текст неплохой. Причесать только надо.


-netch-

Lev Walkin

unread,
Jun 28, 2003, 3:50:11 AM6/28/03
to

Valentin Nechayev wrote:

> YT> Правило 3
> YT> Существует ровно один тип статических данных, sig_atomic_t, переменную
> YT> которого обработчик сигнала может установить. Поведение приложения
> YT> неопределено, если обработчик сигнала обращается к статическим
> YT> данным любым другим способом.
>
> Хотел бы я увидеть платформу, где это несправедливо для int или char. ;)


Intel x86.


--
Lev Walkin
v...@netli.com

Lev Walkin

unread,
Jun 28, 2003, 3:53:45 AM6/28/03
to


Pardon me. Если это действительно _статическая переменная_ типа char,
то тогда все в порядке. Другое дело, если там статический struct, например,
забитый char'ами - вот их как раз и нельзя использовать в качестве флагов.
С другой стороны, статический struct, набитый sig_atomic_t работать будет.


--
Lev Walkin
v...@netli.com

Valentin Nechayev

unread,
Jun 28, 2003, 1:54:55 PM6/28/03
to

>>> Lev Walkin wrote:

>> YT>> Существует ровно один тип статических данных, sig_atomic_t, переменную
>> YT>> которого обработчик сигнала может установить. Поведение приложения
>> YT>> неопределено, если обработчик сигнала обращается к статическим
>> YT>> данным любым другим способом.
>>> Хотел бы я увидеть платформу, где это несправедливо для int или char. ;)
>> Intel x86.

LW> Pardon me. Если это действительно _статическая переменная_ типа char,
LW> то тогда все в порядке. Другое дело, если там статический struct, например,
LW> забитый char'ами - вот их как раз и нельзя использовать в качестве флагов.

Всё равно непонятно. В чём разница между отдельно описанным char'ом
и элементом структуры?

LW> С другой стороны, статический struct, набитый sig_atomic_t работать будет.


-netch-

Lev Walkin

unread,
Jun 29, 2003, 2:35:17 AM6/29/03
to

Valentin Nechayev wrote:
>>>>Lev Walkin wrote:
>
>
>>>YT>> Существует ровно один тип статических данных, sig_atomic_t, переменную
>>>YT>> которого обработчик сигнала может установить. Поведение приложения
>>>YT>> неопределено, если обработчик сигнала обращается к статическим
>>>YT>> данным любым другим способом.
>>>
>>>>Хотел бы я увидеть платформу, где это несправедливо для int или char. ;)
>>>
>>>Intel x86.
>
> LW> Pardon me. Если это действительно _статическая переменная_ типа char,
> LW> то тогда все в порядке. Другое дело, если там статический struct, например,
> LW> забитый char'ами - вот их как раз и нельзя использовать в качестве флагов.
>
> Всё равно непонятно. В чём разница между отдельно описанным char'ом
> и элементом структуры?

Встречались такие оптимизации (gcc), которые рассматривают некоторые
виды структур (четыре char'а, например) как какой-то элементарный тип
(int), и присвоение _нескольких_ флагов в, казалось бы, определенном
порядке не только меняет порядок (что к дискуссии не относится, так
как никто порядок и не гарантировал), но и производит присвоения не
через movb, а через пляску вокруг shll/andl/orl. При этом используется
больше одной (видел две) инструкции на одно присвоение.

Короче, действительно, нужно делать

sig_atomic_t var;

и затем в обработчике делать что-то типа

var = 1;

Никаких ++ или использования структур данных, инкапсулирующих var этого
же или других типов.

> LW> С другой стороны, статический struct, набитый sig_atomic_t работать будет.
>
>
> -netch-


--
Lev Walkin
v...@netli.com

Kirill Frolov

unread,
Jun 29, 2003, 1:49:31 PM6/29/03
to
Hемедленно нажми на RESET, y...@comp.chem.msu.su!

On Fri, 27 Jun 03 17:55:13 +0400, y...@comp.chem.msu.su wrote:

y> Устав чинить программы с кривой обработкой сигналов, я написал некий
y> текст и теперь выношу его на суд общественности. Заранее спасибо за
y> комментарии :-)

А вот хотелось-бы самому получить комментарии по некоторым пунктам этого
текста. И желательно с кратенькими примерами где это необходимо, чтобы лучше
доходило. Думаю, не только мне было-бы интересно. Лично я думаю, что здесь
всё-же нагнали ненужного страху на пустом месте (longjmp & н.б.ф.).


y> Правило 3

y> Существует ровно один тип статических данных, sig_atomic_t, переменную
y> которого обработчик сигнала может установить. Поведение приложения
y> неопределено, если обработчик сигнала обращается к статическим
y> данным любым другим способом.

Это машинное слово читаемое или пишущееся за одну неделимую команду,
я так понимаю. Я кажется понял, что имелось ввиду здесь -- это то, что
обработчик может нарушить нормальное обращение к этой переменной из
основного потока программы или другого обработчика. Hо тогда утверждение
насчёт того, что обработчик может только установить, и только переменную
одного типа HЕКОРРЕКТHО. Хотя конечно, на факт одновременного обращения
к переменным стоит обращать особое внимание.

y> Следствия

y> 1. [Из п. 2] Из обработчика сигнала нельзя выполнять общий c другими
y> частями процесса нереентерабельный участок кода, если он не защищается
y> всякий раз путем блокировки соответствующих сигналов.

Hу почему нельзя? Только представлять что произойдёт нужно чётко.
Тоже самое касается и H.Б.Ф. Можно привести пример с longjmp вызванным
из обработчика -- надеюсь, никто не будет называть его некорректным?

y> 2. [Из п. 2, с. 1] Из обработчика сигнала нельзя вызывать
y> нереентерабельные библиотечные функции (н.б.ф.), если только
y> устройством программы не гарантируется, что на момент _каждого_
y> вызова _любой_ н.б.ф. все сигналы, чьи обработчики содержат вызовы
^^^^^^^^^^^^^^^^
y> _любых_ н.б.ф., не будут заблокированы.
^^^^^^^^^^^^^^^
Почему ЛЮБЫХ функций. Допустим, есть две HИКАК не связанных между собой
H.Б.Ф., и в программе и обработчике вызываются разные из них. В чём проблема?

y> Так как внутренние зависимости по статическим данным между н.б.ф.
y> полностью зависят от реализации, вложенный вызов _любой_ н.б.ф.
y> может привести к неопределенному поведению _всех_ н.б.ф.

Да, что-то в этом есть. Следует искать аналоги из thread-safe функций?

y> Список реентерабельных функций стандартизован, однако на практике
y> он может быть системно-зависим. То есть, наиболее безопасно полагать,
y> что все библиотечные функции нереентерабельны.

А как насчёт функций помеченных как thread-safe?

y> 5. [Из п. 3] Hе гарантируется, что обработчик сигнала может читать
y> статическую переменную типа sig_atomic_t.

Это почему ещё??? Вот это мне больше всего непонятно.
Именно типа sig_atomic_t?

y> 7. [Из п. 2, п. 3, с. 1] "Трюк" с возвратом из обработчика сигнала
y> через longjmp(3), кочующий из книги в книгу (даже W.R.Stevens
y> приводит его), на самом деле использовать нельзя.

:-( ... )

A как-же жить теперь дальше? Вообще нельзя?
Или же нельзя при неаккуратном использовании?

y> longjmp(3) не восстановит статических переменных libc
y> и структуру кучи malloc(3) на момент вызова setjmp(3),

Hе закроет файлов... не освободит любые другие ресурсы...
Hичего этого нигде и не обещалось -- я не вижу здесь проблемы. :-/

y> а значит, он ничем не поможет в решении проблемы реентерабельности.

А может её и нет? Может мне не нужна реентрабельность, тогда почему
мне нельзя?

Меня только malloc навёл на мысль, что... а сам malloc внутри библиотеки
ведь как-то защищается от сигналов. И другие библиотечные функции тоже.
Тогда откуда это, почему нельзя делать longjmp и вызов H.Б.Ф из обработчика?

y> Более того, стандарт C99 явно указывает на неопределенность
y> результатов вызова longjmp(3) из обработчика сигнала.

А с чем это связано и в чём эта неопределённость выражается?

y> 9. [Из п. 2, с. 1] Для завершения процесса из обработчика сигнала
y> следует использовать _exit(2) (POSIX) или _Exit() (C99), которые
y> не имеют побочных эффектов, в отличие от exit(3).

Какие ещё побочные эффекты? Вызов H.Б.Ф. это побочный эффект?

А покажите реальный пример, где longjmp, н.б.ф. и exit не будут работать.

Valentin Nechayev

unread,
Jun 30, 2003, 3:18:10 AM6/30/03
to

>>> Kirill Frolov wrote:

y>> Правило 3

y>> Существует ровно один тип статических данных, sig_atomic_t, переменную
y>> которого обработчик сигнала может установить. Поведение приложения
y>> неопределено, если обработчик сигнала обращается к статическим
y>> данным любым другим способом.

KF> Это машинное слово читаемое или пишущееся за одну неделимую команду,
KF> я так понимаю. Я кажется понял, что имелось ввиду здесь -- это то, что
KF> обработчик может нарушить нормальное обращение к этой переменной из
KF> основного потока программы или другого обработчика. Hо тогда утверждение
KF> насчёт того, что обработчик может только установить, и только переменную
KF> одного типа HЕКОРРЕКТHО. Хотя конечно, на факт одновременного обращения
KF> к переменным стоит обращать особое внимание.

Фактически, как я понял, это у Яра была прямая цитата из SUSv3:

==={{{ functions/sigaction.html
If the signal occurs other than as the result of calling abort(),
kill(), or raise(), the behavior is undefined if the signal handler
calls any function in the standard library other than one of the
functions listed in the table above or refers to any object with
static storage duration other than by assigning a value to a static
storage duration variable of type volatile sig_atomic_t.
Furthermore, if such a call fails, the value of errno is
unspecified.
===}}}

Здесь говорится именно про присвоение. В то же время:

==={{{ basedefs/signal.h.html
sig_atomic_t
Possibly volatile-qualified integer type of an object that can
be accessed as an atomic entity, even in the presence of
asynchronous interrupts.
===}}}

Здесь уже про _доступ_.
Спрошу-ка я в comp.unix.programmer...

y>> Следствия

y>> 1. [Из п. 2] Из обработчика сигнала нельзя выполнять общий c другими
y>> частями процесса нереентерабельный участок кода, если он не защищается
y>> всякий раз путем блокировки соответствующих сигналов.

KF> Hу почему нельзя? Только представлять что произойдёт нужно чётко.
KF> Тоже самое касается и H.Б.Ф. Можно привести пример с longjmp вызванным
KF> из обработчика -- надеюсь, никто не будет называть его некорректным?

1. Ниже было в оригинале про longjmp.
2. Речь про *нереентерабельный* участок кода. На самом деле, надо было
говорить про произвольные участки кода, которые доступаются к данным,
требующим сериализации доступа. Те, кто возился со всякими семафорами,
поймут;))

y>> 2. [Из п. 2, с. 1] Из обработчика сигнала нельзя вызывать
y>> нереентерабельные библиотечные функции (н.б.ф.), если только
y>> устройством программы не гарантируется, что на момент _каждого_
y>> вызова _любой_ н.б.ф. все сигналы, чьи обработчики содержат вызовы

KF> ^^^^^^^^^^^^^^^^


y>> _любых_ н.б.ф., не будут заблокированы.

KF> ^^^^^^^^^^^^^^^
KF> Почему ЛЮБЫХ функций. Допустим, есть две HИКАК не связанных между собой
KF> H.Б.Ф., и в программе и обработчике вызываются разные из них. В чём проблема?

1. Ты *не знаешь* гарантированно, для большинства случаев и *для всех
возможных платформ* (а не только на одной отдельно взятой glibc-1.2.3.4.5.6),
будет ли связь между ними.
2. Ты задолбёшься подсчитывать в более-менее реальном случае, какие пересечения
между группами функций требует какой маскировки сигналов в каком месте кода.
А код у тебя будет таким, что 99% вызовов будет sigprocmask().

Пример (из того же Залевского, была ссылка): syslog() и malloc()/free().
Что общего между ними? А то, что syslog() зовёт (в BSD варианте) fwopen().
А fwopen() вызывает malloc() для получения нового FILE объекта.
А пусть при этом основной код сидел в malloc(). Что будет? Каша будет
вместо кучи.

y>> Так как внутренние зависимости по статическим данным между н.б.ф.
y>> полностью зависят от реализации, вложенный вызов _любой_ н.б.ф.
y>> может привести к неопределенному поведению _всех_ н.б.ф.

KF> Да, что-то в этом есть. Следует искать аналоги из thread-safe функций?

Thread-safe и асинхронная "вертикальная" реентерабельность - достаточно
разные вещи, чтобы первое было недостаточным для второго.
Тот же malloc() вполне thread-safe (в любой нормальной реализации), но он
совершенно не рассчитан на прерывание себя сигналом и malloc() в обработчике.

y>> Список реентерабельных функций стандартизован, однако на практике
y>> он может быть системно-зависим. То есть, наиболее безопасно полагать,
y>> что все библиотечные функции нереентерабельны.

KF> А как насчёт функций помеченных как thread-safe?

Нет. См. выше.

y>> 5. [Из п. 3] Hе гарантируется, что обработчик сигнала может читать
y>> статическую переменную типа sig_atomic_t.

KF> Это почему ещё??? Вот это мне больше всего непонятно.
KF> Именно типа sig_atomic_t?

Мне тоже непонятно. См, выше.

y>> 7. [Из п. 2, п. 3, с. 1] "Трюк" с возвратом из обработчика сигнала
y>> через longjmp(3), кочующий из книги в книгу (даже W.R.Stevens
y>> приводит его), на самом деле использовать нельзя.

KF> :-( ... )

KF> A как-же жить теперь дальше? Вообще нельзя?
KF> Или же нельзя при неаккуратном использовании?

y>> longjmp(3) не восстановит статических переменных libc
y>> и структуру кучи malloc(3) на момент вызова setjmp(3),

KF> Hе закроет файлов... не освободит любые другие ресурсы...
KF> Hичего этого нигде и не обещалось -- я не вижу здесь проблемы. :-/

Есть разница между простым неосвобождением ресурсов и оставлением
их в неконсистентном состоянии. Тут говорится, что longjmp() даёт и второе
тоже.

y>> а значит, он ничем не поможет в решении проблемы реентерабельности.

KF> А может её и нет? Может мне не нужна реентрабельность, тогда почему
KF> мне нельзя?

Тогда у тебя программа будет мереть неизвестно отчего в произвольных местах.
Ты этого хочешь? Ну тогда вперёд.

KF> Меня только malloc навёл на мысль, что... а сам malloc внутри библиотеки
KF> ведь как-то защищается от сигналов.

Нет. Или по крайней мере не везде.

KF> И другие библиотечные функции тоже.

Какие? Где? Когда? Зачем?

y>> 9. [Из п. 2, с. 1] Для завершения процесса из обработчика сигнала
y>> следует использовать _exit(2) (POSIX) или _Exit() (C99), которые
y>> не имеют побочных эффектов, в отличие от exit(3).

KF> Какие ещё побочные эффекты? Вызов H.Б.Ф. это побочный эффект?
KF> А покажите реальный пример, где longjmp, н.б.ф. и exit не будут работать.

Там была ссылка. Ты зря по ней не сходил.
http://razor.bindview.com/publish/papers/signals.txt


-netch-

virtan

unread,
Jun 30, 2003, 6:19:46 AM6/30/03
to
Lev Walkin wrote:
> Встречались такие оптимизации (gcc), которые рассматривают некоторые
> виды структур (четыре char'а, например) как какой-то элементарный тип
> (int), и присвоение _нескольких_ флагов в, казалось бы, определенном
> порядке не только меняет порядок (что к дискуссии не относится, так
> как никто порядок и не гарантировал), но и производит присвоения не
> через movb, а через пляску вокруг shll/andl/orl. При этом используется
> больше одной (видел две) инструкции на одно присвоение.
>
> Короче, действительно, нужно делать
>
> sig_atomic_t var;
>
> и затем в обработчике делать что-то типа
>
> var = 1;
>
> Никаких ++ или использования структур данных, инкапсулирующих var этого
> же или других типов.

А почему запрещено использование операций со статичными переменными,
реализованных через более чем одну ассемблерную операцию?
Ведь если у нас одна нить и случился вызова обработчика сигнала, то
гарантировано, что никто другой не обратиться к этой переменной до
окончания работы обработчика.
Я подразумеваю, что другие обработчики используют другие флаги.

--
virtan
vir...@virtan.com // BSrLblbG
http://www.virtan.com

Lev Walkin

unread,
Jun 30, 2003, 6:43:11 AM6/30/03
to

Допустим, у нас архитектура не имеет incl. То есть, инкрементирование
в обработчике может быть сделано примерно так:

movl %eax, xyz(%ebp) # Загрузили в регистр (A1)
addl $1, %eax # Инкрементировали на 1 (B1)
movl xyz(%ebp), %eax # Слили обратно (C1)

При этом, если в теле основной программы есть такое:

if(flag) {
do_something();
flag--;
}

То, будучи реализовано ("flag--") через

movl %eax, xyz(%ebp) # Загрузили в регистр (*A2)
subl $1, %eax # Декрементировали на 1
movl xyz(%ebp), %eax # Слили обратно

Мы можем задействовать следующую граблю:

1. Допустим, процесс видит, что flag установлен в 1;
2. Он делает что-то в do_something();
3. Так как do_something() - это функция (значит, у нас
нет кэшированного в регистре значения flag, так как
перед вызовом функций компилятор сбрасывает и обесцвечивает
регистры), либо так как мы объявили flag как volatile,
либо даже если все есть закешированное в регистрах
(чего не может быть в этом коде при нормальном компиляторе),
то командой (*A2) мы загрузим единицу в регистр;
4. После этого запускается обработчик и декрементирует 1 до 0
операциями A1-C1;
5. Основной код получает управление и делает декремент его
тухлой единицы, сгнившей в регистре;
6. Основной код сохраняет 0 в памяти.

Что мы получили? То, что после do_something() мы получили
сигнал, но не заметили этого. Больше до следующего сигнала
мы в do_something() не придем. Итог - потеря одного сигнала.

В данном примере, наверное, неважно, пропустили ли мы лишний
сигнал, или нет. Но всегда можно представить программу,
в которой приход каждого сигнала имеет свой смысл и
пропускать их нельзя.


--
Lev Walkin
v...@netli.com

virtan

unread,
Jun 30, 2003, 10:04:23 AM6/30/03
to
Lev Walkin wrote:

> movl %eax, xyz(%ebp) # Загрузили в регистр (A1)
> addl $1, %eax # Инкрементировали на 1 (B1)
> movl xyz(%ebp), %eax # Слили обратно (C1)

> if(flag) {
> do_something();
> flag--;
> }

> movl %eax, xyz(%ebp) # Загрузили в регистр (*A2)
> subl $1, %eax # Декрементировали на 1
> movl xyz(%ebp), %eax # Слили обратно
>
> Мы можем задействовать следующую граблю:
>
> 1. Допустим, процесс видит, что flag установлен в 1;
> 2. Он делает что-то в do_something();
> 3. Так как do_something() - это функция (значит, у нас
> нет кэшированного в регистре значения flag, так как
> перед вызовом функций компилятор сбрасывает и обесцвечивает
> регистры), либо так как мы объявили flag как volatile,
> либо даже если все есть закешированное в регистрах
> (чего не может быть в этом коде при нормальном компиляторе),
> то командой (*A2) мы загрузим единицу в регистр;
> 4. После этого запускается обработчик и декрементирует 1 до 0
> операциями A1-C1;
> 5. Основной код получает управление и делает декремент его
> тухлой единицы, сгнившей в регистре;
> 6. Основной код сохраняет 0 в памяти.
>

Точно, я пропустил неатомарность операции в основном коде программы.
Однако на работу с переменной в обработчике данная грабля ограничений не
добавляет.
Кроме того, а при использовании sig_atomic_t что меняется ?
Как защищается ее декрементация в основном коде?
Оберткой из запрета прерываний на время исполнения ?

Valentin Nechayev

unread,
Jun 30, 2003, 4:30:28 PM6/30/03
to

>>> virtan wrote:

>> 4. После этого запускается обработчик и декрементирует 1 до 0
>> операциями A1-C1;
>> 5. Основной код получает управление и делает декремент его
>> тухлой единицы, сгнившей в регистре;
>> 6. Основной код сохраняет 0 в памяти.

v> Точно, я пропустил неатомарность операции в основном коде программы.
v> Однако на работу с переменной в обработчике данная грабля ограничений не
v> добавляет.
v> Кроме того, а при использовании sig_atomic_t что меняется ?
v> Как защищается ее декрементация в основном коде?

Без блокировки сигналов - никак. Разрешается только чтение и запись.
Без предположений об атомарности действий между чтением и записью.
Можно делать действия по атомарной замене, но, к сожалению, стандартных
функций для этого, как виндовых InterlockedExchange(), не предусмотрено.
Если можешь сам сделать для какой-то конкретной аппаратной платформы,
или выдрать откуда-то (в основном это реализации SMP в ядрах и тредовых
библиотек) - OK. Нет - опаньки, сосите лапу в берлоге.

v> Оберткой из запрета прерываний на время исполнения ?

Да, как вариант. Но это явно дороже атомарной замены на уровне действий
для платформы (как правило, sigprocmask() - сисколл, со всеми соответствующими
последствиями в виде прохождения через системные шлюзы сисколлов).


-netch-

Vladimir V. Ivanov

unread,
Jul 1, 2003, 3:57:36 AM7/1/03
to
On Sat, 28 Jun 2003 06:34:21 +0000 (UTC)
Valentin Nechayev <ne...@segfault.kiev.ua> wrote:

> YT> В сложных случаях может оказаться удобнее эмулировать синхронные
> YT> сигналы. Для этого нужно создать два связанных каналом (pipe,
> YT> socketpair) процесса и принимать сигналы в одном из них,

> YT> передавать


> YT> номер сигнала через канал в другой процесс, где и обрабатывать
> YT> сигнал в обычном цикле вокруг select(2)/poll(2).
>
> Слово "процесс" тут явно неуместно. Просто создать пайп и писать в >
> него нужные данные (не обязательно голый номер сигнала). В основном
> цикле (если есть цикл) проверять и ожидать чтения.

Недавно реализовывал код обработки сигналов по такой схеме и вот что
подумалось: а deadlock'а на write() в обработчике сигнала не случится ?
Буфер пайпа, по идее, ведь ограничен...
Я (так, на всякий пожарный) перевёл дескриптор в неблокирующий режим,
но, в таком случае, при переполнении буфера пайпа возможна потеря данных
о сигнале или нужен какой-то дополнительный механизм оповещения для
этого специального случая.

В моём случае, конечно, это некритично.

Чтобы не терять информацию о важных сигналах можно разделить
сигналы по группам и для каждой группы создавать свой пайп
(тривиальный случай: 1 сигнал - 1 пайп).

Или (если приложение многонитевое) ловить сигнал в одной нити - а
обрабатывать в другой.

Мои опасения не напрасны ?

Для Яра:
В "правилах игры...", наверное стоит упомянуть, что нужно быть
готовым к тому, что обработка одного сигнала может быть
прервана другими сигналами, а также то, что этим можно
управлять.

Regards,
Vladimir Ivanov

Yar Tikhiy

unread,
Jul 1, 2003, 9:04:52 AM7/1/03
to
Прежде всего, спасибо за конструктивные замечания :-)

Valentin Nechayev <ne...@segfault.kiev.ua> wrote in
<2003062806...@iv.nn.kiev.ua>:

YT>> Правило 3
YT>> Существует ровно один тип статических данных, sig_atomic_t, переменную
YT>> которого обработчик сигнала может установить. Поведение приложения
YT>> неопределено, если обработчик сигнала обращается к статическим
YT>> данным любым другим способом.

VN> Хотел бы я увидеть платформу, где это несправедливо для int или char. ;)

Сегодня нет -- завтра будет ;-)

YT>> Правило 4
YT>> Если обработчик сигнала завершится возвратом, то текущая операция
YT>> может быть продолжена или прервана, в зависимости от типа операции
YT>> и значения флага SA_RESTART для этого сигнала. Всего есть три случая.
YT>> 1. Если для текущего сигнала не установлен флаг SA_RESTART, то
YT>> некоторые системные вызовы будут прерваны с ошибкой EINTR. Их
YT>> список приведен в sigaction(2); это вызовы ввода-вывода и wait(2).

VN> Их список там неполон, по крайней мере если судить по FreeBSD'шному ману.
VN> Я вот с ходу решил проверить semctl(). Таки да:

Любопытно: SUSv2 списка не приводит, но говорит, что прервать можно
любой вызов, у которого в [ERRORS] есть EINTR. Hо у semctl() такая
ошибка не описана. В общем, все как обычно :-)

YT>> 2. Функции семейства sleep (sleep(3), nanosleep(2) и т.п.) будут
YT>> перваны любым доставленным сигналом, в независимости от наличия у
YT>> него флага SA_RESTART.

VN> Везде?

Я готов присоединиться к этому вопросу.

YT>> 7. [Из п. 2, п. 3, с. 1] "Трюк" с возвратом из обработчика сигнала
YT>> через longjmp(3), кочующий из книги в книгу (даже W.R.Stevens
YT>> приводит его), на самом деле использовать нельзя. longjmp(3) не
YT>> восстановит статических переменных libc и структуру кучи malloc(3)
YT>> на момент вызова setjmp(3), а значит, он ничем не поможет в решении
YT>> проблемы реентерабельности. Более того, стандарт C99 явно указывает
YT>> на неопределенность результатов вызова longjmp(3) из обработчика
YT>> сигнала.

VN> Из этого не следует, что longjmp использовать совсем нельзя. Hо его
VN> использование в асинхронно доставленных сигналах сильно ограничивается.

В comp.unix.programmer, кажется, обсуждали любопытную проблему:
программа, которая выходила из обработчика SIGINT по longjmp(3),
отлично собиралась под Win32, но при запуске падала по GPF или
чему-то подобному. Оказалось, что в Win32 при нажатии ^C обработчик
SIGINT вызывается в отдельном thread.

Коль сторонники Unix никого не хотят победить, но мечтают о полной
и безоговорочной переносимости кода, приходится учитывать и такие
выкрутасы. Хотя, конечно, писать программы в точном согласии со
всеми возможными стандартами становится сложновато. Да здравствует
autoconf :-)

--
Yar

Yar Tikhiy

unread,
Jul 1, 2003, 9:20:40 AM7/1/03
to

YT>> Hе надо относиться к сигналам как к грому и молнии. В любой точке


YT>> аккуратно написанной программы всегда известно, какие сигналы сейчас
YT>> стоит обрабатывать, а какие -- нет.

VN> К сожалению, это неактуально из-за библиотек. Которые написаны неизвестно
VN> как (даже если код есть, в нём копаться - себе дороже).

Я имел ввиду, что или мы сигнал с пользой для себя обрабатываем,
или блокируем, или точно знаем, почему нам в данный момент все равно :-)
Иначе не миновать, как минимум, проблем с безопасностью.

--
Yar

Yar Tikhiy

unread,
Jul 1, 2003, 10:24:54 AM7/1/03
to

YT>> Правило 4


YT>> Если обработчик сигнала завершится возвратом, то текущая операция
YT>> может быть продолжена или прервана, в зависимости от типа операции
YT>> и значения флага SA_RESTART для этого сигнала. Всего есть три случая.
YT>> 1. Если для текущего сигнала не установлен флаг SA_RESTART, то
YT>> некоторые системные вызовы будут прерваны с ошибкой EINTR. Их
YT>> список приведен в sigaction(2); это вызовы ввода-вывода и wait(2).

VN> Их список там неполон, по крайней мере если судить по FreeBSD'шному ману.
VN> Я вот с ходу решил проверить semctl(). Таки да:

VN> eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH,
VN> "semwait", 0);
VN> suptr = NULL; /* sem_undo may have been reallocated */
VN> if (eval != 0)
VN> return(EINTR);

VN> В мане его не было. Причём тут явный EINTR, а не ERESTART - то есть
VN> даже при SA_RESTART перезапуска не будет.

Кстати, боюсь, что этот tsleep() вызывается не из semctl(), а из
semop(); а semop() может возвращать EINTR. Другое дело, что обработкой
ERESTART здесь и не пахнет...

--
Yar

Yar Tikhiy

unread,
Jul 2, 2003, 5:42:07 AM7/2/03
to
Hi Kirill,

Kirill Frolov <Kirill...@p8.f1123.n5030.z2.fidonet.org> wrote in
<10603...@pc.zxlink>:

KF> А вот хотелось-бы самому получить комментарии по некоторым пунктам этого
KF> текста. И желательно с кратенькими примерами где это необходимо, чтобы
KF> лучше
KF> доходило. Думаю, не только мне было-бы интересно. Лично я думаю, что здесь
KF> всё-же нагнали ненужного страху на пустом месте (longjmp & н.б.ф.).

Спасибо за вопросы: они весьма по делу и помогли уточнить "Правила..."
Спасибо также Валентину Hечаеву: он уже прокомментировал все за меня :-)
Мне осталось только добавить свои соображения.

y>> Существует ровно один тип статических данных, sig_atomic_t, переменную
y>> которого обработчик сигнала может установить. Поведение приложения
y>> неопределено, если обработчик сигнала обращается к статическим
y>> данным любым другим способом.

KF> Это машинное слово читаемое или пишущееся за одну неделимую команду,
KF> я так понимаю. Я кажется понял, что имелось ввиду здесь -- это то, что
KF> обработчик может нарушить нормальное обращение к этой переменной из
KF> основного потока программы или другого обработчика. Hо тогда утверждение
KF> насчёт того, что обработчик может только установить, и только переменную
KF> одного типа HЕКОРРЕКТHО. Хотя конечно, на факт одновременного обращения
KF> к переменным стоит обращать особое внимание.

Мы так привыкли к архитектуре с общей памятью, что уже забыли о
других возможностях. Hо я не удивлюсь, если в какой-нибудь бортовой
ЭВМ стиральной машины обработчик сигнала не сможет просто так
обращаться к памяти, где лежат статические переменные, и потребуется
особый трюк со стороны компилятора и ОС, чтобы записать туда хотя
бы sig_atomic_t.

Кстати, из того же корня растет фраза SUS, что если вызванная из
обработчика б.ф. вернула ошибку, то значение errno неопределено.
(Hадо это в "Правила..." добавить.)

y>> 1. [Из п. 2] Из обработчика сигнала нельзя выполнять общий c другими
y>> частями процесса нереентерабельный участок кода, если он не защищается
y>> всякий раз путем блокировки соответствующих сигналов.

KF> Hу почему нельзя? Только представлять что произойдёт нужно чётко.

А как ты собираешься достичь четкости представления, если сигналы
доставляются асинхронно, т.е., неожиданно? Четкость здесь одна:
заблокировали сигнал -- спим спокойно...

KF> Тоже самое касается и H.Б.Ф. Можно привести пример с longjmp вызванным
KF> из обработчика -- надеюсь, никто не будет называть его некорректным?

Про longjmp() я уже приводил пример из Win32. А какой Unix developer
не мечтает, чтобы его программы работали под Win32? ;-) Тем более,
C99 похоронил longjmp() из обработчика.

y>> 2. [Из п. 2, с. 1] Из обработчика сигнала нельзя вызывать
y>> нереентерабельные библиотечные функции (н.б.ф.), если только
y>> устройством программы не гарантируется, что на момент _каждого_
y>> вызова _любой_ н.б.ф. все сигналы, чьи обработчики содержат вызовы

KF> ^^^^^^^^^^^^^^^^


y>> _любых_ н.б.ф., не будут заблокированы.

KF> ^^^^^^^^^^^^^^^
KF> Почему ЛЮБЫХ функций. Допустим, есть две HИКАК не связанных между собой
KF> H.Б.Ф., и в программе и обработчике вызываются разные из них. В чём
KF> проблема?

А как ты узнаешь, что они не связаны? Скажем даже сильнее: если
ты сегодня найдешь в системе FooUnix пару независимых б.ф., то
завтра твою программу соберут под BarUnix, где эти функции дружно
вызывают местный кривой и нереентерабельный malloc().

y>> Так как внутренние зависимости по статическим данным между н.б.ф.
y>> полностью зависят от реализации, вложенный вызов _любой_ н.б.ф.
y>> может привести к неопределенному поведению _всех_ н.б.ф.

KF> Да, что-то в этом есть. Следует искать аналоги из thread-safe функций?

y>> Список реентерабельных функций стандартизован, однако на практике
y>> он может быть системно-зависим. То есть, наиболее безопасно полагать,
y>> что все библиотечные функции нереентерабельны.

KF> А как насчёт функций помеченных как thread-safe?

Thread-safe -- это функции, которые не хранят состояние внутри себя.
Пример: strtok() vs. strtok_r(). Hо не факт, что strtok_r() можно
безболезненно прервать сигналом и вызвать еще раз.

y>> 5. [Из п. 3] Hе гарантируется, что обработчик сигнала может читать
y>> статическую переменную типа sig_atomic_t.

KF> Это почему ещё??? Вот это мне больше всего непонятно.
KF> Именно типа sig_atomic_t?

Я имел ввиду, что другие static переменные он не может читать и
подавно. Hадо эту фразу видоизменить для ясности...

y>> 7. [Из п. 2, п. 3, с. 1] "Трюк" с возвратом из обработчика сигнала
y>> через longjmp(3), кочующий из книги в книгу (даже W.R.Stevens
y>> приводит его), на самом деле использовать нельзя.

KF> A как-же жить теперь дальше? Вообще нельзя?
KF> Или же нельзя при неаккуратном использовании?

Согласно C99, вообще нельзя. В реальности, этот longjmp() ровным
счетом ничего не добавляет. Вообще. И зачем он нужен тогда?

y>> longjmp(3) не восстановит статических переменных libc
y>> и структуру кучи malloc(3) на момент вызова setjmp(3),

KF> Hе закроет файлов... не освободит любые другие ресурсы...
KF> Hичего этого нигде и не обещалось -- я не вижу здесь проблемы. :-/

Дело не в освобождении ресурсов, а в возврате к некоему состоянию,
из которого дальше безопасно вызывать н.б.ф. А возвращает к нему
только return, если непонятно, в какой момент вызван обработчик
(еще раз вспомним об асинхронности сигналов).

y>> Более того, стандарт C99 явно указывает на неопределенность
y>> результатов вызова longjmp(3) из обработчика сигнала.

KF> А с чем это связано и в чём эта неопределённость выражается?

Hапример, в том, что в некоторых средах переход от контекста
обработчика к контексту основного потока не может быть выполнен
простым longjmp() (см. уже набивший, наверное, оскомину пример про
эмуляцию SIGINT в Win32).

y>> 9. [Из п. 2, с. 1] Для завершения процесса из обработчика сигнала
y>> следует использовать _exit(2) (POSIX) или _Exit() (C99), которые
y>> не имеют побочных эффектов, в отличие от exit(3).

KF> Какие ещё побочные эффекты? Вызов H.Б.Ф. это побочный эффект?

Да, именно вызов н.б.ф. и приводит к побочным эффектам. А побочные
эффекты в данном случае -- это когда помимо выполнения основной
задачи функция изменяет какие-то статические данные.

KF> А покажите реальный пример, где longjmp, н.б.ф. и exit не будут
KF> работать.

H.б.ф. долгое время вызывались в BSD ftpd, из-за чего тот с весьма
заметной вероятностью падал в корку, стоило в клиенте нажать ^C при
передаче файла. (TELNET IAC перед ABOR передается как urgent data,
что приводит к SIGURG.)

exit(3) ничем не отличается от остальных н.б.ф.

longjmp(3) -- см. мой любимый пример :-)

--
Yar

ig...@paco.net

unread,
Jul 2, 2003, 7:19:11 AM7/2/03
to

Yar Tikhiy <y...@comp.chem.msu.su> wrote:
> Hi Kirill,


>
> y>> Список реентерабельных функций стандартизован, однако на практике
> y>> он может быть системно-зависим. То есть, наиболее безопасно полагать,
> y>> что все библиотечные функции нереентерабельны.
>
> KF> А как насчёт функций помеченных как thread-safe?
>
> Thread-safe -- это функции, которые не хранят состояние внутри себя.
> Пример: strtok() vs. strtok_r(). Hо не факт, что strtok_r() можно
> безболезненно прервать сигналом и вызвать еще раз.

Такие функции (безопасно вызываемые из обработчика сигналов) называются
Async-Signal-Safe.

Для solaris:

Async-Signal-Safe
Async-Signal-Safe refers to particular library rou-
tines that can be safely called from a signal handler.
A thread that is executing an Async-Signal-Safe rou-
tine will not deadlock with itself if interrupted by a
signal. Signals are only a problem for MT-Safe rou-
tines that acquire locks.

>>> MT-Safe не означает Async-Signal-Safe

Signals are disabled when locks are acquired in
Async-Signal-Safe routines. This prevents a signal
handler that might acquire the same lock from being
called. The list of Async-Signal-Safe functions
includes:


_exit access aio_error
aio_return aio_suspend alarm
cfgetispeed cfgetospeed cfsetispeed
cfsetospeed chdir chmod
chown clock_gettime close
creat dup dup2
execle execve fcntl
fdatasync fork fstat
fsync getegid geteuid
getgid getgroups getpgrp
getpid getppid getuid
kill link lseek
mkdir mkfifo open
pathconf pause pipe
read rename rmdir
sem_post sema_post setgid
setpgid setsid setuid
sigaction sigaddset sigdelset
sigemptyset sigfillset sigismember
sigpending sigprocmask sigqueue
sigsuspend sleep stat
sysconf tcdrain tcflow
tcflush tcgetattr tcgetpgrp
tcsendbreak tcsetattr tcsetpgrp
thr_kill thr_sigsetmask time
timer_getoverrun timer_gettime timer_settime
times umask uname
unlink utime wait
waitpid write


--
Igor Khasilev |
PACO Links, igor at paco dot net |

Vladimir N. Oleynik

unread,
Jul 3, 2003, 3:45:06 AM7/3/03
to
Здарофъ, Yar


> KF> А как насчёт функций помеченных как thread-safe?
>
> Thread-safe -- это функции, которые не хранят состояние внутри себя.

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

> KF> A как-же жить теперь дальше? Вообще нельзя?
> KF> Или же нельзя при неаккуратном использовании?
>
> Согласно C99, вообще нельзя.

Ну мода, что поделаешь. Типа "мы лучше знаем что вам нужно,
можно и нельзя".

> В реальности, этот longjmp() ровным
> счетом ничего не добавляет. Вообще.

Сильно... Я могу допустить, что на неких платформах longjmp()
из обработчика даст проблемы. Но для примера создание
машины состояний с обработчиком фатальных ошибок longjmp()
даст фору запросто, другое дело, это надо УМЕТЬ делать.

> Дело не в освобождении ресурсов, а в возврате к некоему состоянию,
> из которого дальше безопасно вызывать н.б.ф. А возвращает к нему
> только return, если непонятно, в какой момент вызван обработчик
> (еще раз вспомним об асинхронности сигналов).

Зачем страху нагонять? Не засирайте стек и обеспечте, чтоб
одна и таже структура данных не менялась асинхронно и
все будет работать ;-)

> Hапример, в том, что в некоторых средах переход от контекста
> обработчика к контексту основного потока не может быть выполнен
> простым longjmp() (см. уже набивший, наверное, оскомину пример про
> эмуляцию SIGINT в Win32).

Что доказывает только кривость этой эмуляции, а longjmp()
чем вам виноват.

> Да, именно вызов н.б.ф. и приводит к побочным эффектам. А побочные

Да ясен пень, что если нам нужен флаг, который может меняться
асинхронно, то его значение нельзя кешировать. То что могут быть
проблемы с volatile у компиляторов никак не доказательство, что
надо бросить 20-летнюю практику пользования сигналами:
Это работает, так как по идеологии - нефиг генерировать сигналы
с огромной скоростью, это по сути для единичных и зачастую фатальных
ситуаций. Их ждем в любой момент, далее взводим флаг и когда надо его
обрабатываем. А то что сбросив его мы можем один раз потерять, то
это ничем не отличается от потери быстро-быстро нагенеренных
когда работает обработчик. Ибо - нефиг.
Надо осуществить обмен - делайте pipe, socket, IPC...

> эффекты в данном случае -- это когда помимо выполнения основной
> задачи функция изменяет какие-то статические данные.

> exit(3) ничем не отличается от остальных н.б.ф.

Да вот только это называется не статические данные ;-)
Это внутренние данные, обрабатываемые не атомарно.
fflush() вначале сбрасывает буфер, потом помечает, что он сброшен, потом
помечается FILE * как закрытый. Если прервать и вызвать повторно,
то могло произойти двойной fflush. Выбирая tread_safe вариант, каждая
структура FILE * до изменений блокируется, так что ничего страшного
произойти не может.


--w
vodz

Mykola Dzham

unread,
Jul 3, 2003, 6:13:14 AM7/3/03
to
Yar Tikhiy (y...@comp.chem.msu.su):

> Правило 3
>
> Существует ровно один тип статических данных, sig_atomic_t, переменную
> которого обработчик сигнала может установить. Поведение приложения
> неопределено, если обработчик сигнала обращается к статическим
> данным любым другим способом.

А вот если делать обработчик сигнала например в перловом скрипте,
то как там с подобным ограничением поступать?

--
LEFT-UANIC

Lev Walkin

unread,
Jul 3, 2003, 7:24:16 AM7/3/03
to


Plug and pray...
Хорошо, что Perl не работает на моем тостере.


=== pl source code ===
$SIG{'ALRM'} = \&handler;

$\="\n";

alarm(1);
sleep(2);
alarm(1);
sleep(2);

print 'exit';

sub handler() {
print 'alarm';
}
=== pl source code ===

=== ktrace perl pl ===
2694 perl CALL nanosleep(0xbfbff668,0xbfbff660)
2694 perl PSIG SIGALRM caught handler=0x280be470 mask=0x0 code=0x0
2694 perl RET nanosleep -1 errno 4 Interrupted system call
2694 perl CALL fstat(0x1,0xbfbff1a4)
2694 perl RET fstat 0
2694 perl CALL ioctl(0x1,TIOCGETA,0xbfbff1d8)
2694 perl RET ioctl 0
2694 perl CALL write(0x1,0x805c000,0x6)
2694 perl GIO fd 1 wrote 6 bytes
"alarm
"
2694 perl RET write 6
2694 perl CALL sigreturn(0xbfbff4b0)
2694 perl RET sigreturn JUSTRETURN
2694 perl CALL gettimeofday(0xbfbff668,0)
2694 perl RET gettimeofday 0
2694 perl CALL setitimer(0,0xbfbff670,0xbfbff660)
2694 perl RET setitimer 0
2694 perl CALL gettimeofday(0xbfbff668,0)
2694 perl RET gettimeofday 0
2694 perl CALL nanosleep(0xbfbff668,0xbfbff660)
2694 perl PSIG SIGALRM caught handler=0x280be470 mask=0x0 code=0x0
2694 perl RET nanosleep -1 errno 4 Interrupted system call
2694 perl CALL write(0x1,0x805c000,0x6)
2694 perl GIO fd 1 wrote 6 bytes
"alarm
"
2694 perl RET write 6
2694 perl CALL sigreturn(0xbfbff4b0)
2694 perl RET sigreturn JUSTRETURN
2694 perl CALL gettimeofday(0xbfbff668,0)
2694 perl RET gettimeofday 0
2694 perl CALL write(0x1,0x805c000,0x5)
2694 perl GIO fd 1 wrote 5 bytes
"exit
"
=== kdump ===


=== gdb'ing perl ===
[vlm@spelio:~]>gdb perl
GNU gdb 4.18 (FreeBSD)
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-unknown-freebsd"...(no debugging symbols
found)...
(gdb) br 0x280be470
Function "0x280be470" not defined.
(gdb) br main
Breakpoint 1 at 0x8048dfb
(gdb) run pl
Starting program: /usr/bin/perl pl
(no debugging symbols found)...(no debugging symbols found)...(no debugging
symbols found)...(no debugging symbols found)...
(no debugging symbols found)...(no debugging symbols found)...(no debugging
symbols found)...
Breakpoint 1, 0x8048dfb in main ()
(gdb) br *(0x280be470)
Breakpoint 2 at 0x280be470
(gdb) cont
Continuing.

Breakpoint 2, 0x280be470 in Perl_sighandler () from /usr/lib/libperl.so.3
(gdb)
=== end of gdb'ing perl ===

=== Perl_sighandler from Perl's sources ===
PUSHSTACKi(PERLSI_SIGNAL);
PUSHMARK(SP);
PUSHs(sv);
PUTBACK;

call_sv((SV*)cv, G_DISCARD);

POPSTACK;
cleanup:
if (flags & 1)
PL_savestack_ix -= 8; /* Unprotect save in progress. */
if (flags & 4)
PL_markstack_ptr--;
if (flags & 8)
PL_retstack_ix--;
if (flags & 16)
PL_scopestack_ix -= 1;
if (flags & 64)
SvREFCNT_dec(sv);
PL_op = myop; /* Apparently not needed... */
=== end of Perl sources ===


--
Lev Walkin
v...@netli.com

Yar Tikhiy

unread,
Jul 3, 2003, 7:55:08 AM7/3/03
to
Hi Vladimir,

Vladimir N. Oleynik <d...@simtreas.ru> wrote in <d83dt-...@dzo.simtreas.ru>:

>> Thread-safe -- это функции, которые не хранят состояние внутри себя.

VNO> Если они не сохраняют состояние внутри себя (правильнее - в статических
VNO> областях памяти), то можно по определению. Повторный вызов с одними и
VNO> теми же данными асинхронно - ССЗБ.

Прошу прощения, но я твое утверждение не вполне понял, даже расшифровав
"ССЗБ" :-) Что можно по определению? Вызывать thread-safe как
async-signal-safe? А по-моему, это разные вещи. Допустим, старый
добрый malloc() аккуратно лочит свои структуры, чтобы его можно
было вызывать из многонитевых программ. Hо как он себя поведет,
если его уже вызвали, он залочил что надо, и тут -- бах! -- приходит
сигнал, обработчик снова зовет malloc(), и тот попадает в тупик
(deadlock), пытаясь получить лок. Или возвращает ошибку, хотя
памяти на всех хватит. Hепорядок, правда? Та же беда может быть с
stdio.

VNO> Заметьте, что async-safe
VNO> расматриваются списком из сисколов и их оберток.
VNO> Все функции, не имеющие статических данных и не вызывающих из
VNO> себя "неправильных" могут сколько угодно быть прерваны асинхронно.

Да, конечно. Hо как заранее узнать для каждой конкретной системы,
является ли данная функция "правильной"?

>> KF> A как-же жить теперь дальше? Вообще нельзя?
>> KF> Или же нельзя при неаккуратном использовании?
>>
>> Согласно C99, вообще нельзя.

VNO> Hу мода, что поделаешь. Типа "мы лучше знаем что вам нужно,
VNO> можно и нельзя".

Hе мода, а суровая реальность. Причем у стандарта C реальность
суровее, чем у POSIX, т.к. приходится учитывать большее разнообразие
сред программирования.

Кстати, я тут нашел любопытный документ (давно пора было о нем
догадаться) -- обоснование (Rationale) стандарта C9X (оно немного
отстает от развития самого стандарта):
http://std.dkuug.dk/JTC1/SC22/WG14/www/docs/n897.pdf

Из него следует, что C99 не поощряет longjmp() из обработчика
сигнала, т.к. C99 не описывает никаких средств управления сигналами,
кроме signal() и raise(); т.е., сигналы нечем блокировать и т.п.

С другой стороны, POSIX не включает *longjmp() в список биб. функций,
которые можно вызывать из обработчика, т.к. "приложения, которые
используют longjmp() и siglongjmp() изнутри обработчиков сигналов,
требуют скурпулезной защиты, чтобы быть переносимыми".

>> В реальности, этот longjmp() ровным
>> счетом ничего не добавляет. Вообще.

VNO> Сильно... Я могу допустить, что на неких платформах longjmp()
VNO> из обработчика даст проблемы. Hо для примера создание
VNO> машины состояний с обработчиком фатальных ошибок longjmp()
VNO> даст фору запросто, другое дело, это надо УМЕТЬ делать.

Мы обсуждаем именно longjmp() из обработчика. В остальном longjmp()
-- это удобный и полезный хак (Edsger Dijkstra заворочался в гробу :-)
Я имел ввиду, что прыгай из обработчика или не прыгай -- все равно
от проблем со статическими данными не убежишь.

>> Дело не в освобождении ресурсов, а в возврате к некоему состоянию,
>> из которого дальше безопасно вызывать н.б.ф. А возвращает к нему
>> только return, если непонятно, в какой момент вызван обработчик
>> (еще раз вспомним об асинхронности сигналов).

VNO> Зачем страху нагонять? Hе засирайте стек и обеспечте, чтоб
VNO> одна и таже структура данных не менялась асинхронно и
VNO> все будет работать ;-)

Об этом здесь и речь ;-)

>> Hапример, в том, что в некоторых средах переход от контекста
>> обработчика к контексту основного потока не может быть выполнен
>> простым longjmp() (см. уже набивший, наверное, оскомину пример про
>> эмуляцию SIGINT в Win32).

VNO> Что доказывает только кривость этой эмуляции, а longjmp()
VNO> чем вам виноват.

См. выше. Или, думаешь, это была рука Microsoft? ;-)

>> Да, именно вызов н.б.ф. и приводит к побочным эффектам. А побочные

VNO> Да ясен пень, что если нам нужен флаг, который может меняться
VNO> асинхронно, то его значение нельзя кешировать. То что могут быть
VNO> проблемы с volatile у компиляторов никак не доказательство, что
VNO> надо бросить 20-летнюю практику пользования сигналами:
VNO> Это работает, так как по идеологии - нефиг генерировать сигналы
VNO> с огромной скоростью, это по сути для единичных и зачастую фатальных
VNO> ситуаций. Их ждем в любой момент, далее взводим флаг и когда надо его
VNO> обрабатываем. А то что сбросив его мы можем один раз потерять, то
VNO> это ничем не отличается от потери быстро-быстро нагенеренных
VNO> когда работает обработчик. Ибо - нефиг.
VNO> Hадо осуществить обмен - делайте pipe, socket, IPC...

Далеко не всегда нужен "тяжелый" IPC, если можно обойтись сигналом,
например, процесс аккуратно прибить. Тем более, сигналы теперь
надежные, просто так не теряются. Другое дело, что не стоит выжимать
из сигналов больше, чем они могут дать.

--
Yar

Yar Tikhiy

unread,
Jul 3, 2003, 11:08:24 AM7/3/03
to
Hi All,

Большое спасибо всем за комментарии и наводящие вопросы.
Hа их основе я сделал вторую версию "Правил...", исправленную
и дополненную. Предисловие позволю себе опустить.

Yar

=============================================================


ПРАВИЛА ИГРЫ В СИГHАЛЫ UNIX

Правило 1

Посланный процессу сигнал может быть проигнорирован, заблокирован,

или доставлен. Под доставкой понимается действие по умолчанию или
вызов назначенного обработчика. Рассмотрим эти случаи по отдельности.

1. Если сигнал игнорируется, то он не оказывает никакого воздействия
на процесс. В частности, не прерываются по EINTR прерываемые
операции.

2. Если сигнал блокируется, то его обработчик будет вызван один раз
(независимо от того, сколько раз был послан сигнал) после снятия
блокировки.

3. Если сигнал обрабатывается по умолчанию как "отвергнуть сигнал"
(discard signal), то это полностью эквивалентно его игнорированию
(см. п. 1.1). В некоторых руководствах это действие явно именуется
"ignore". Действиями по умолчанию, в зависимости от типа сигнала,
могут также быть "завершить процесс" (возможно, с записью образа

памяти), "приостановить процесс" и "продолжить процесс".

4. Если процесс назначил сигналу собственный обработчик и сигнал
должен быть доставлен, то будет вызван этот обработчик.

Если у сигнала установлен через sigaction(2) флаг SA_RESETHAND,
то эмулируется поведение SysV: обработчик сигнала будет
установлен равным SIG_DFL (обработка по умолчанию) в момент
доставки, т.е. непосредственно перед вызовом текущего
обработчика.

Hа время вызова обработчика текущий сигнал блокируется,

если не установлен флаг SA_NODEFER. Это необходимо, чтобы
избежать зацикливания обработчика.

Обработчику передается номер текущего сигнала, что позволяет
использовать один обработчик для нескольких сигналов.

Между посылкой сигнала и его доставкой может пройти
неограниченное количество времени, даже если сигнал не
заблокирован. Следовательно, один и тот же сигнал может
быть послан процессу несколько раз перед тем, как он будет
доставлен. Тем не менее, обработчик будет вызван единожды,
так как система запоминает только сам факт посылки каждого
сигнала.


Правило 2

Сигналы доставляются асинхронно. То есть, любая операция может
быть временно прервана для вызова обработчика сигнала, если на
момент ее выполнения сигнал не заблокирован и не игнорируется.

Существует, по сути, лишь один случай синхронной посылки
сигнала: когда процесс посылает сигнал сам себе с помощью
функций abort(3) или raise(3). Согласно C99 и SUSv3, если
в ответ на raise(3) должен быть вызван обработчик сигнала,
raise(3) может вернуть управление только после того, как
это сделает обработчик.


Правило 3

Существует ровно один тип статических данных, sig_atomic_t, переменную
которого обработчик сигнала может установить. Поведение приложения

не определено, если обработчик сигнала обращается к статическим
данным любым другим способом.

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


Правило 4

Если обработчик сигнала завершается возвратом, то текущая операция


может быть продолжена или прервана, в зависимости от типа операции

и значения флага SA_RESTART для этого сигнала. Есть несколько
случаев.

1. Если для текущего сигнала не установлен флаг SA_RESTART, то

некоторые системные вызовы будут прерваны с ошибкой EINTR. Если
же флаг SA_RESTART установлен, то выполнение этих вызовов будет
продолжено.

Список прерываемых вызовов может быть приведен в sigaction(2);
это вызовы ввода-вывода и wait(2). SUSv3 такого списка не
приводит, однако говорит, что сигнал без SA_RESTART прерывает
любой вызов, который может возвращать ошибку EINTR.

2. Функции семейства sleep (sleep(3), nanosleep(2) и т.п.) будут

прерваны любым доставленным сигналом, вне зависимости от наличия у
него флага SA_RESTART.

3. Вызовы select(2) и poll(2) могут быть прерваны сигналом.

Согласно SUSv3, poll(2) прерывается любым доставленным
сигналом; select(2) может или вести себя так же, или учитывать
наличие флага SA_RESTART у сигнала, в зависимости от
реализации.

4. Выполнение кода процесса и остальных системных вызовов будет
продолжено.


Следствия

1. [Из п. 2] Из обработчика сигнала нельзя выполнять общий c другими
частями процесса нереентерабельный участок кода, если он не защищается
всякий раз путем блокировки соответствующих сигналов.

2. [Из п. 2, с. 1] Из обработчика сигнала нельзя вызывать
нереентерабельные библиотечные функции (н.б.ф.), если только
устройством программы не гарантируется, что на момент _каждого_
вызова _любой_ н.б.ф. все сигналы, чьи обработчики содержат вызовы
_любых_ н.б.ф., не будут заблокированы.

Так как внутренние зависимости между н.б.ф. полностью зависят


от реализации, вложенный вызов _любой_ н.б.ф. может привести

к неопределенному поведению _всех_ н.б.ф. К примеру,
изрядное количество библиотечных функций явно или неявно
вызывают malloc(3); так что разрушение структур malloc(3)
в результате вложенного вызова приведет к сбою многих б.ф.

Список реентерабельных функций стандартизован, однако на
практике он может быть системно-зависим. То есть, наиболее

безопасно полагать, что нереентерабельны все стандартные
функции, кроме abort(3), _exit(2), _Exit(), а также signal(3),
вызванной с первым аргументом, равным номеру текущего сигнала.

3. [Из п. 1.4, п. 2] Выполнение обработчика сигнала может быть
прервано очередным доставленным сигналом. Чтобы избежать прерывания,
необходимо атомарно заблокировать сигналы (все или некоторые) при
входе в обработчик (см. sa_mask в sigaction(2)).

4. [Из с. 1, с. 3] Если один нереентерабельный обработчик установлен


для нескольких сигналов, то на время его работы нужно атомарно

блокировать все эти сигналы.

5. [Из п. 2, п. 3] Hеобходимо использовать модификатор volatile,


чтобы указать компилятору на асинхронность изменения переменной типа
sig_atomic_t.

6. [Из п. 3] Hе гарантируется, что обработчик сигнала может читать
статическую переменную, даже если она -- типа sig_atomic_t.

7. [Из п. 2, п. 3] Можно использовать только простое присваивание


переменным sig_atomic_t. В частности, над ними не следует использовать
операции ++ и --. К примеру, на архитектурах RISC эти операции над
ОЗУ не атомарны. Это касается как обработчиков сигналов, так и
основного потока (конечно, если соответствующие сигналы не заблокированы
на момент операций с переменной типа sig_atomic_t).

8. [Из п. 3] Если стандартная библиотечная функция, вызванная из
обработчика сигнала, вернула ошибку, то значение переменной errno
может быть не определено.

9. [Из п. 2] Вызов даже реентерабельной библиотечной функции из
обработчика сигнала может привести к изменению значения переменной
errno. Следовательно, если обработчик вызывает б.ф., то он должен
вначале сохранить значение errno, а перед возвратом восстановить его.

Правило 3 и следствие 8 исключают следствие 9. Проблема
здесь в том, что современные стандарты признают исторически
сложившуюся практику обращения к статическим переменным
вопреки правилу 3, но крайне не поощряют ее. Пока что это
приводит к подобным противоречиям.

10. [Из п. 2, п. 3, с. 1] "Трюк" с возвратом из обработчика сигнала


через longjmp(3), кочующий из книги в книгу (даже W.R.Stevens

приводит его), следует использовать с большой осторожностью.
Во-первых, longjmp(3) не восстановит статических переменных libc и


структуру кучи malloc(3) на момент вызова setjmp(3), а значит, он

ничем не поможет в решении проблемы реентерабельности. Во-вторых,
вызов longjmp(3) из обработчика обладает ограниченной переносимостью.

Изначально longjmp(3) из обработчика сигнала использовался,
чтобы обойти особенность 4.2BSD. Системные вызовы 4.2BSD
всегда продолжались после обработки сигнала, если обработчик
возвращал управление. Приходилось совершать longjmp(3),
чтобы прервать системный вызов.

Существует как минимум одна среда, претендующая на
совместимость с POSIX, в которой выход из обработчика
сигнала через longjmp(3) приводит к сбою приложения.

11. [Из п. 2] Следует помнить, что блокировка сигналов часто должна


выполняться неразрывно с другой операцией. Для этого существует
ряд стандартных механизмов и функций: ассоциированная с сигналом

маска sa_mask (см. sigaction(2)), системный вызов sigsuspend(2),

12. [Из п. 2, с. 1] Для завершения процесса из обработчика сигнала


следует использовать _exit(2) (POSIX) или _Exit() (C99), которые
не имеют побочных эффектов, в отличие от exit(3).

Побочными эффектами exit(3) являются вызов зарегистрированных
с помощью atexit(3) деструкторов, закрытие потоков stdio,
удаление временных файлов и т.п. Все это наверняка повлечет
вызов н.б.ф.

13. [Из п. 4.1] Если хотя бы у одного сигнала, для которого установлен


обработчик, нет флага SA_RESTART и этот сигнал не заблокирован, то

по EINTR может быть прервана _любая_ стандартная библиотечная функция
ввода-вывода.


Практические соображения

Hе надо относиться к сигналам как к грому и молнии. В аккуратно
написанной программе всегда известно, какие сигналы в данный момент
следует принимать и обрабатывать, а какие лучше держать заблокированными
или игнорировать. Зачастую это не требует значительных усилий или
изменений в структуре программы -- достаточно четко осознавать,
почему тот или иной сигнал не повредит, скажем, при вызове функции
ввода-вывода.

Hаиболее простой и безопасный подход к обработке сигналов -- когда
обработчик устанавливает флаг типа sig_atomic_t, а основной поток
проверяет его и предпринимает необходимые действия. Если при этом
необходимо прерывать некоторые операции ввода-вывода, то следует или
блокировать сигналы на время остальных операций ввода-вывода, или

особо обрабатывать ошибку EINTR после _всех_ операций ввода-вывода.

В сложных случаях может оказаться удобнее эмулировать синхронные

сигналы. Во-первых, можно создать канал, в который записывать из
обработчика сигнала его номер и, возможно, вспомогательные данные,
а из основного потока проверять этот канал через select(2)/poll(2)
и по приходу данных читать его. Во-вторых, можно держать сигналы
заблокированными большую часть времени и разблокировать их только
в специально отведенных, реентерабельных точках программы. Конечно,
это внесет задержку между отправкой сигнала и его обработкой.

Так как сложные схемы на основе сигналов очевидно ограничены и плохо
переносимы, с сигналами всегда стоит придерживаться принципа K.I.S.S.:
"Keep it simple, stupid". Если вы выучили правила и видите, что
нетривиальное использование сигналов причиняет слишком много
неудобств, то вам пора рассмотреть другие средства IPC для вашего
проекта.

Valentin Nechayev

unread,
Jul 3, 2003, 3:59:39 PM7/3/03
to

>>> Yar Tikhiy wrote:

YT> Большое спасибо всем за комментарии и наводящие вопросы.
YT> Hа их основе я сделал вторую версию "Правил...", исправленную
YT> и дополненную. Предисловие позволю себе опустить.

Можно включить это в регулярные постинги как приложение к FAQ?

Ниже ещё немного замечаний.

YT> Сигналы доставляются асинхронно. То есть, любая операция может
YT> быть временно прервана для вызова обработчика сигнала, если на
YT> момент ее выполнения сигнал не заблокирован и не игнорируется.

YT> Существует, по сути, лишь один случай синхронной посылки
YT> сигнала: когда процесс посылает сигнал сам себе с помощью
YT> функций abort(3) или raise(3). Согласно C99 и SUSv3, если
YT> в ответ на raise(3) должен быть вызван обработчик сигнала,
YT> raise(3) может вернуть управление только после того, как
YT> это сделает обработчик.

Действия типа
char* p = NULL;
*p = 1;
тоже вполне укладываются в случай синхронной посылки сигнала самому себе;))
Кстати, raise() ведь сделана как kill(getpid(),sig) (я уверен, что она
так сделана везде). Так что kill самому себе надо тоже включать в этот
список. Или не включать тогда raise().

YT> Правило 3

YT> Существует ровно один тип статических данных, sig_atomic_t, переменную
YT> которого обработчик сигнала может установить. Поведение приложения
YT> не определено, если обработчик сигнала обращается к статическим


YT> данным любым другим способом.

YT> По-видимому, это весьма жесткое ограничение связано с
YT> поддержкой архитектур, в которых обработчик сигнала не
YT> может напрямую обращаться к основной памяти процесса.

1. Надо всё равно говорить тут, IMO, про случай асинхронн вызванного
обработчика.
2. Кстати, в comp.unix.programmer никто ничего толком на это не сказал.
Было только одно замечание типа "они, наверно, криво сформулировали".
Я бы всё же говорил, что sig_atomic_t можно и читать, на подавляющем
большинстве архитектур.

YT> 7. [Из п. 2, п. 3] Можно использовать только простое присваивание
YT> переменным sig_atomic_t. В частности, над ними не следует использовать
YT> операции ++ и --. К примеру, на архитектурах RISC эти операции над
YT> ОЗУ не атомарны. Это касается как обработчиков сигналов, так и
YT> основного потока (конечно, если соответствующие сигналы не заблокированы
YT> на момент операций с переменной типа sig_atomic_t).

Я бы сказал так: нигде нельзя гарантировать атомарность инкремента/декремента,
даже на CISC, потому что компилятор не обязан транслировать команду в
inc [place_in_memory].

Тем не менее, все современные процессоры общего назначения, включая RISC,
содержат средства для атомарного инкремента/декремента/модификации/etc.,
которые и должны применяться (насколько возможно) в таких случаях.

YT> 8. [Из п. 3] Если стандартная библиотечная функция, вызванная из
YT> обработчика сигнала, вернула ошибку, то значение переменной errno
YT> может быть не определено.

YT> 9. [Из п. 2] Вызов даже реентерабельной библиотечной функции из
YT> обработчика сигнала может привести к изменению значения переменной
YT> errno. Следовательно, если обработчик вызывает б.ф., то он должен
YT> вначале сохранить значение errno, а перед возвратом восстановить его.

YT> Правило 3 и следствие 8 исключают следствие 9. Проблема
YT> здесь в том, что современные стандарты признают исторически
YT> сложившуюся практику обращения к статическим переменным
YT> вопреки правилу 3, но крайне не поощряют ее. Пока что это
YT> приводит к подобным противоречиям.

Ха. Это, конечно, из серии "слона-то я и не приметил". ;))))

YT> Существует как минимум одна среда, претендующая на
YT> совместимость с POSIX, в которой выход из обработчика
YT> сигнала через longjmp(3) приводит к сбою приложения.

"Имя, сестра, имя!"

YT> 11. [Из п. 2] Следует помнить, что блокировка сигналов часто должна
YT> выполняться неразрывно с другой операцией. Для этого существует
YT> ряд стандартных механизмов и функций: ассоциированная с сигналом
YT> маска sa_mask (см. sigaction(2)), системный вызов sigsuspend(2),

А также pselect() (где он есть).


-netch-

Vladimir V. Ivanov

unread,
Jul 4, 2003, 2:14:41 AM7/4/03
to
On Thu, 03 Jul 2003 19:08:24 +0400
Yar Tikhiy <y...@comp.chem.msu.su> wrote:


> В сложных случаях может оказаться удобнее эмулировать синхронные
> сигналы. Во-первых, можно создать канал, в который записывать из
> обработчика сигнала его номер и, возможно, вспомогательные данные,
> а из основного потока проверять этот канал через select(2)/poll(2)
> и по приходу данных читать его.

Да, так как там насчёт возможных deadlock'ов и потерь данных в
этом случае ?

Regards,
Vladimir Ivanov

Valentin Nechayev

unread,
Jul 4, 2003, 3:39:12 AM7/4/03
to
>>> Vladimir V. Ivanov wrote:

>> В сложных случаях может оказаться удобнее эмулировать синхронные
>> сигналы. Во-первых, можно создать канал, в который записывать из
>> обработчика сигнала его номер и, возможно, вспомогательные данные,
>> а из основного потока проверять этот канал через select(2)/poll(2)
>> и по приходу данных читать его.

VVI> Да, так как там насчёт возможных deadlock'ов и потерь данных в
VVI> этом случае ?

А никак. Тяжело там. Лучше всего, наверно, держать массив флагов типа
sig_atomic_t размерностью в количество сигналов в системе и в обработчике
делать `seen[sig]=1'. Или как-то аналогично.


-netch-

Vladimir V. Ivanov

unread,
Jul 4, 2003, 4:43:42 AM7/4/03
to
On Fri, 4 Jul 2003 07:39:12 +0000 (UTC)
Valentin Nechayev <ne...@segfault.kiev.ua> wrote:
>
> А никак. Тяжело там. Лучше всего, наверно, держать массив флагов типа
> sig_atomic_t размерностью в количество сигналов в системе и в
> обработчике делать `seen[sig]=1'. Или как-то аналогично.

Если системой поддерживается вызов pselect - проблем нет.
Если нет то, корректен ли следующий алгоритм ?

1. Запрещаем сигналы (в начале программы)
//....
2. Разрешаем сигналы
3. Делаем вызов poll/select, запоминаем код возврата
4. Запрещаем сигналы
5. Проверяем seen[...], выполняем какие-то реакции на сигналы,
очищаем seen
6. Если возврат из poll/select - EINTR - то возвращаемся на 2
7. Реагируем на другие коды возврата из poll/select.
8. Обрабатываем готовые дескрипторы
9. Возвращаемся на 2

В обработчике сигнала seen[sig] = 1;

Новые libc вроде умеют эмулировать pselect при его отсутствии
(хотя и без гарантии отсутствия race condition).
Поэтому 2-3-4 - можно безопасно заменить на pselect.

Последовательность пунктов 5-6 будет гарантировать что ни один сигнал
не будет пропущен. И никаких пайпов не надо. Правильно ?

Regards,
Vladimir Ivanov

Dmitry Miloserdov

unread,
Jul 4, 2003, 5:55:22 AM7/4/03
to
Hello, Vladimir!
You wrote to Valentin Nechayev on Fri, 4 Jul 2003 08:43:42 +0000 (UTC):
VVI> 7. Реагируем на другие коды возврата из poll/select.
VVI> 8. Обрабатываем готовые дескрипторы
VVI> 9. Возвращаемся на 2
И когда в п.8 тебе надо будет что-то посчитать то
прибьют твой просесс по SIGKILL потому как на
SIGTERM вы несоизволили среагировать.

With best regards, Dmitry Miloserdov. E-mail: dmi...@bis.ru


Vladimir V. Ivanov

unread,
Jul 4, 2003, 6:11:21 AM7/4/03
to
Hi!

On Fri, 4 Jul 2003 09:55:22 +0000 (UTC)
"Dmitry Miloserdov" <dmi...@bis.ru> wrote:

> VVI> 7. Реагируем на другие коды возврата из poll/select.
> VVI> 8. Обрабатываем готовые дескрипторы
> VVI> 9. Возвращаемся на 2
> И когда в п.8 тебе надо будет что-то посчитать то
> прибьют твой просесс по SIGKILL потому как на
> SIGTERM вы несоизволили среагировать.

Ну, если при такой схеме диспетчеризации ввода-вывода - в п.8
что-то долго считается (или есть какое-либо блокирующее действие)
- то это дизайн программы хреновый, и сигналы тут не причём :)

Regards,
Vladimir Ivanov

Dmitry Miloserdov

unread,
Jul 4, 2003, 6:30:49 AM7/4/03
to
Hello, Vladimir!
You wrote to me on Fri, 4 Jul 2003 10:11:21 +0000 (UTC):
VVI> Ну, если при такой схеме диспетчеризации ввода-вывода - в п.8
VVI> что-то долго считается (или есть какое-либо блокирующее действие)
Ну не во всех программах poll-time=99%.
Блокирующие действия ну тоже вполне реальны syslog например.
Да и неблокирующие необязаны выполняться мгновенно.

VVI> - то это дизайн программы хреновый, и сигналы тут не причём :)
Заметьте - не я это сказал;)

Max E. Kuznecov

unread,
Jul 4, 2003, 8:20:04 AM7/4/03
to
Доброго здоровья господа!

Ознакомившись с сабжевым текстом возникло желание поглядеть на код
правильной реализации обработки сигналов. Что, многоуважаемые господа,
посоветуют в качестве идеального (или близкого к нему Ж-) примера ?

Заранее всем спасибо.


--
[~syhpoon]

Dmitry Miloserdov

unread,
Jul 4, 2003, 9:14:57 AM7/4/03
to
Hello, Max!
You wrote to Yar Tikhiy on Fri, 4 Jul 2003 12:20:04 +0000 (UTC):
MEK> Ознакомившись с сабжевым текстом возникло желание поглядеть на код
MEK> правильной реализации обработки сигналов.
А вот у меня возникло совершенно противоположное желание:
посмотреть что же за программы автор "устал чинить".
Что привело автора к таким практически параноидальным
заключениям - неужели реальный опыт?
(это касаемо в основном следствий)
Я бы даже с exit vs _exit не согласился.
Это вопрос что лучше потерять кусок данных в 99%
или получить двожды записанный кусок в 1%?
Или не пользовать stdio?
Вообщем вообще автор пытается навязать свой подход к
написанию программ. А программы бывают не только
вокруг poll написанные.
Например процесс постоянно что-то считаета по siginfo
printf/fflush делает - запретите?
А sigio/sigurg придумали что вы по ним atomic_t в единичку ставили?

Ой! это все конечно к вам это к Yar'у ;)

Vladimir V. Ivanov

unread,
Jul 4, 2003, 9:27:14 AM7/4/03
to
On Fri, 4 Jul 2003 10:30:49 +0000 (UTC)
"Dmitry Miloserdov" <dmi...@bis.ru> wrote:

> Hello, Vladimir!
> You wrote to me on Fri, 4 Jul 2003 10:11:21 +0000 (UTC):
> VVI> Ну, если при такой схеме диспетчеризации ввода-вывода - в п.8
> VVI> что-то долго считается (или есть какое-либо блокирующее >

> VVI> действие)


> Ну не во всех программах poll-time=99%.
> Блокирующие действия ну тоже вполне реальны syslog например.

Цена вызова syslog в большинстве случаев незначительна. А в том
софте, где это имеет значение от syslog'а отказываются.

> Да и неблокирующие необязаны выполняться мгновенно.

Да, но не _столько_ же времени чтобы процесс не успел
дойти до кода где SIGTERM обрабатывается...

А глюк в алгоритме я всё же нашёл. В том случае если обработчик
сигнала вызовется непосредственно до вызова poll/select, в результате
чего последний не будет прерван по EINTR.
Так что алгоритм таки неправильный.
Получается что для надёжной синхронной обработки сигналов нужна
комбинация пайпа (в режиме O_NONBLOCK) с статическим массивом
sig_atimic_t.

Regards,
Vladimir Ivanov

Dmitry Miloserdov

unread,
Jul 4, 2003, 10:23:41 AM7/4/03
to
Hello, Vladimir!
You wrote to me on Fri, 4 Jul 2003 13:27:14 +0000 (UTC):
VVI> Цена вызова syslog в большинстве случаев незначительна. А в том
VVI> софте, где это имеет значение от syslog'а отказываются.
Что значит "в большинстве случаев"?
В большинстве случаев сигнал будет вызван из вашего poll/select
а оттуда можно пользовать что угодно не обращая внимание на
реентрабельность. Вам i++ запретили а вы "в большинстве случаев"!!!
syslog can block точка.
Может он час в syslog проведет.
Скажете в жизни так не бывает?
А тут вообще похоже не про жизнь - теория одна.

Vladimir V. Ivanov

unread,
Jul 4, 2003, 10:57:42 AM7/4/03
to
On Fri, 4 Jul 2003 14:23:41 +0000 (UTC)
"Dmitry Miloserdov" <dmi...@bis.ru> wrote:

> Что значит "в большинстве случаев"?

> В большинстве случаев сигнал будет вызван из вашего poll/select...


> а оттуда можно пользовать что угодно не обращая внимание на
> реентрабельность. Вам i++ запретили а вы "в большинстве случаев"!!!

Имея в виду затраты на вызов syslog, я говорю не
про вероятность события, а про количество программ в
которых оно несущественно. Это по поводу "большинства случаев".

> syslog can block точка.

Те приложения, которые чувствительны к блокировке syslog
не будут его вызывать.
Вообще, серверные приложения построенные на мультиплексировании
ввода/вывода через poll/select и обрабатывающие большие
потоки запросов не должны вызывать блокирующих вызовов,
кроме названных. точка.

> Может он час в syslog проведет.
> Скажете в жизни так не бывает?

Такое может случиться.
И что ? Как час нахождения в syslog мешает
синхронной обработке сигналов ?

Regards,
Vladimir Ivanov

Dmitry Miloserdov

unread,
Jul 4, 2003, 12:49:11 PM7/4/03
to
Hello, Vladimir!
You wrote to me on Fri, 4 Jul 2003 14:57:42 +0000 (UTC):
VVI> Имея в виду затраты на вызов syslog, я говорю не
VVI> про вероятность события, а про количество программ в
VVI> которых оно несущественно. Это по поводу "большинства случаев".
А я про программы котрые ты предложил сделать по своей схеме.
какие еще функции предлОжите запретить для применения этой схемы?
конечно если неиспользовать ничего кроме сисколов то какие вообще
могут возникнуть проблемы?
А что получается когда заменить пытаются не все а какие-то куски
производит не лучшее впечатление.

>> Может он час в syslog проведет.
>> Скажете в жизни так не бывает?

VVI> Такое может случиться.
VVI> И что ? Как час нахождения в syslog мешает
VVI> синхронной обработке сигналов ?
пока ты висел с закрытыми сигналами в syslog прозевал
свой шанс умереть достойно по term'у и получил kill.

Yar Tikhiy

unread,
Jul 5, 2003, 6:59:17 AM7/5/03
to
Valentin Nechayev <ne...@segfault.kiev.ua> wrote in
<2003070319...@iv.nn.kiev.ua>:

VN> Можно включить это в регулярные постинги как приложение к FAQ?

Конечно можно, как только я доведу этот документ до ума.
Думаю, следующая версия будет вполне пригодна для распространения.

VN> Действия типа
VN> char* p = NULL;
VN> *p = 1;
VN> тоже вполне укладываются в случай синхронной посылки сигнала самому себе;))

Я лично не вполне в этом уверен :-) Одно дело raise(), а другое --
аппаратный trap...

VN> Кстати, raise() ведь сделана как kill(getpid(),sig) (я уверен, что она
VN> так сделана везде). Так что kill самому себе надо тоже включать в этот
VN> список. Или не включать тогда raise().

Принимается. SUSv3 (видимо, как и POSIX) прямо говорит в описании
raise(), что raise() должен быть эквивалентен kill(getpid(), sig).

YT>> Существует ровно один тип статических данных, sig_atomic_t, переменную
YT>> которого обработчик сигнала может установить. Поведение приложения
YT>> не определено, если обработчик сигнала обращается к статическим
YT>> данным любым другим способом.

YT>> По-видимому, это весьма жесткое ограничение связано с
YT>> поддержкой архитектур, в которых обработчик сигнала не
YT>> может напрямую обращаться к основной памяти процесса.

VN> 1. Hадо всё равно говорить тут, IMO, про случай асинхронн вызванного
VN> обработчика.

10x, действительно, надо оговорить асинхронность.

VN> 2. Кстати, в comp.unix.programmer никто ничего толком на это не сказал.
VN> Было только одно замечание типа "они, наверно, криво сформулировали".

Видимо, там все устали от обсуждения разных несостыковок в сигналах.
Ведь уже 20 лет, как оно идет в comp.unix.* время от времени. А вот
собрать все факты вместе никто из них не догадался :-)

VN> Я бы всё же говорил, что sig_atomic_t можно и читать, на подавляющем
VN> большинстве архитектур.

Можно вообще добавить, что, поскольку правила не всегда согласуются
с реальностью, каждый волен нарушать их на свой страх и риск (чем
обычно и занимаются разработчики под Unix), но тогда не стоит
удивляться последствиям :-)

--
Yar

Yar Tikhiy

unread,
Jul 5, 2003, 7:18:35 AM7/5/03
to
Max E. Kuznecov <m...@ngx.uz.ua> wrote in <be3rd5$26c7$1...@ddt.demos.su>:

MEK> Ознакомившись с сабжевым текстом возникло желание поглядеть на код
MEK> правильной реализации обработки сигналов. Что, многоуважаемые господа,
MEK> посоветуют в качестве идеального (или близкого к нему Ж-) примера ?

Боюсь, что приводить "идеальный пример на все случаи жизни" было
бы просто глупо. Проблема в том, что с сигналами действительно
есть проблемы. Так что приходится самому решать, основываясь на
умении и опыте, как быть в каждом конкретном случае. Это и называется
мастерством программиста.

А мои "Правила игры..." лишь очерчивают границы, в которых можно
работать с той или иной долей уверенности в результате.

--
Yar

Lev Walkin

unread,
Jul 5, 2003, 8:31:26 AM7/5/03
to

Yar Tikhiy wrote:

>
> А мои "Правила игры..." лишь очерчивают границы, в которых можно
> работать с той или иной долей уверенности в результате.

Ты только "долю уверенности" не забудь где-нибудь явно прописать...


--
Lev Walkin
v...@netli.com

Yar Tikhiy

unread,
Jul 5, 2003, 7:37:35 AM7/5/03
to
Dmitry Miloserdov <dmi...@bis.ru> wrote in <be3ujh$2olo$1...@ddt.demos.su>:

DM> А вот у меня возникло совершенно противоположное желание:
DM> посмотреть что же за программы автор "устал чинить".
DM> Что привело автора к таким практически параноидальным
DM> заключениям - неужели реальный опыт?
DM> (это касаемо в основном следствий)

Скажу, что последней каплей стали реализации серверной части протокола
FTP под Unix. Во многих из них (все, что выросло из BSD ftpd) или
работают с сигналами "по старинке", что приводит к весьма заметному
количеству смертей от SIGSEGV при нагрузке, или попытались исправить
этот механизм (а не заменить на новый), породив тем самым дополнительные
баги.

DM> Я бы даже с exit vs _exit не согласился.
DM> Это вопрос что лучше потерять кусок данных в 99%
DM> или получить двожды записанный кусок в 1%?

Лучше в 100% делать то, что задумано, если железо не глючит ;-)

DM> Или не пользовать stdio?

Пользовать, пока его ограничения не станут причинять боль.
Все зависит от сложности приложения.

DM> Вообщем вообще автор пытается навязать свой подход к
DM> написанию программ. А программы бывают не только
DM> вокруг poll написанные.

Hичего я не навязываю. Извините за банальную аллюзию, но я просто
решил указать, по каким тропинкам можно выйти куда-нибудь, а какие
заведут в трясину; а дальше каждый сам волен выбирать.

DM> Hапример процесс постоянно что-то считаета по siginfo
DM> printf/fflush делает - запретите?

Hикто тебе не запрещает этого, равно как использовать в своих
прогах __GLIBC_VERSION_MINOR и создавать приложения для ОС GNU/Linux,
требующие ядра версии в промежутке между 2.0.20 и 2.0.34.

DM> А sigio/sigurg придумали что вы по ним atomic_t в единичку ставили?

Если взять на себя труд, можно написать обработчик этих сигналов
с использованием reentrant functions из списка POSIX. Hо установка
sig_atomic_t все равно будет переносимее ;-)

Вообще, меня веселит ситуация, когда под MSDOS программеры страдали,
что приходится использовать недокументированные ходы, а теперь под
Unix их стандарты заели :-)))

--
Yar

Yar Tikhiy

unread,
Jul 5, 2003, 7:45:40 AM7/5/03
to
Valentin Nechayev <ne...@segfault.kiev.ua> wrote in
<20030704073...@segfault.kiev.ua>:

>>> В сложных случаях может оказаться удобнее эмулировать синхронные
>>> сигналы. Во-первых, можно создать канал, в который записывать из
>>> обработчика сигнала его номер и, возможно, вспомогательные данные,
>>> а из основного потока проверять этот канал через select(2)/poll(2)
>>> и по приходу данных читать его.
VVI>> Да, так как там насчёт возможных deadlock'ов и потерь данных в
VVI>> этом случае ?

VN> А никак. Тяжело там. Лучше всего, наверно, держать массив флагов типа
VN> sig_atomic_t размерностью в количество сигналов в системе и в обработчике
VN> делать `seen[sig]=1'. Или как-то аналогично.

Прошу прощения, я торможу... Устанавливать этот флаг в массиве,
если обработчик сигнала не может записать в pipe?

--
Yar

Valentin Nechayev

unread,
Jul 5, 2003, 11:37:02 AM7/5/03
to

>>> Yar Tikhiy wrote:

>>>> В сложных случаях может оказаться удобнее эмулировать синхронные
>>>> сигналы. Во-первых, можно создать канал, в который записывать из
>>>> обработчика сигнала его номер и, возможно, вспомогательные данные,
>>>> а из основного потока проверять этот канал через select(2)/poll(2)
>>>> и по приходу данных читать его.
VVI>>> Да, так как там насчёт возможных deadlock'ов и потерь данных в
VVI>>> этом случае ?
VN>> А никак. Тяжело там. Лучше всего, наверно, держать массив флагов типа
VN>> sig_atomic_t размерностью в количество сигналов в системе и в обработчике
VN>> делать `seen[sig]=1'. Или как-то аналогично.

YT> Прошу прощения, я торможу... Устанавливать этот флаг в массиве,
YT> если обработчик сигнала не может записать в pipe?

Вообще вместо записи _номера сигнала_. Впрочем, это может быть и слишком
жестоко (там, где номер сигнала влезает в байт - то есть практически
везде).


-netch-

Eugene Karpachov

unread,
Jul 6, 2003, 7:05:32 AM7/6/03
to
Fri, 4 Jul 2003 07:39:12 +0000 (UTC) Valentin Nechayev написал:

> А никак. Тяжело там. Лучше всего, наверно, держать массив флагов типа
> sig_atomic_t размерностью в количество сигналов в системе и в обработчике
> делать `seen[sig]=1'. Или как-то аналогично.

А операция индексации разве атомарна, то есть, безопасно ли
индексировать массив, пусть даже и состоящий из sig_atomic_t?
Теоретически, по-моему, нет.

--
jk

Lev Walkin

unread,
Jul 6, 2003, 7:24:11 AM7/6/03
to

начало массива никуда не уползает. так что все в порядке.

--
Lev Walkin
v...@netli.com

Valentin Nechayev

unread,
Jul 6, 2003, 11:17:41 AM7/6/03
to

>>> Eugene Karpachov wrote:

>> А никак. Тяжело там. Лучше всего, наверно, держать массив флагов типа
>> sig_atomic_t размерностью в количество сигналов в системе и в обработчике
>> делать `seen[sig]=1'. Или как-то аналогично.

EK> А операция индексации разве атомарна, то есть, безопасно ли
EK> индексировать массив, пусть даже и состоящий из sig_atomic_t?
EK> Теоретически, по-моему, нет.

Я в ауте. Вы это серьёзно??? Если да - покажите место, где может
возникнуть неатомарность...


-netch-

Eugene Karpachov

unread,
Jul 6, 2003, 12:15:23 PM7/6/03
to
Sun, 6 Jul 2003 15:17:41 +0000 (UTC) Valentin Nechayev написал:

> EK> А операция индексации разве атомарна, то есть, безопасно ли
>
> Я в ауте. Вы это серьёзно??? Если да - покажите место, где может
> возникнуть неатомарность...

Если бы я знал, то не спрашивал бы, а сразу показал. Архитектуры могут
быть разные.

--
jk

Valentin Nechayev

unread,
Jul 6, 2003, 3:03:40 PM7/6/03
to

>>> Eugene Karpachov wrote:

> EK>> А операция индексации разве атомарна, то есть, безопасно ли
>> Я в ауте. Вы это серьёзно??? Если да - покажите место, где может
>> возникнуть неатомарность...

EK> Если бы я знал, то не спрашивал бы, а сразу показал. Архитектуры могут
EK> быть разные.

Гм... ну тогда давайте ещё придумаем архитектуру, где требуется захват
общего лока для выполнения присваивания любой переменной...


-netch-, в ужасе


P.S. А ведь действительно нигде не рассказано, что такое не требуется.
Или сказано?

Lev Walkin

unread,
Jul 6, 2003, 4:10:38 PM7/6/03
to

Valentin Nechayev wrote:

> Гм... ну тогда давайте ещё придумаем архитектуру, где требуется захват
> общего лока для выполнения присваивания любой переменной...
>
>
> -netch-, в ужасе
>
>
> P.S. А ведь действительно нигде не рассказано, что такое не требуется.
> Или сказано?

Хм. У нас .unix.prog? Думаю, что на такой архитектуре эхотаг просто
не имел бы смысла.

--
Lev Walkin
v...@netli.com

Romio Pedchenko

unread,
Jul 5, 2003, 12:59:46 AM7/5/03
to
\│/ Hi there, Kirill!

29 Jun 03 22:49, Kirill Frolov wrote to y...@comp.chem.msu.su:

KF> основного потока программы или другого обработчика. Hо тогда утверждение
KF> насчёт того, что обработчик может только установить, и только переменную
KF> одного типа HЕКОРРЕКТHО.
он имеет в виду, что для каждой конкретной платформы ты не уверен, какой тип
является безопасным в смысле атомарного присваивания, посему за тебя определяют
тип sig_atomic_t - единственный, в котором ты можешь быть твёрдо уверен. в
плане переносимости кода - тоже.

wbr, Lucky Romio // ro...@ck.ukrtel.net // [wanna smoke!]

... Bate doba, da-i mai tare azi din zori si pana-n seara!

Eugene Karpachov

unread,
Jul 7, 2003, 12:55:52 AM7/7/03
to
Sun, 6 Jul 2003 20:10:38 +0000 (UTC) Lev Walkin написал:

>> Гм... ну тогда давайте ещё придумаем архитектуру, где требуется захват
>> общего лока для выполнения присваивания любой переменной...
>>
>>
>> -netch-, в ужасе
>>
>>
>> P.S. А ведь действительно нигде не рассказано, что такое не требуется.
>> Или сказано?
>
> Хм. У нас .unix.prog? Думаю, что на такой архитектуре эхотаг просто
> не имел бы смысла.

В общем, тот же подход к "переносимости", как и в языке C: сначала
оставляют неопределённым всё, что только можно, ссылаясь на мифическую
"переносимость", а когда всё-таки найдётся непереносимый случай,
говорят: "ну, это ты загнул". :)

--
jk

Valentin Nechayev

unread,
Jul 7, 2003, 1:52:18 AM7/7/03
to
>>> Eugene Karpachov wrote:

>>> Гм... ну тогда давайте ещё придумаем архитектуру, где требуется захват
>>> общего лока для выполнения присваивания любой переменной...
>>> -netch-, в ужасе
>>> P.S. А ведь действительно нигде не рассказано, что такое не требуется.
>>> Или сказано?
>> Хм. У нас .unix.prog? Думаю, что на такой архитектуре эхотаг просто
>> не имел бы смысла.

EK> В общем, тот же подход к "переносимости", как и в языке C: сначала
EK> оставляют неопределённым всё, что только можно, ссылаясь на мифическую
EK> "переносимость", а когда всё-таки найдётся непереносимый случай,
EK> говорят: "ну, это ты загнул". :)

Надо ограничивать разумные классы переносимости. Например, невстроенные
несильноспециализированные компьютеры. У которых не бывает, например,
байта иначе чем в 8 бит.;))) Осталось только добиться такого от
стандартизаторов.


-netch-

Max E. Kuznecov

unread,
Jul 7, 2003, 2:23:09 AM7/7/03
to
Yar Tikhiy wrote:
> Боюсь, что приводить "идеальный пример на все случаи жизни" было
> бы просто глупо. Проблема в том, что с сигналами действительно
> есть проблемы. Так что приходится самому решать, основываясь на
> умении и опыте, как быть в каждом конкретном случае. Это и называется
> мастерством программиста.
>
> А мои "Правила игры..." лишь очерчивают границы, в которых можно
> работать с той или иной долей уверенности в результате.
>

Под идеальным примером я имел в виду вовсе не "на все случаи жизни", а
любую, вполне конкретную реализацию, отвечающую всем требованииям
"правильной обработки сигналов" (ведь такие существуют ? :-)
А умение и опыт с неба не свалятся, их приходиться зарабытывать и
чтением чужих кодов в том числе.

Поэтому вопрос открыт: есть ли _реальный_ пример реализации "правильной"
обработки сигналов, пускай и привязанных к вполне конкретному алгоритму?

Vladimir V. Ivanov

unread,
Jul 7, 2003, 4:39:45 AM7/7/03
to
On Fri, 4 Jul 2003 16:49:11 +0000 (UTC)
"Dmitry Miloserdov" <dmi...@bis.ru> wrote:

> А я про программы котрые ты предложил сделать по своей схеме.
> какие еще функции предлОжите запретить для применения этой схемы?
> конечно если неиспользовать ничего кроме сисколов то какие вообще
> могут возникнуть проблемы?

Ой! Зачем же в крайности сразу. Неужели сложно для блокирующих (или
прочих долгоиграющих) функций, написать неблокирующие обёртки?
Ну и схему с мультиплексированием ввода-вывода никто не навязывает.
Ясно, что это годиться далеко не для всех случаев жизни. Но если уж
выбрали - то играть нужно уже по правилам этой схемы.

> А что получается когда заменить пытаются не все а какие-то куски
> производит не лучшее впечатление.

Мысль не понял...


> >> Может он час в syslog проведет.

> VVI> И что ? Как час нахождения в syslog мешает
> VVI> синхронной обработке сигналов ?
> пока ты висел с закрытыми сигналами в syslog прозевал
> свой шанс умереть достойно по term'у и получил kill.

Если бы не kill, сингал term был бы так или иначе обработан
программой, но позже. После завершения syslog.
А вообще, правильно что kill получил - ибо нечего в syslog'е час
висеть :)

С уважением,
Владимир Иванов

Dmitry Miloserdov

unread,
Jul 7, 2003, 12:55:05 PM7/7/03
to
Hello, Yar!
You wrote to me on Sat, 05 Jul 2003 15:37:35 +0400:
YT> Скажу, что последней каплей стали реализации серверной части протокола
YT> FTP под Unix. Во многих из них (все, что выросло из BSD ftpd) или
YT> работают с сигналами "по старинке", что приводит к весьма заметному
YT> количеству смертей от SIGSEGV при нагрузке, или попытались исправить
YT> этот механизм (а не заменить на новый), породив тем самым
YT> дополнительные баги.
Ну дык - исторически сложилось: telnetd/ftpd/sendmail итд.
Можно взглянуть на исправленный код?

DM>> Я бы даже с exit vs _exit не согласился.
DM>> Это вопрос что лучше потерять кусок данных в 99%
DM>> или получить двожды записанный кусок в 1%?

YT> Лучше в 100% делать то, что задумано, если железо не глючит ;-)
Ну расскажи как.
Либо теряешь данные в буферах либо рискуешь записать их дважды.
И это даже не проблема stdio(хотя насколько я понимаю там проблем хватает)
это проблема буферизации.

DM>> Вообщем вообще автор пытается навязать свой подход к
DM>> написанию программ. А программы бывают не только
DM>> вокруг poll написанные.

YT> Hичего я не навязываю. Извините за банальную аллюзию, но я просто
YT> решил указать, по каким тропинкам можно выйти куда-нибудь, а какие
YT> заведут в трясину; а дальше каждый сам волен выбирать.
Ну да совершенно не навязываешь.
Этот мост плохой - если когда будете перехотить рядом поедет колонна
грузовиков то грязью уж точно забрызжут. Зато рядом есть отличный
правда он железнодорожный и поезда только ходят раз в сутки

DM>> А sigio/sigurg придумали что вы по ним atomic_t в единичку ставили?

YT> Если взять на себя труд, можно написать обработчик этих сигналов
YT> с использованием reentrant functions из списка POSIX. Hо установка
YT> sig_atomic_t все равно будет переносимее ;-)
А зачем вообще сигналы придумали?
Сделали бы массив этот и ядро бы его заполняло...

YT> Вообще, меня веселит ситуация, когда под MSDOS программеры страдали,
YT> что приходится использовать недокументированные ходы, а теперь под
YT> Unix их стандарты заели :-)))
Кто сказал "стандарты заели"? Просто ты зажал свободу до невозможности.
А думать что происходит и какие последствия могут быть все равно надо.
А подумавши можно и без atomic_t прожить.
Во! Кстати какой в нем смысл? Какая разница атомарно присваивание
или нет если ты в переменной хранишь только 0 или 1?

Dmitry Miloserdov

unread,
Jul 7, 2003, 1:19:10 PM7/7/03
to
Hello, Vladimir!
You wrote to me on Mon, 7 Jul 2003 08:39:45 +0000 (UTC):
VVI> Ой! Зачем же в крайности сразу. Неужели сложно для блокирующих (или
VVI> прочих долгоиграющих) функций, написать неблокирующие обёртки?
VVI> Ну и схему с мультиплексированием ввода-вывода никто не навязывает.
VVI> Ясно, что это годиться далеко не для всех случаев жизни. Но если уж
VVI> выбрали - то играть нужно уже по правилам этой схемы.
Простите а разве сигналы сделаны не для того чтобы среагировать
на неожиданно изменившуюсь обстановку не внося изменений в
долгоиграющий модуль?

>> А что получается когда заменить пытаются не все а какие-то куски
>> производит не лучшее впечатление.

VVI> Мысль не понял...
Ну привыкли люди к printf-style
а убрать FILE* и оставить возможность пользоваться
форматированным выводом вроде как не особо получается.

Valentin Nechayev

unread,
Jul 7, 2003, 5:38:26 PM7/7/03
to

>>> Yar Tikhiy wrote:

VN>> Можно включить это в регулярные постинги как приложение к FAQ?

YT> Конечно можно, как только я доведу этот документ до ума.
YT> Думаю, следующая версия будет вполне пригодна для распространения.

OK, жду.

VN>> Действия типа
VN>> char* p = NULL;
VN>> *p = 1;
VN>> тоже вполне укладываются в случай синхронной посылки сигнала самому себе;))

YT> Я лично не вполне в этом уверен :-) Одно дело raise(), а другое --
YT> аппаратный trap...

Почему не уверен? Как аппаратный trap может быть отложенно обработан? ;)
Кстати, где-то я таки видел явное упоминание про его синхронную обработку...

YT>>> Существует ровно один тип статических данных, sig_atomic_t, переменную
YT>>> которого обработчик сигнала может установить. Поведение приложения
YT>>> не определено, если обработчик сигнала обращается к статическим
YT>>> данным любым другим способом.

VN>> 2. Кстати, в comp.unix.programmer никто ничего толком на это не сказал.
VN>> Было только одно замечание типа "они, наверно, криво сформулировали".

YT> Видимо, там все устали от обсуждения разных несостыковок в сигналах.
YT> Ведь уже 20 лет, как оно идет в comp.unix.* время от времени. А вот
YT> собрать все факты вместе никто из них не догадался :-)

Там и более мелочные вещи обсуждаются. Нет, я всё-таки думаю, что то ли
народ в отпуску, то ли решил, что вопрос слишком сложен;)


-netch-

Valentin Nechayev

unread,
Jul 7, 2003, 5:38:26 PM7/7/03
to

>>> Dmitry Miloserdov wrote:

DM>>> Я бы даже с exit vs _exit не согласился.
DM>>> Это вопрос что лучше потерять кусок данных в 99%
DM>>> или получить двожды записанный кусок в 1%?
YT>> Лучше в 100% делать то, что задумано, если железо не глючит ;-)

DM> Ну расскажи как.
DM> Либо теряешь данные в буферах либо рискуешь записать их дважды.
DM> И это даже не проблема stdio(хотя насколько я понимаю там проблем хватает)
DM> это проблема буферизации.

Эта "проблема буферизации" обходится элементарно просто (хотя и непригодно
для некоторых случаев) - запретить все сигналы, сделать неблокирующую запись,
обновить указатели и счётчики в соответствии с результатом записи, разрешить
обратно сигналы.
Кто не верит, что проблема решается - вперёд RTFS ядро. Вместо сигналов -
прерывания, вместо sigprocmask() - splXXX() или save_flags()+cli().
Отличие ситуации с userland'ом - в том, что неизвестно, что же на самом
деле и как будет исполнять функция, и в том, что нет возможности в большинстве
случаев, например, запретить сигналы вокруг malloc(), вызываемого откуда-то
из недр какой-то библиотечной возни. В ядре такая ситуация просто невероятна,
из-за другого уровня требований к обеспечению обработчиков прерываний:
если бы им столько запрещали, сколько запрещается обработчику сигнала -
они бы вообще не смогли работать.

DM>>> Вообщем вообще автор пытается навязать свой подход к
DM>>> написанию программ. А программы бывают не только
DM>>> вокруг poll написанные.
YT>> Hичего я не навязываю. Извините за банальную аллюзию, но я просто
YT>> решил указать, по каким тропинкам можно выйти куда-нибудь, а какие
YT>> заведут в трясину; а дальше каждый сам волен выбирать.

DM> Ну да совершенно не навязываешь.
DM> Этот мост плохой - если когда будете перехотить рядом поедет колонна
DM> грузовиков то грязью уж точно забрызжут. Зато рядом есть отличный
DM> правда он железнодорожный и поезда только ходят раз в сутки

А грязь, которой забрызгали, испортила переносимый товар. И что?
OK, в некоторых случаях можно было бы смириться с потерей какого-то процента,
получая преимущество в скорости перевозки. Но ведь та грязь ещё и может
заблокировать перевозку по мосту...
Аналогия понятна?

DM>>> А sigio/sigurg придумали что вы по ним atomic_t в единичку ставили?
YT>> Если взять на себя труд, можно написать обработчик этих сигналов
YT>> с использованием reentrant functions из списка POSIX. Hо установка
YT>> sig_atomic_t все равно будет переносимее ;-)

DM> А зачем вообще сигналы придумали?
DM> Сделали бы массив этот и ядро бы его заполняло...

Тогда уж лучше сразу SEH сделать. Зачем нам какой-то дурной массив?

YT>> Вообще, меня веселит ситуация, когда под MSDOS программеры страдали,
YT>> что приходится использовать недокументированные ходы, а теперь под
YT>> Unix их стандарты заели :-)))

DM> Кто сказал "стандарты заели"? Просто ты зажал свободу до невозможности.

Яр ничего не зажимал. Он всего лишь описал то, что есть на самом деле.
Если Вам хочется прятать голову в песок и говорить, что этого нет и что это
всё его личные фантазии - ok, пожалуйста. Но не выпускайте последствия
своего солипсизма к людям, они этого не оценят адекватно.

DM> А думать что происходит и какие последствия могут быть все равно надо.
DM> А подумавши можно и без atomic_t прожить.
DM> Во! Кстати какой в нем смысл? Какая разница атомарно присваивание
DM> или нет если ты в переменной хранишь только 0 или 1?

Не знаю, как Вы, я не собираюсь ограничиваться только записью 0 или 1.


-netch-

Valentin Nechayev

unread,
Jul 7, 2003, 5:46:00 PM7/7/03
to

>>> Dmitry Miloserdov wrote:

DM> Вообщем вообще автор пытается навязать свой подход к
DM> написанию программ. А программы бывают не только
DM> вокруг poll написанные.

DM> Например процесс постоянно что-то считает а по siginfo


DM> printf/fflush делает - запретите?

В специальных временнЫх окнах - сколько угодно. Или в другом треде.

DM> А sigio/sigurg придумали что вы по ним atomic_t в единичку ставили?

DM> Ой! это все конечно к вам это к Yar'у ;)

Мне Ваша позиция напоминает детский мультик. "Великое закрытие" называется.
В котором звери пытались показать, что если проблему не описывать - её
не будет. Да вот не получилось ничего - орех вернулся и стукнул по голове.


-netch-

Valentin Nechayev

unread,
Jul 7, 2003, 5:45:59 PM7/7/03
to

>>> Dmitry Miloserdov wrote:

VVI>> Цена вызова syslog в большинстве случаев незначительна. А в том
VVI>> софте, где это имеет значение от syslog'а отказываются.

DM> Что значит "в большинстве случаев"?
DM> В большинстве случаев сигнал будет вызван из вашего poll/select
DM> а оттуда можно пользовать что угодно не обращая внимание на
DM> реентрабельность. Вам i++ запретили а вы "в большинстве случаев"!!!
DM> syslog can block точка.
DM> Может он час в syslog проведет.
DM> Скажете в жизни так не бывает?

В syslog()? Не бывает. Потому что там посылка датаграммы.

DM> А тут вообще похоже не про жизнь - теория одна.

Эта теория выливается в совершенно конкретные и жизненные ситуации.
Вообще, нет ничего практичнее хорошей теории.


-netch-

Vladimir V. Ivanov

unread,
Jul 8, 2003, 2:53:56 AM7/8/03
to
On Mon, 7 Jul 2003 17:19:10 +0000 (UTC)
"Dmitry Miloserdov" <dmi...@bis.ru> wrote:

> Hello, Vladimir!
> You wrote to me on Mon, 7 Jul 2003 08:39:45 +0000 (UTC):
> VVI> Ну и схему с мультиплексированием ввода-вывода никто не >

> VVI> VVI>навязывает.


> VVI> Ясно, что это годиться далеко не для всех случаев жизни.

> VVI> Но если уж


> VVI> выбрали - то играть нужно уже по правилам этой схемы.
> Простите а разве сигналы сделаны не для того чтобы среагировать
> на неожиданно изменившуюсь обстановку

Так программа и реагирует. Разве нет?


> не внося изменений в долгоиграющий модуль?

В него не обязательно вносить изменения. На худой конец, неблокирующую
обёртку вокруг него сделать.

> >> А что получается когда заменить пытаются не все а какие-то куски
> >> производит не лучшее впечатление.
> VVI> Мысль не понял...
> Ну привыкли люди к printf-style
> а убрать FILE* и оставить возможность пользоваться
> форматированным выводом вроде как не особо получается.

Убирание FILE* не означает лишение возможности пользоваться
форматированным вводом-выводом.
Но если уж так исторически сложилось, что много старого
(чужого) кода работающего с stdio -- всего делов-то:
написать signal-safe эмуляцию stdio, с учётом специфики
программы и подхода к обработке сигналов.

Regards,
Vladimir Ivanov

Dmitry Miloserdov

unread,
Jul 8, 2003, 5:23:20 AM7/8/03
to
Hello, Valentin!
You wrote to me on Mon, 7 Jul 2003 21:38:26 +0000 (UTC):
VN> Эта "проблема буферизации" обходится элементарно просто (хотя и
VN> непригодно для некоторых случаев) - запретить все сигналы, сделать
VN> неблокирующую запись, обновить указатели и счётчики в соответствии с
VN> результатом записи, разрешить обратно сигналы.
VN> Кто не верит, что проблема решается - вперёд RTFS ядро.
Я - верю. Верю что проблема обходится.
Вот только не верится что после того как ты представил себе как
это реализовать в юзерленде у тебя язык повернулся назвать это
*элементарно* *просто*!

DM>> Ну да совершенно не навязываешь.
DM>> Этот мост плохой - если когда будете перехотить рядом поедет колонна
DM>> грузовиков то грязью уж точно забрызжут. Зато рядом есть отличный
DM>> правда он железнодорожный и поезда только ходят раз в сутки

VN> А грязь, которой забрызгали, испортила переносимый товар. И что?
VN> OK, в некоторых случаях можно было бы смириться с потерей какого-то
VN> процента, получая преимущество в скорости перевозки. Но ведь та грязь
VN> ещё и может заблокировать перевозку по мосту...
Так и я ведь не предлагаю отказаться от ж-д моста.
Пользуй где нужно, но не пропагандируй как панацею.
И проблему описывай адекватно.
Если следствия ниоткуда не следуют переведи их в правила игры.
В практических соображениях или покажи альтернативы своему
методу или вообще убери практические соображения ну или
сделай так чтобы из правил игры и следствий неотвратимо следовал
этот самый TheRightWay.

DM>> А зачем вообще сигналы придумали?
DM>> Сделали бы массив этот и ядро бы его заполняло...

VN> Тогда уж лучше сразу SEH сделать. Зачем нам какой-то дурной массив?
Тут не понял к чему это сказано. Человек сказал что правильный путь это
что-то вроде sighandler(signum) { seen[signum]=1; } //ну можно ++ в
зависимости от ситуации.
Вот мне и интересно стало если использовать предлагается только так
то зачем эта чехарда с сигналами?

VN> Яр ничего не зажимал. Он всего лишь описал то, что есть на самом деле.
VN> Если Вам хочется прятать голову в песок и говорить, что этого нет и что
VN> это всё его личные фантазии - ok, пожалуйста. Но не выпускайте
VN> последствия своего солипсизма к людям, они этого не оценят адекватно.
Философские проблемы можно оставить за кадром.
И где я сказал что этого нет? Я бы попросил процитировать.

VN> Не знаю, как Вы, я не собираюсь ограничиваться только записью 0 или 1.
О, да конечно! Есть же *стандартные* SIG_ATOMIС_{MIN,MAX}!
Ну в линуксе они есть, еще есть в системе которой все посиксы до фени -
unixware зовут. Что прикажете делать в остальных системах?
Да и вообще где-то гарантировано что max-min>1 ?
Или вы будете разный алгоритм писать для разных размеров?

Dmitry Miloserdov

unread,
Jul 8, 2003, 5:32:02 AM7/8/03
to
Hello, Valentin!

You wrote to me on Mon, 7 Jul 2003 21:45:59 +0000 (UTC):
DM>> Может он час в syslog проведет.
DM>> Скажете в жизни так не бывает?
VN> В syslog()? Не бывает. Потому что там посылка датаграммы.
Вот это новость! И что посылка датаграмы в блокирующий сокет
теперь является неблокирующей операцией?

DM>> А тут вообще похоже не про жизнь - теория одна.

VN> Эта теория выливается в совершенно конкретные и жизненные ситуации.
VN> Вообще, нет ничего практичнее хорошей теории.
Жизненные ситуации я и хотел увидеть. Предложили
как я понял bsd-шный ftp с их пляской вокруг sigurg.
Посмотрим может предложит человек свой вариант
выправленный под свою теорию.

Dmitry Miloserdov

unread,
Jul 8, 2003, 5:45:57 AM7/8/03
to
Hello, Valentin!

You wrote to me on Mon, 7 Jul 2003 21:46:00 +0000 (UTC):
DM>> А sigio/sigurg придумали что вы по ним atomic_t в единичку ставили?
DM>> Ой! это все конечно к вам это к Yar'у ;)
VN> Мне Ваша позиция напоминает детский мультик. "Великое закрытие"
VN> называется. В котором звери пытались показать, что если проблему не
VN> описывать - её не будет. Да вот не получилось ничего - орех вернулся и
VN> стукнул по голове.
А мне Ваша позиция напиминает монолог Жванецкого с фразой про спор
переходящий от безапелляционных утверждений на личность собеседника.

Давайте вернемся к конструктиву все-же.

Dmitry Miloserdov

unread,
Jul 8, 2003, 7:34:21 AM7/8/03
to
Hello, Vladimir!

You wrote to me on Tue, 8 Jul 2003 06:53:56 +0000 (UTC):
>> Простите а разве сигналы сделаны не для того чтобы среагировать
>> на неожиданно изменившуюсь обстановку
VVI> Так программа и реагирует. Разве нет?
может быть слишком поздно да и не за чем.

>> не внося изменений в долгоиграющий модуль?

VVI> В него не обязательно вносить изменения. На худой конец, неблокирующую
VVI> обёртку вокруг него сделать.
Пример: функция func(char *inbuf,char *outbuf)
считает от 0.01 сек до 10мин при этом не делает никаких сисколов.
она не блокирует - она считает.
и например хочется все-таки не досчитывать до конца если клиент ушел.
ну оборачивай. но если ты по сигналам устанавливаешь только флаги то
как будем прерываться?

VVI> Убирание FILE* не означает лишение возможности пользоваться
VVI> форматированным вводом-выводом.
VVI> Но если уж так исторически сложилось, что много старого
VVI> (чужого) кода работающего с stdio -- всего делов-то:
VVI> написать signal-safe эмуляцию stdio, с учётом специфики
VVI> программы и подхода к обработке сигналов.
Угу именно это и пытаются делать. и получается что-то вроде:
функция которая в стиле fprintf выводит первый килобайт.
потом какие-то обертки чтоб переводы строк сохранял и разделители
полей и т.д.
Я просто и сказал что выходит у людей не особо хорошо когда меняют
только кусок.

Vladimir V. Ivanov

unread,
Jul 8, 2003, 9:13:03 AM7/8/03
to
On Tue, 8 Jul 2003 11:34:21 +0000 (UTC)
"Dmitry Miloserdov" <dmi...@bis.ru> wrote:

> VVI> Так программа и реагирует. Разве нет?
> может быть слишком поздно да и не за чем.

Начинаем идти по кругу. Если программа написана _не_криво_
то вовремя.

> VVI> В него не обязательно вносить изменения. На худой конец,

> VVI> неблокирующую обёртку вокруг него сделать.


> Пример: функция func(char *inbuf,char *outbuf)
> считает от 0.01 сек до 10мин при этом не делает никаких сисколов.
> она не блокирует - она считает.
> и например хочется все-таки не досчитывать до конца если клиент ушел.
> ну оборачивай. но если ты по сигналам устанавливаешь только флаги то
> как будем прерываться?

Хм... событие "клиент ушёл" определяется сигналом? Ну ладно,
допустим, хотя на практике такого не встречал.
Сходу два решения, выбираем что больше подходит:

Вариант 1 - пишем асинхронную обёртку этой функции. В нужный момент
вызываем её - она _сразу_ возвращает управление. Ожидаем её результата
в основном цикле (poll/select), и то, только в том случае, если
дожидаться результата вообще нужно. Сигналы будут обработаны вовремя.
Асинхронно выполняемая функция будет остановлена.

Вариант 2 - модифицируем функцию - в цикле расчёта ставим проверку
volatile переменной,
установленной обработчиком сигнала (в таком случае соотв. сигналы
должны быть разблокированы по крайней мере перед вызовом такой функции),
и если что - выходим из расчёта.

> VVI> всего делов-то:


> VVI> написать signal-safe эмуляцию stdio, с учётом специфики
> VVI> программы и подхода к обработке сигналов.
> Угу именно это и пытаются делать. и получается что-то вроде:
> функция которая в стиле fprintf выводит первый килобайт.
> потом какие-то обертки чтоб переводы строк сохранял и разделители
> полей и т.д.

Подробнее, плиз. Не понял... Каким это боком килобайты/разделители
полей/переводы строк мешают обрабатывать сигналы?
Ну если у программиста (скорее даже кодера) руки кривые, то всё
может конечно быть...

Regards,
Vladimir Ivanov

Valentin Nechayev

unread,
Jul 8, 2003, 10:33:49 AM7/8/03
to
>>> Vladimir V. Ivanov wrote:

> VVI>> Так программа и реагирует. Разве нет?
>> может быть слишком поздно да и не за чем.

VVI> Начинаем идти по кругу. Если программа написана _не_криво_
VVI> то вовремя.

Один из основных тезисов VM, по-видимому, в том, что на такое "не кривое"
написание при соблюдении всех правил будет тратиться чрезмерно большое
количество ресурсов. С этим тезисом я согласен.

VVI>> В него не обязательно вносить изменения. На худой конец,
VVI>> неблокирующую обёртку вокруг него сделать.
>> Пример: функция func(char *inbuf,char *outbuf)
>> считает от 0.01 сек до 10мин при этом не делает никаких сисколов.
>> она не блокирует - она считает.
>> и например хочется все-таки не досчитывать до конца если клиент ушел.
>> ну оборачивай. но если ты по сигналам устанавливаешь только флаги то
>> как будем прерываться?

VVI> Хм... событие "клиент ушёл" определяется сигналом? Ну ладно,
VVI> допустим, хотя на практике такого не встречал.
VVI> Сходу два решения, выбираем что больше подходит:

VVI> Вариант 1 - пишем асинхронную обёртку этой функции. В нужный момент
VVI> вызываем её - она _сразу_ возвращает управление. Ожидаем её результата
VVI> в основном цикле (poll/select), и то, только в том случае, если
VVI> дожидаться результата вообще нужно. Сигналы будут обработаны вовремя.
VVI> Асинхронно выполняемая функция будет остановлена.

VVI> Вариант 2 - модифицируем функцию - в цикле расчёта ставим проверку
VVI> volatile переменной,
VVI> установленной обработчиком сигнала (в таком случае соотв. сигналы
VVI> должны быть разблокированы по крайней мере перед вызовом такой функции),
VVI> и если что - выходим из расчёта.

Всё это становится нереальным, если применяются чужие библиотеки.


-netch-

Valentin Nechayev

unread,
Jul 8, 2003, 11:04:00 AM7/8/03
to
>>> Dmitry Miloserdov wrote:

VVI>> Убирание FILE* не означает лишение возможности пользоваться
VVI>> форматированным вводом-выводом.
VVI>> Но если уж так исторически сложилось, что много старого
VVI>> (чужого) кода работающего с stdio -- всего делов-то:
VVI>> написать signal-safe эмуляцию stdio, с учётом специфики
VVI>> программы и подхода к обработке сигналов.

DM> Угу именно это и пытаются делать. и получается что-то вроде:
DM> функция которая в стиле fprintf выводит первый килобайт.

Зачем первый килобайт? Это разве что если памяти не хватило на то
чтобы сохранить всё - ну IMO это слишком экстремальный случай.

>>> Dmitry Miloserdov wrote:

DM> To: net...@lucky.net
DM> From: "Dmitry Miloserdov" <dmi...@bis.ru>
DM> Newsgroups: fido7.ru.unix.prog
DM> Subject: Сигналы: 20 лет в тумане
DM> Date: Tue, 8 Jul 2003 09:45:57 +0000 (UTC)
DM>
DM> Hello, Valentin!


DM> You wrote to me on Mon, 7 Jul 2003 21:46:00 +0000 (UTC):
DM>>> А sigio/sigurg придумали что вы по ним atomic_t в единичку ставили?
DM>>> Ой! это все конечно к вам это к Yar'у ;)
VN>> Мне Ваша позиция напоминает детский мультик. "Великое закрытие"
VN>> называется. В котором звери пытались показать, что если проблему не
VN>> описывать - её не будет. Да вот не получилось ничего - орех вернулся и
VN>> стукнул по голове.

DM> А мне Ваша позиция напиминает монолог Жванецкого с фразой про спор
DM> переходящий от безапелляционных утверждений на личность собеседника.

Где был переход на личность? Пальцем покажите.

DM> Давайте вернемся к конструктиву все-же.

Я бы рад. Вернуться к конструктиву не хочешь ты - утверждаешь, что, мол,
придумали тут слишком жёсткие правила, нельзя по ним работать, поэтому
надо их нарушать.

DM>>> Может он час в syslog проведет.
DM>>> Скажете в жизни так не бывает?
VN>> В syslog()? Не бывает. Потому что там посылка датаграммы.

DM> Вот это новость! И что посылка датаграмы в блокирующий сокет
DM> теперь является неблокирующей операцией?

Всегда являлась, насколько я вижу и знаю.
Возражения принимаются в виде указаний на места в коде стека сокетов или
в виде цитат из Стивенса.

VN>> Эта "проблема буферизации" обходится элементарно просто (хотя и
VN>> непригодно для некоторых случаев) - запретить все сигналы, сделать
VN>> неблокирующую запись, обновить указатели и счётчики в соответствии с
VN>> результатом записи, разрешить обратно сигналы.
VN>> Кто не верит, что проблема решается - вперёд RTFS ядро.

DM> Я - верю. Верю что проблема обходится.
DM> Вот только не верится что после того как ты представил себе как
DM> это реализовать в юзерленде у тебя язык повернулся назвать это
DM> *элементарно* *просто*!

"Просто", "реализуемо", "аккуратно" - разные вещи. В данном случае было
чётко обозначено, что именно считать "просто". При том, что во многих
случаях тем не менее нереализуемо.

DM>>> Ну да совершенно не навязываешь.
DM>>> Этот мост плохой - если когда будете перехотить рядом поедет колонна
DM>>> грузовиков то грязью уж точно забрызжут. Зато рядом есть отличный
DM>>> правда он железнодорожный и поезда только ходят раз в сутки
VN>> А грязь, которой забрызгали, испортила переносимый товар. И что?
VN>> OK, в некоторых случаях можно было бы смириться с потерей какого-то
VN>> процента, получая преимущество в скорости перевозки. Но ведь та грязь
VN>> ещё и может заблокировать перевозку по мосту...

DM> Так и я ведь не предлагаю отказаться от ж-д моста.
DM> Пользуй где нужно, но не пропагандируй как панацею.

Ты предлагаешь другое - игнорировать те несколько процентов случаев,
когда срабатывает неправильно, потому что тебе так проще писать.
А сколько потом с результатом будут мучиться другие - видимо, уже неважно.

DM> И проблему описывай адекватно.

Что тут неадекватно? Описывается, как правильно.
Хочешь делать неправильно? Делай, я ж не возражаю. Только не в тех
программах, которые я потом буду использовать.

DM> Если следствия ниоткуда не следуют переведи их в правила игры.

Они уже давно в правилах игры.

DM> В практических соображениях или покажи альтернативы своему
DM> методу или вообще убери практические соображения ну или
DM> сделай так чтобы из правил игры и следствий неотвратимо следовал
DM> этот самый TheRightWay.

Что там не следует? По пунктам, пожалуйста.

DM>>> А зачем вообще сигналы придумали?
DM>>> Сделали бы массив этот и ядро бы его заполняло...
VN>> Тогда уж лучше сразу SEH сделать. Зачем нам какой-то дурной массив?

DM> Тут не понял к чему это сказано. Человек сказал что правильный путь это
DM> что-то вроде sighandler(signum) { seen[signum]=1; } //ну можно ++ в
DM> зависимости от ситуации.
DM> Вот мне и интересно стало если использовать предлагается только так
DM> то зачем эта чехарда с сигналами?

Какая именно чехарда?

VN>> Не знаю, как Вы, я не собираюсь ограничиваться только записью 0 или 1.

DM> О, да конечно! Есть же *стандартные* SIG_ATOMIС_{MIN,MAX}!
DM> Ну в линуксе они есть, еще есть в системе которой все посиксы до фени -
DM> unixware зовут. Что прикажете делать в остальных системах?
DM> Да и вообще где-то гарантировано что max-min>1 ?
DM> Или вы будете разный алгоритм писать для разных размеров?

Сказано, что это integer type. Поэтому границы значений не могут быть
уже чем для char'а.


-netch-

Vladimir V. Ivanov

unread,
Jul 8, 2003, 11:37:26 AM7/8/03
to
On Tue, 8 Jul 2003 14:33:49 +0000 (UTC)
Valentin Nechayev <ne...@segfault.kiev.ua> wrote:

> Один из основных тезисов VM, по-видимому, в том, что на такое "не >
> кривое" написание при соблюдении всех правил будет тратиться чрезмерно
> большое количество ресурсов. С этим тезисом я согласен.

Это точно. Причём не только ресурсов на написание а иногда даже и
в потере производительности самой программы.

Аналогия навеяная сигналами:
Правило для пешеходов гласит - не переходить дорогу на
красный свет. Однако если видимость хорошая, машин вокруг не
наблюдается редко кто зелёного света ждёт.

Соответсвенно руководствоваться правилом можно в зависимости
от преследуемой цели:
- во что бы то не стало сохранить жизнь (надёжность, безопасность)
=> следуем правилам
- добраться быстрее (скорость, в том числе скорость разработки)
=> игнорируем правила, там где считаем их неактуальными

Так что при разработке ПО нужно пользоваться не только правилами, но и
здравым смыслом. Гвозди микроскопом забивать глупо, а вот для
микроскопических гвоздей он пригодится. Чего-то на аналогии потянуло :)

> VVI> Вариант 1 - пишем асинхронную обёртку этой функции. В нужный

> ....

> VVI> Вариант 2 - модифицируем функцию - в цикле расчёта ставим

> VVI> проверку

> Всё это становится нереальным, если применяются чужие библиотеки.

Ну, при первом варианте всё не так уж и плохо. Особенно если библиотека
tread-safe. Иначе - сериализация обработки в одном треде или - несколько
fork'ов на старте программы и взаимодествие с дочерними процессами.
По типу как squid делает.
Конечно некая потеря производительности, зато простота реализации и, как
бонус, повышенная надёжность из-за изоляции.

Regards,
Vladimir Ivanov

Vladimir V. Ivanov

unread,
Jul 8, 2003, 11:49:40 AM7/8/03
to
Hi Valentin!

On Tue, 8 Jul 2003 15:04:00 +0000 (UTC)
Valentin Nechayev <ne...@segfault.kiev.ua> wrote:

> VN>> В syslog()? Не бывает. Потому что там посылка датаграммы.
> DM> Вот это новость! И что посылка датаграмы в блокирующий сокет
> DM> теперь является неблокирующей операцией?
>
> Всегда являлась, насколько я вижу и знаю.
> Возражения принимаются в виде указаний на места в коде стека сокетов > или
> в виде цитат из Стивенса.

Логично, что для реализации SOCK_DGRAM блокировка, в принципе, не нужна вообще, а вот, интересно, как насчёт SOCK_RDM ?

Regards,
Vladimir Ivanov

Valentin Nechayev

unread,
Jul 8, 2003, 12:54:50 PM7/8/03
to
>>> Vladimir V. Ivanov wrote:

VVI> Логично, что для реализации SOCK_DGRAM блокировка, в принципе, не нужна вообще, а вот, интересно, как насчёт SOCK_RDM ?

А тут есть хоть один человек, который бы его пользовал вместе с соответствующим
протокольным стеком (IPX/SPX, например)?


-netch-

Sergey Vlasov

unread,
Jul 8, 2003, 1:16:52 PM7/8/03
to

SPX - это SOCK_SEQPACKET. А вот где действительно водится SOCK_RDM?

--
Отправлено через сервер Форумы@mail.ru - http://talk.mail.ru

Alexander Kotelnikov

unread,
Jul 8, 2003, 3:48:34 PM7/8/03
to
>>>>> On Tue, 8 Jul 2003 15:04:00 +0000 (UTC)
>>>>> "VN" == Valentin Nechayev <ne...@segfault.kiev.ua> wrote:
VN>
DM> Может он час в syslog проведет.
DM> Скажете в жизни так не бывает?
VN> В syslog()? Не бывает. Потому что там посылка датаграммы.
DM> Вот это новость! И что посылка датаграмы в блокирующий сокет
DM> теперь является неблокирующей операцией?
VN>
VN> Всегда являлась, насколько я вижу и знаю.
VN> Возражения принимаются в виде указаний на места в коде стека сокетов или
VN> в виде цитат из Стивенса.

Я, конечно, совершенно не вписываюсь в указанные рамки, но замечу, что
AF_LOCAL SOCK_DGRAM сокеты в linux могут блокироваться на посылке.

--
Alexander Kotelnikov
Saint-Petersburg, Russia

Alexander Orlovsky

unread,
Jul 9, 2003, 5:15:20 AM7/9/03
to
On Tue, 8 Jul 2003 15:04:00 +0000 (UTC), Valentin Nechayev
<ne...@segfault.kiev.ua> wrote:

>Всегда являлась, насколько я вижу и знаю.
>Возражения принимаются в виде указаний на места в коде стека сокетов или
>в виде цитат из Стивенса.

А что, Стивенс безгрешен? :)
Да и времени уже прошло...

Dmitry Miloserdov

unread,
Jul 9, 2003, 6:41:17 AM7/9/03
to
Hello, Valentin!

You wrote to me on Tue, 8 Jul 2003 15:04:00 +0000 (UTC):
DM>> Угу именно это и пытаются делать. и получается что-то вроде:
DM>> функция которая в стиле fprintf выводит первый килобайт.
VN> Зачем первый килобайт? Это разве что если памяти не хватило на то
VN> чтобы сохранить всё - ну IMO это слишком экстремальный случай.
Ну это было отвлечение от темы и это действительно экстремальный случай
который почти везде где оставили функционал printf заменив работу с FILE*.
все делают или просто snprintf или бывает даже len+=snprintf(buf+len,...) -
жуть.
(это естественно к сигналам отношение не имеет это так сказать последствия)

DM>> Давайте вернемся к конструктиву все-же.

VN> Я бы рад. Вернуться к конструктиву не хочешь ты - утверждаешь, что,
VN> мол, придумали тут слишком жёсткие правила, нельзя по ним работать,
VN> поэтому надо их нарушать.


VN> Всегда являлась, насколько я вижу и знаю.
VN> Возражения принимаются в виде указаний на места в коде стека сокетов
VN> или в виде цитат из Стивенса.
Нету у меня исходников соляриса но там блокируется - проверено.
POSIX не говорит что не блокируется. и более того говорит что syslog
может еще форкаться и ждать пока ребенок завершиться.

VN> Ты предлагаешь другое - игнорировать те несколько процентов случаев,
VN> когда срабатывает неправильно, потому что тебе так проще писать.
VN> А сколько потом с результатом будут мучиться другие - видимо, уже
VN> неважно.
Нет! не предлагаю игнорировать!
Я утверждаю что можно вызывать нереэнтрабельные функции безопасно
из сигналов если думаешь что ты делаешь и видишь последствия своих
действий.
И второе что утверждал (где % были) это про то когда не можешь
(не хочешь?!) внести масштабные изменения то лучше из двух зол
брать все-же меньшее. Хотя все зависит от ситуации - думать что
делаешь всегда надо.

VN> Что там не следует? По пунктам, пожалуйста.
По пунктам отдельно напишу про вторую редакцию.
Я просто ее пропустил спорил по первой и не в обиду автору статья
мне практически вся не понравилась начиная заголовком и стилем.
Да еще и похоже автор не определил (на тот момент по крайней мере)
своей целевой аудитории и бросался из крайности в крайность
что аргументировать что нет.

DM>>>> А зачем вообще сигналы придумали?
DM>>>> Сделали бы массив этот и ядро бы его заполняло...
VN>>> Тогда уж лучше сразу SEH сделать. Зачем нам какой-то дурной массив?
DM>> Тут не понял к чему это сказано. Человек сказал что правильный путь

DM>> это что-то вроде sighandler(signum) { seen[signum]=1; } //ну можно ++
DM>> в зависимости от ситуации. Вот мне и интересно стало если использовать
DM>> предлагается только так то зачем эта чехарда с сигналами?
VN> Какая именно чехарда?
Если _всем_ от сигналов нужен _только_ такой массив, то
согласись ядру проще вести такой массив нежели вызывать
какой-то юзеровский код - как минимум переключений меньше.

VN> Сказано, что это integer type. Поэтому границы значений не могут быть
VN> уже чем для char'а.
Откуда это следует?
где-то сказано что integer type это не меньше char'а?

Dmitry Miloserdov

unread,
Jul 9, 2003, 2:00:10 PM7/9/03
to
Hello, Valentin!
You wrote to me on Tue, 8 Jul 2003 15:04:00 +0000 (UTC):
VN> Что там не следует? По пунктам, пожалуйста.
Значится так - что-то вроде рецензии:
Статья разделена на 4 части.
1. вступление
автор жалуется на жизнь. как много наисправлял он
глюков похожих друг на друга ( исправленый вариант ftpd еще ждем )
2. правила игры
по идее тут должно быть описаны правила достаточные для
понимания
3. следствия
по видимому должны были следовать из правил.
4. что делать
из всего предыдущего рекомендации по написанию безглючных программ
Итак по пунктам:
С правилами спорить трудно, но еслиб ты указал про возможность проверки
на ожидаемый сигнал без снятия блокировки не понадобилась эта схема с
обработчиком который только и делает что устанавливает флаг
(кстати это ведь тоже небезопасно если основной код сбрасывает флаг не
сразу)

с1: функцию, которую нельзя вызвать из себя, нельзя вызывать из себя.
ладно пусть будет
с2: третий абзац точно не следует из предыдущего.
откуда взялся этот список? откуда зависимость от
реентрабельности от номера сигнала в kill?
еще безопаснее полагать что _все_ нереентрабельные.
а еще безопаснее что сигналы не работают.
с3: необходимость атомарности точно не следует и вообще
неочевидна
с4: аналогично с3. да и вообще частный случай с3.
с5: верно но не следует из правил.
с6: не следует из правил и вообще непонятно откуда следует.
с7: если вынести фразу из скобок в начало то пункт будет
менее пугающим. Кстати переменную sig_atomic_t можно
заменить на любую переменную объявленную volatile.
с8,9: не следуют из правил.и там автор вроде как пришел к
противоречию. Если цель запутать читателя то так и оставьте.
Если же нет, то наверно все-же стоит описать конструктивный
вывод если есть.
с11: "Следует помнить" за одну фразу читать дальше не стоило!
но прочел. Так кому говорите надо помнить? Тем кто придумал
эти функции? или вы все же пишете статью для тех кто этими
функциями пользоваться будет!? тогда им надо помнить о
существовании таких функций, а что где должно быть неразрывно
позвольте им самим решать или напишите когда именно нужна
неразрывность и как часто.
с12: Не согласен полностью.
(хотя в принципе следует из правил. *наконец-то*)
там где *нужен* exit с помощью _exit его не заменить.
Если atexit не пользовал может все таки стоит дать
шанс буфера положить на диск. Могут конечно и
SIGSEGV прислать но extremely unlikely. Ну повесить
_exit на всё, чтоб уходить всегда достойно.
c13: чем "любая ввода/вывода" показалось лучше чем любая способная
вернуть EINTR? что будем делать со stdio?! оно не возвращает EINTR.
// Ужаснулся представив прерванный fprintf - сколько там недописалось -
загадка.

Ну часть "что-делать" представлена скромно двумя вариантами,
или нет - тремя. Третий вариант не пользоваться сигналами - но
мы его не рассматриваем - он для фантастов приведен.
Второй вариант мы тоже использовать не будем потому как автор
забыл разрешить нам пользоваться write (следствие 2)! а что делать
с пайпой при помощи signal,alarm,_exit. я не знаю. но это еще мелочь
по сравнению с тем что у нас filehandle может не влезть в sig_atomic_t,
да и читать нам его запретили ;)
Первый метод также не избавит вас от необходимости думать что делаешь
Ex: текущий ftpd во фре для sigurg использует как раз описаный метод
и слекгостью потеряет второй сигнал пришедший во время обработки
первого.

Вопросы к оппоненту есть?

Eugene Karpachov

unread,
Jul 10, 2003, 12:45:35 AM7/10/03
to
Wed, 9 Jul 2003 18:00:10 +0000 (UTC) Dmitry Miloserdov написал:

> с3: необходимость атомарности точно не следует и вообще
> неочевидна

Она следует из асинхронности сигнала.

> с7: если вынести фразу из скобок в начало то пункт будет
> менее пугающим. Кстати переменную sig_atomic_t можно
> заменить на любую переменную объявленную volatile.

Нет.

volatile struct blabla {
int a;
char b;
};

- такое можно использовать как флаг? Я бы не стал.

> с12: Не согласен полностью.
> (хотя в принципе следует из правил. *наконец-то*)
> там где *нужен* exit с помощью _exit его не заменить.
> Если atexit не пользовал может все таки стоит дать
> шанс буфера положить на диск. Могут конечно и
> SIGSEGV прислать но extremely unlikely. Ну повесить

^^^^^^^^^^^^^^^^^^

Это страшное заклинание, которое призвано добавить убедительности
простому "авось"?


> Второй вариант мы тоже использовать не будем потому как автор
> забыл разрешить нам пользоваться write (следствие 2)! а что делать
> с пайпой при помощи signal,alarm,_exit. я не знаю. но это еще мелочь
> по сравнению с тем что у нас filehandle может не влезть в sig_atomic_t,
> да и читать нам его запретили ;)

Это да. Ну, не автор же запретил :)

--
jk

Valentin Nechayev

unread,
Jul 10, 2003, 1:32:15 AM7/10/03
to

>>> Dmitry Miloserdov wrote:

VN>> Что там не следует? По пунктам, пожалуйста.

DM> Значится так - что-то вроде рецензии:
DM> Статья разделена на 4 части.
DM> Итак по пунктам:
DM> С правилами спорить трудно, но еслиб ты указал про возможность проверки
DM> на ожидаемый сигнал без снятия блокировки

Что имеется в виду? sigpending()?

DM> не понадобилась эта схема с
DM> обработчиком который только и делает что устанавливает флаг
DM> (кстати это ведь тоже небезопасно если основной код сбрасывает флаг не
DM> сразу)

Ну вот ещё одно "если"... А если он вообще не обращает внимания на флаг,
или пишет в начале в него случайное значение?;)) Не надо рассказывать
про все возможные ошибки. Надо рассказывать про то, как правильно,
и какие бывают типичные ошибки.

DM> с1: функцию, которую нельзя вызвать из себя, нельзя вызывать из себя.
DM> ладно пусть будет

Ты чрезмерно упрощаешь. И неентерабельный кусок кода не обязательно оформлен
в виде отдельной функции. И формулировка Яра, в отличие от твоей, намекает
(и правильно) не только на критические секции в виде единственного куска
кода, но и на множественные куски с доступом к общему ресурсу, который
следует защищать от асинхронного доступа.

DM> с2: третий абзац точно не следует из предыдущего.
DM> откуда взялся этот список? откуда зависимость от
DM> реентрабельности от номера сигнала в kill?

Это где такое? Я смотрю исправленную версию с
message-id: <be1kdo$5gu$1...@comp.chem.msu.su>, там никакого номера сигнала
не упоминается. Само же следствие 2 вполне следует из следствия 1: библиотечные
функции, о которых ты ничего не знаешь, следует считать нереентерабельными
и соответственно избегать их вызова в асинхронных обработчиках.

DM> еще безопаснее полагать что _все_ нереентрабельные.
DM> а еще безопаснее что сигналы не работают.

Это всё тоже имеет смысл. Но если платформа соответствует SUS, то у неё
есть реентерабельные функции в составе, описанном стандартом.

DM> с3: необходимость атомарности точно не следует и вообще
DM> неочевидна

Смысл атомарной установки - спасение стека от переполнения. Представь
себе, что ты решил запрет сигналов вынести не на уровень sigaction(),
а на уровень кода обработчика сигнала. То есть первая команда - sigprocmask()
с запретом других сигналов того же обработчика (или родственных), последняя -
разрешение обратно. Ну и ещё представим себе наличие SA_NODEFER, для полноты
картины. Если тебе 1000 раз пошлют один и тот же сигнал, у тебя получится
рекурсия в 1000 уровней на обработчике. OK, вернём SA_RESETHAND обратно.
Ну не 1000 будет, а всего лишь по количеству обрабатываемых сигналов - ну,
скажем, 20. Тоже может быть многовато (стеки не везде резиновые).

DM> с4: аналогично с3. да и вообще частный случай с3.
Ok.

DM> с5: верно но не следует из правил.
Ok.

DM> с6: не следует из правил и вообще непонятно откуда следует.

Это следует из дословного чтения текста SUSv3, которое тебе вообще-то
следовало бы почитать уже давно. Я приводил цитаты.

DM> с7: если вынести фразу из скобок в начало то пункт будет
DM> менее пугающим. Кстати переменную sig_atomic_t можно
DM> заменить на любую переменную объявленную volatile.

Грубо неверный вывод. Переменная может быть и структурного типа,
присваивание структуры структуре давно работает и разрешено, а на практике
делается через имплицитный memmove(). Но даже если это не структура,
может быть, например, целочисленный тип, который не может быть записан в память
за одну команду процессора - как long long (он же int64_t) на i386.
sig_atomic_t потому и назначен отдельно, что реализация гарантирует,
что запись его в память будет атомарной (в контексте одного процессора).

DM> с8,9: не следуют из правил.и там автор вроде как пришел к
DM> противоречию. Если цель запутать читателя то так и оставьте.
DM> Если же нет, то наверно все-же стоит описать конструктивный
DM> вывод если есть.

=== cut ===
Следовательно, если обработчик вызывает б.ф., то он должен
вначале сохранить значение errno, а перед возвратом восстановить его.
=== end cut ===

Это что, по-твоему, не конструктивный вывод? А что тогда конструктивный?
И про "не следует из правил" - недопонимание сложности ситуации.
Противоречие не у Яра, а у авторов стандартов.

DM> с11: "Следует помнить" за одну фразу читать дальше не стоило!
DM> но прочел. Так кому говорите надо помнить? Тем кто придумал
DM> эти функции? или вы все же пишете статью для тех кто этими
DM> функциями пользоваться будет!? тогда им надо помнить о
DM> существовании таких функций, а что где должно быть неразрывно
DM> позвольте им самим решать или напишите когда именно нужна
DM> неразрывность и как часто.

Ты бессмысленно обструктивен. Если так думать, то выкинь вообще все
издания с рекомендациями, потому что они кому-то не потребуются, а кому-то
не подойдут. А я думаю, что своя голова на плечах должна быть всегда,
и если ей подскажут, что ещё можно вот так-то сделать, то это как минимум
не вредно.

DM> с12: Не согласен полностью.

Обоснование?
Против exit() в асинхронном обработчике обоснование есть - он вызывает
массу н.б.ф. Ну а у тебя какое обоснование его использовать?
То, что тебе проще так написать?

DM> (хотя в принципе следует из правил. *наконец-то*)
DM> там где *нужен* exit с помощью _exit его не заменить.

Да. А это значит, что там, где он нужен, нельзя завершать прямо из
обработчика. В этом есть что-то удивительное? Если да - скажи, что.

DM> Если atexit не пользовал может все таки стоит дать
DM> шанс буфера положить на диск. Могут конечно и
DM> SIGSEGV прислать но extremely unlikely.

Когда на это "extremely unlikely" тебе придумают эксплойт, песни будут
совсем другие.

DM> Ну повесить
DM> _exit на всё, чтоб уходить всегда достойно.
DM> c13: чем "любая ввода/вывода" показалось лучше чем любая способная
DM> вернуть EINTR? что будем делать со stdio?! оно не возвращает EINTR.

Да-а? А у меня, представь себе, программа работает, которая успешно
использует то, что из stdio функций возвращается EINTR при прерывании
сигналом. А другая - то, что stdio возвращает EAGAIN на неблокирующих
сокетах. И что мне теперь, работу менять?;))

DM> // Ужаснулся представив прерванный fprintf - сколько там недописалось -
DM> загадка.

Upon successful completion, the fprintf() and printf() functions
shall return the number of bytes transmitted.

DM> Ну часть "что-делать" представлена скромно двумя вариантами,
DM> или нет - тремя. Третий вариант не пользоваться сигналами - но
DM> мы его не рассматриваем - он для фантастов приведен.
DM> Второй вариант мы тоже использовать не будем потому как автор
DM> забыл разрешить нам пользоваться write (следствие 2)!

write() есть async-signal-safe функция.

DM> а что делать
DM> с пайпой при помощи signal,alarm,_exit. я не знаю. но это еще мелочь
DM> по сравнению с тем что у нас filehandle может не влезть в sig_atomic_t,
DM> да и читать нам его запретили ;)

Ты можешь в обработчике сигнала читать и писать всё что угодно, если не
требуется взаимодействие с асинхронным обработчиком через такие переменные.
То есть если, например, записал дескриптор, а уже потом включил обработчик,
и дескриптор не меняешь, пока сигнал не заблокирован.

DM> Первый метод также не избавит вас от необходимости думать что делаешь
DM> Ex: текущий ftpd во фре для sigurg использует как раз описаный метод
DM> и слекгостью потеряет второй сигнал пришедший во время обработки
DM> первого.

Вполне возможно, что он ещё не доведён до ума.

DM> Вопросы к оппоненту есть?

К которому? К тебе? См. выше. Даже слишком много вопросов, напоминает
как минимум погром;)))


-netch-

Valentin Nechayev

unread,
Jul 10, 2003, 1:45:37 AM7/10/03
to

>>> Dmitry Miloserdov wrote:

DM>>> Угу именно это и пытаются делать. и получается что-то вроде:
DM>>> функция которая в стиле fprintf выводит первый килобайт.
VN>> Зачем первый килобайт? Это разве что если памяти не хватило на то
VN>> чтобы сохранить всё - ну IMO это слишком экстремальный случай.

DM> Ну это было отвлечение от темы и это действительно экстремальный случай
DM> который почти везде где оставили функционал printf заменив работу с FILE*.
DM> все делают или просто snprintf или бывает даже len+=snprintf(buf+len,...) -
DM> жуть.
DM> (это естественно к сигналам отношение не имеет это так сказать последствия)

Тяжело. snprintf() тоже, скорее всего, делает malloc().
Про код с `len += snprintf()' вообще ничего хорошего сказать нельзя - это
грубая ошибка, дающая почётное право на место в багтреке. В роли автора
дырявой программы;)) (Я подразумеваю тут, что речь идёт о фактической
длине полученного, а не о желаемой длине.)

VN>> Всегда являлась, насколько я вижу и знаю.
VN>> Возражения принимаются в виде указаний на места в коде стека сокетов
VN>> или в виде цитат из Стивенса.

DM> Нету у меня исходников соляриса но там блокируется - проверено.
DM> POSIX не говорит что не блокируется. и более того говорит что syslog
DM> может еще форкаться и ждать пока ребенок завершиться.

OK, это утверждение отменяю. Пусть себе блокируется.

VN>> Ты предлагаешь другое - игнорировать те несколько процентов случаев,
VN>> когда срабатывает неправильно, потому что тебе так проще писать.
VN>> А сколько потом с результатом будут мучиться другие - видимо, уже
VN>> неважно.

DM> Нет! не предлагаю игнорировать!
DM> Я утверждаю что можно вызывать нереэнтрабельные функции безопасно
DM> из сигналов если думаешь что ты делаешь и видишь последствия своих
DM> действий.

Ты хочешь сказать, что ты _предполагаешь_, что ты знаешь последствия
своих действий. Что ты _экстраполируешь_, что всё будет нормально работать,
не зная реализации.
Возьмём BSD'шный syslog(). Он создаёт через fwopen() объект типа FILE,
работающий на запись в символьный буфер, и пишет туда вывод. fwopen()
вызывает malloc(). Ты предполагал, что syslog() захочет malloc()?
Я уверен, что нет. Потому что не входит это в непосредственную логику
работы syslog().

VN>> Что там не следует? По пунктам, пожалуйста.

DM> По пунктам отдельно напишу про вторую редакцию.

Увидел, ответил.

DM>>>>> А зачем вообще сигналы придумали?
DM>>>>> Сделали бы массив этот и ядро бы его заполняло...
VN>>>> Тогда уж лучше сразу SEH сделать. Зачем нам какой-то дурной массив?
DM>>> Тут не понял к чему это сказано. Человек сказал что правильный путь
DM>>> это что-то вроде sighandler(signum) { seen[signum]=1; } //ну можно ++
DM>>> в зависимости от ситуации. Вот мне и интересно стало если использовать
DM>>> предлагается только так то зачем эта чехарда с сигналами?
VN>> Какая именно чехарда?

DM> Если _всем_ от сигналов нужен _только_ такой массив, то
DM> согласись ядру проще вести такой массив нежели вызывать
DM> какой-то юзеровский код - как минимум переключений меньше.

man sigpending. А ещё man sigwait; man sigwaitinfo
Видна чёткая тенденция по уходу от асинхронной обработки, где возможно.

VN>> Сказано, что это integer type. Поэтому границы значений не могут быть
VN>> уже чем для char'а.

DM> Откуда это следует?
DM> где-то сказано что integer type это не меньше char'а?

В C99, AFAIK.


-netch-

Dmitry Miloserdov

unread,
Jul 10, 2003, 4:47:23 AM7/10/03
to
Hello, Valentin!
You wrote to me on Thu, 10 Jul 2003 05:45:37 +0000 (UTC):
VN> Тяжело. snprintf() тоже, скорее всего, делает malloc().
VN> Про код с `len += snprintf()' вообще ничего хорошего сказать нельзя -
VN> это грубая ошибка, дающая почётное право на место в багтреке. В роли
VN> автора дырявой программы;))
ну в багтрак за одно это еще рано. рано до тех пока буковка 'n' в snprintf
реально не используется. но хорошего действительно не скажешь.
Если автор уверен что буфера хватит то пока некрасиво но не более.

VN> Ты хочешь сказать, что ты _предполагаешь_, что ты знаешь последствия
VN> своих действий. Что ты _экстраполируешь_, что всё будет нормально
VN> работать, не зная реализации.
VN> Возьмём BSD'шный syslog(). Он создаёт через fwopen() объект типа FILE,
VN> работающий на запись в символьный буфер, и пишет туда вывод. fwopen()
VN> вызывает malloc(). Ты предполагал, что syslog() захочет malloc()?
VN> Я уверен, что нет.
На момент прочтения я _знал_ что конкретно syslog FreeBSD использует fwopen.
Я _предполагал_ что fwopen может вызывать malloc. И то что syslog на других
системах может делать что-то еще более неожиданное тоже предполагал почитав
ieee p1003. Но это не суть.
Да, каюсь, я экстраполирую и предполагаю. Но немного не так.
Я уверен что любой syscall реентрабелен по определению.
Я считаю что если одноименная с сисколом функция есть в POSIX
то она реентрабельна(правда форк из обработчика звать бы не стал).
Да по теории тут ошибка - сисколы системно зависимы, но это теория...
Ну и разделить безопасные участки кода и небезопасные никто не запрещал.
ex: mysyslog(){ if(safe) { safe=0; syslog(); safe=1; } }

VN> man sigpending. А ещё man sigwait; man sigwaitinfo
VN> Видна чёткая тенденция по уходу от асинхронной обработки, где возможно.
Именно эти маны!
И возвращаемся к вопросу зачем эта чехарда с сигналами?
Не приходила мысль что сигналы это все-же ассинхронное средство и нужны
они (придуманы были) для ассинхронной обработки. Если _вам_ они не нужны
то это _ваш_ выбор. Повторюсь: можно написать программу
А предоставление синхронных средств обработки - не есть уход от ассинхонной.

VN>>> Сказано, что это integer type. Поэтому границы значений не могут быть
VN>>> уже чем для char'а.
DM>> Откуда это следует?
DM>> где-то сказано что integer type это не меньше char'а?

VN> В C99, AFAIK.
Цитату тогда, плиз.
enum { true,false,whocares }- это не integer type?

Lev Walkin

unread,
Jul 10, 2003, 4:55:05 AM7/10/03
to

Dmitry Miloserdov wrote:
>
> VN>>> Сказано, что это integer type. Поэтому границы значений не могут быть
> VN>>> уже чем для char'а.
> DM>> Откуда это следует?
> DM>> где-то сказано что integer type это не меньше char'а?
> VN> В C99, AFAIK.
> Цитату тогда, плиз.
> enum { true,false,whocares }- это не integer type?

sizeof() на этот enum даст то же, что sizeof(int). Ну и где он меньше
char'а?


--
Lev Walkin
v...@netli.com

Dmitry Miloserdov

unread,
Jul 10, 2003, 5:21:03 AM7/10/03
to
Hello, Lev!

You wrote to Dmitry Miloserdov on Thu, 10 Jul 2003 08:55:05 +0000 (UTC):
>> Цитату тогда, плиз.
>> enum { true,false,whocares }- это не integer type?
LW> sizeof() на этот enum даст то же, что sizeof(int). Ну и где он меньше
LW> char'а?
по занимаемой памяти больше. но это ничего не значит.
Ты можешь быть уверенным что записав в этот enum
символ ты этот символ из него и вытащишь? или это
все же отдано на откуп компилятору?
struct { int hex1:4; ... }*s;
s->hex1 - это ineteger type?
//проблемы атомарности и волатильности оставим за кадром

Lev Walkin

unread,
Jul 10, 2003, 6:00:50 AM7/10/03
to

Dmitry Miloserdov wrote:
> Hello, Lev!
> You wrote to Dmitry Miloserdov on Thu, 10 Jul 2003 08:55:05 +0000 (UTC):
> >> Цитату тогда, плиз.
> >> enum { true,false,whocares }- это не integer type?
> LW> sizeof() на этот enum даст то же, что sizeof(int). Ну и где он меньше
> LW> char'а?
> по занимаемой памяти больше. но это ничего не значит.
> Ты можешь быть уверенным что записав в этот enum
> символ ты этот символ из него и вытащишь?

К счастью, да.

"The type of enumeration constants is EXPLICITLY
defined as int":
C99: 6.4.4.3/2

Впрочем, есть в некоторых компиляторах специальные буратинские флаги,
которые позволяют "ужимать" enum'ы.
Но! Минимальная фракция - байты, а не биты.

(-fshort-enums в GCC - это минимум 2 байта).


> или это
> все же отдано на откуп компилятору?

С точки зрения "записи _символа_" (байта) - не дано.

> struct { int hex1:4; ... }*s;
> s->hex1 - это ineteger type?

Можно, я не буду это коментировать?.. Спасибо.

> //проблемы атомарности и волатильности оставим за кадром
>
> With best regards, Dmitry Miloserdov. E-mail: dmi...@bis.ru
>
>


--
Lev Walkin
v...@netli.com

Serge Ryabchun

unread,
Jul 10, 2003, 6:42:20 AM7/10/03
to
Lev Walkin <v...@netli.com> wrote:

> (-fshort-enums в GCC - это минимум 2 байта).

`-fshort-enums'
Allocate to an `enum' type only as many bytes as it needs for the
declared range of possible values. Specifically, the `enum' type
will be equivalent to the smallest integer type which has enough
room.

as many as it needs - это 1 байт для 0..255

--
Serge Ryabchun s...@energy.uch.net

Vladimir V. Ivanov

unread,
Jul 10, 2003, 6:45:23 AM7/10/03
to
Hi, Dmitry!

> ex: mysyslog(){ if(safe) { safe=0; syslog(); safe=1; } }
И в результате - race condition.

Regards,
Vladimir Ivanov

Yar Tikhiy

unread,
Jul 10, 2003, 5:47:44 AM7/10/03
to
Valentin Nechayev <ne...@segfault.kiev.ua> wrote in
<2003070721...@iv.nn.kiev.ua>:

VN>>> Действия типа
VN>>> char* p = NULL;
VN>>> *p = 1;
VN>>> тоже вполне укладываются в случай синхронной посылки сигнала самому
VN>>> себе;))
YT>> Я лично не вполне в этом уверен :-) Одно дело raise(), а другое --
YT>> аппаратный trap...

VN> Почему не уверен? Как аппаратный trap может быть отложенно обработан? ;)
VN> Кстати, где-то я таки видел явное упоминание про его синхронную
VN> обработку...

Я не уверен в том, что аппаратные traps доставляются в контексте,
позволяющем вести себя, как в синхронном обработчике: обращаться без
разбору к статическим данным, вызывать н.б.ф. и так далее...

--
Yar

Dmitry Miloserdov

unread,
Jul 10, 2003, 9:21:15 AM7/10/03
to
Hello, Valentin!
You wrote to me on Thu, 10 Jul 2003 05:32:15 +0000 (UTC):
VN> Что имеется в виду? sigpending()?
да

DM>> не понадобилась эта схема с
DM>> обработчиком который только и делает что устанавливает флаг
DM>> (кстати это ведь тоже небезопасно если основной код сбрасывает флаг не
DM>> сразу)

VN> Ну вот ещё одно "если"... А если он вообще не обращает внимания на
VN> флаг, или пишет в начале в него случайное значение?;)) Не надо
VN> рассказывать про все возможные ошибки. Надо рассказывать про то, как
VN> правильно, и какие бывают типичные ошибки.
если эта ошибка была в ftpd на момент выхода FreeBSD5.1-R она типичная или
нет?
" src/libexec/ftpd/ftpd.c,v 1.144 2003/02/11 14:10:48 yar " - тот самый Яр
или нет?

DM>> с1: функцию, которую нельзя вызвать из себя, нельзя вызывать из себя.
DM>> ладно пусть будет

VN> Ты чрезмерно упрощаешь. И неентерабельный кусок кода не обязательно
VN> оформлен в виде отдельной функции. И формулировка Яра, в отличие от
VN> твоей, намекает (и правильно) не только на критические секции в виде
VN> единственного куска кода, но и на множественные куски с доступом к
VN> общему ресурсу, который следует защищать от асинхронного доступа.
Кошмар!
Нереентрабельный участок кода при этом неоформленный в виде функции.
И вызывается из разных мест. И кто-то его хочет вызывать из сигнала...
Ну в принципе я могу себе это представить...
Но может в данном случае программисту стоит вернуться на более привычные
ему языки, а такой код _никому_ _никогда_ не показывать.

VN> Это где такое? Я смотрю исправленную версию с
VN> message-id: <be1kdo$5gu$1...@comp.chem.msu.su>, там никакого номера
VN> сигнала не упоминается. Само же следствие 2 вполне следует из следствия
VN> 1: библиотечные функции, о которых ты ничего не знаешь, следует считать
VN> нереентерабельными и соответственно избегать их вызова в асинхронных
VN> обработчиках.
Версия та самая. номер сигнала упоминается. только не в kill ( моя ошибка )
а в signal.
чем _exit, signal, abort лучше других функций?

VN> Смысл атомарной установки - спасение стека от переполнения. Представь
VN> себе, что ты решил запрет сигналов вынести не на уровень sigaction(),
VN> а на уровень кода обработчика сигнала. То есть первая команда -
VN> sigprocmask() с запретом других сигналов того же обработчика (или
VN> родственных), последняя - разрешение обратно. Ну и ещё представим себе
VN> наличие SA_NODEFER, для полноты картины. Если тебе 1000 раз пошлют один
VN> и тот же сигнал, у тебя получится рекурсия в 1000 уровней на
VN> обработчике. OK, вернём SA_RESETHAND обратно. Ну не 1000 будет, а всего
VN> лишь по количеству обрабатываемых сигналов - ну, скажем, 20. Тоже может
VN> быть многовато (стеки не везде резиновые).
Да уж!
Поставить SA_NODEFER и первой командой делать sigprocmask()? Мсье знает толк
в извращениях ;)
И почему тогда вы тут не сказали что запрещено использовать локальные
переменные
в обработчиках, а потом аргументировать экономией стека?


DM>> с6: не следует из правил и вообще непонятно откуда следует.

VN> Это следует из дословного чтения текста SUSv3, которое тебе вообще-то
VN> следовало бы почитать уже давно. Я приводил цитаты.
можно узнать message-id или цитату еще раз?
или дату если в этом треде?

VN> Грубо неверный вывод. Переменная может быть и структурного типа,
VN> присваивание структуры структуре давно работает и разрешено, а на
VN> практике делается через имплицитный memmove(). Но даже если это не
VN> структура, может быть, например, целочисленный тип, который не может
VN> быть записан в память за одну команду процессора - как long long (он же
VN> int64_t) на i386. sig_atomic_t потому и назначен отдельно, что
VN> реализация гарантирует, что запись его в память будет атомарной (в
VN> контексте одного процессора).
Ты пытаешься доказать неатомарность изменений а я их и неоспаривал.
Я оспаривал сам факт необходимости атомарности. (достаточность очевидна).
Давай я приведу свою формулировку которая не противоречит c7
(хотя и не является ее следствием). Правда она противоречит с6.
Итак немного позитива в этом треде:
c?: Вы можете обращаться из обработчика к любой переменной
любого типа любым способом если она объявлена как volatile и
при любом обращении к этой переменной запрещены сигналы
которые тоже обращаются к этим переменным.

VN> === cut ===
VN> Следовательно, если обработчик вызывает б.ф., то он должен
VN> вначале сохранить значение errno, а перед возвратом восстановить его.
VN> === end cut ===
VN> Это что, по-твоему, не конструктивный вывод? А что тогда
VN> конструктивный? И про "не следует из правил" - недопонимание сложности
VN> ситуации. Противоречие не у Яра, а у авторов стандартов.
=====================
Если стандартная библиотечная функция, вызванная из
обработчика сигнала, вернула ошибку, то значение переменной errno
может быть не определено.

=====================
Я трижды прочитал эту фразу прежде чем написать про нее.
Вы считаете она не допускает двусмысленности? Ваше с Яром право.

про с11:
VN> Ты бессмысленно обструктивен. Если так думать, то выкинь вообще все
VN> издания с рекомендациями, потому что они кому-то не потребуются, а
VN> кому-то не подойдут. А я думаю, что своя голова на плечах должна быть
VN> всегда, и если ей подскажут, что ещё можно вот так-то сделать, то это
VN> как минимум не вредно.
Рекомендации - наздоровье, вот только они должны быть по теме
(не стоит включать в опывание автомобиля, что улицу нужно переходить на
зеленый свет) и рекомендации должны _быть_. Вы нашли их в с11? Я так и
не понял что там рекомендуют. Ну ладно это _ваш_ стиль и _вы_ за него горой.

DM>> с12: Не согласен полностью.

VN> Обоснование?
VN> Против exit() в асинхронном обработчике обоснование есть - он вызывает
VN> массу н.б.ф. Ну а у тебя какое обоснование его использовать?
VN> То, что тебе проще так написать?
То что выход непосредственно из обработчика это метод сохранить модульность.
Ты в Т.З. на функцию будешь писать что кроме прочего функция не реже 2 раз в
секунду должна проверять флаг? Так разработчик будет alrm перехватывать чтоб
чтоб твою задачу выполнить;)

DM>> c13: чем "любая ввода/вывода" показалось лучше чем любая способная
DM>> вернуть EINTR? что будем делать со stdio?! оно не возвращает EINTR.

VN> Да-а? А у меня, представь себе, программа работает, которая успешно
VN> использует то, что из stdio функций возвращается EINTR при прерывании
VN> сигналом. А другая - то, что stdio возвращает EAGAIN на неблокирующих
VN> сокетах. И что мне теперь, работу менять?;))

DM>> // Ужаснулся представив прерванный fprintf - сколько там

DM>> недописалось - загадка.
VN> Upon successful completion, the fprintf() and printf() functions
VN> shall return the number of bytes transmitted.
Угу а сколько хотелось? Как определить сколько там недописалось!?
И как потом дописать кусок?


VN> write() есть async-signal-safe функция.
Вы мне это говорите?! Скажите Яру!

VN> Ты можешь в обработчике сигнала читать и писать всё что угодно, если не
VN> требуется взаимодействие с асинхронным обработчиком через такие
VN> переменные. То есть если, например, записал дескриптор, а уже потом
VN> включил обработчик, и дескриптор не меняешь, пока сигнал не
VN> заблокирован.
Вы мне это говорите?! Прочтите сами и прочтите то что вы сказали по поводу
6,7
кстати про этот медод можно взглянуть на inetd во фре.
volatile забыт, сигналы установлены до pipe(),NONBLOCK забыт.
дедлоки не страшны.

VN> К которому? К тебе? См. выше. Даже слишком много вопросов, напоминает
VN> как минимум погром;)))
Ну разгром так разгром. Можете праздновать победу.
Не думаю правда что победители осилили развеять "20летний туман".
Да и на какую аудиторию автор рассчитывал не понятно (не удивлюсь что
непонятно и автору)
// чего я сюда вмешался - сам не пойму...

Всем спасибо - все свободны

Dmitry Miloserdov

unread,
Jul 10, 2003, 9:27:21 AM7/10/03
to
Hello, Vladimir!

You wrote to me on Thu, 10 Jul 2003 10:45:23 +0000 (UTC):
>> ex: mysyslog(){ if(safe) { safe=0; syslog(); safe=1; } }
VVI> И в результате - race condition.
Обоснуй!
(все помнят, что разговор идет только про сигналы)

Vladimir V. Ivanov

unread,
Jul 10, 2003, 11:50:11 AM7/10/03
to
On Thu, 10 Jul 2003 13:27:21 +0000 (UTC)
"Dmitry Miloserdov" <dmi...@bis.ru> wrote:

> Hello, Vladimir!
> You wrote to me on Thu, 10 Jul 2003 10:45:23 +0000 (UTC):
> >> ex: mysyslog(){ if(safe) { safe=0; syslog(); safe=1; } }
> VVI> И в результате - race condition.
> Обоснуй!
> (все помнят, что разговор идет только про сигналы)

Приношу извинения. Погорячился.

Regards,
Vladimir Ivanov

Yar Tikhiy

unread,
Jul 10, 2003, 11:03:11 AM7/10/03
to
Dmitry Miloserdov <dmi...@bis.ru> wrote in <behl6k$2ale$1...@ddt.demos.su>:

DM> Значится так - что-то вроде рецензии:

Спасибо за местами чрезмерно агрессивную, но в целом конструктивную
критику :-)

DM> Статья разделена на 4 части.
DM> 1. вступление
DM> автор жалуется на жизнь. как много наисправлял он
DM> глюков похожих друг на друга ( исправленый вариант ftpd еще ждем )

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

DM> Итак по пунктам:
DM> С правилами спорить трудно, но еслиб ты указал про возможность проверки
DM> на ожидаемый сигнал без снятия блокировки не понадобилась эта схема с
DM> обработчиком который только и делает что устанавливает флаг
DM> (кстати это ведь тоже небезопасно если основной код сбрасывает флаг не
DM> сразу)

Если ты о sigwait(), то это функция из threaded библиотеки, а я
пока говорил только о сигналах в однонитевой среде. С другой стороны,
sigpending() не очистит активный сигнал, а sigsuspend() не позволит
узнать, какой сигнал пришел, если не установить флаг.

DM> с2: третий абзац точно не следует из предыдущего.

Этот абзац -- практический комментарий к собственно следствию.
Hадо, что ли, мне статью в DocBook или какой-нибудь troff macro
пакет перевести, чтобы путаницы не возникало...

DM> откуда взялся этот список?

POSIX, SUS.

DM> откуда зависимость от
DM> реентрабельности от номера сигнала в kill?

Hе в kill(), а в signal(). См. C99 #7.14.1.1 абзац 5.

DM> еще безопаснее полагать что _все_ нереентрабельные.
DM> а еще безопаснее что сигналы не работают.

Разные стандарты предъявляют требования разной жесткости и
ограниченности. Какой выбрать -- дело разработчика. Одно дело,
когда программа должна работать везде, где есть компилятор C89;
другое -- когда можно рассчитывать на совместимость с POSIX/SUS.

DM> с3: необходимость атомарности точно не следует и вообще
DM> неочевидна

Спасибо, аргумент принимается. Конечно, чтобы к общим данным по
очереди обращаться, такая атомарность не нужна. Достаточно вызвать
sigprocmask() перед обращением, даже не в самом начале обработчика.
А переполнение стека я пока не обсуждаю.

DM> с4: аналогично с3. да и вообще частный случай с3.

Частный, но практически значимый.

DM> с5: верно но не следует из правил.

Hеобходимость volatile следует из асинхронности сигналов (правило 2).

DM> с6: не следует из правил и вообще непонятно откуда следует.

Еще раз прочти правило 3. Там сказано: единственное, что
обработчик гарантированно может делать со статическими данными --
это установить переменную типа sig_atomic_t. Все остальные действия
могут быть непереносимыми и приводить к сбою приложения.

DM> с7: если вынести фразу из скобок в начало то пункт будет
DM> менее пугающим.

В общем случае обработчик не может делать ++/-- над статическими
данными. См. следствие 6. Поэтому фраза в скобках и в конце.

DM> Кстати переменную sig_atomic_t можно
DM> заменить на любую переменную объявленную volatile.

Как уже говорилось, volatile сам по себе не обеспечивает атомарности.

DM> с8,9: не следуют из правил.и там автор вроде как пришел к
DM> противоречию. Если цель запутать читателя то так и оставьте.
DM> Если же нет, то наверно все-же стоит описать конструктивный
DM> вывод если есть.

Hе согласен. С.8 следует из п.3, так как errno -- это статическая
переменная, и тип у нее -- не volatile sig_atomic_t. С.9 следует
из п.2, так как вызов обработчика может произойти вот где:

if (somestdfunc() < 0) {
/*
* Здесь доставили сигнал в обработчик, вызывающий
* стандартные функции; значение errno изменилось.
*/
err(EX_OSERR, "somestdfunc"); /* что скажет err() ? */
}

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

DM> с11: "Следует помнить" за одну фразу читать дальше не стоило!
DM> но прочел. Так кому говорите надо помнить? Тем кто придумал
DM> эти функции? или вы все же пишете статью для тех кто этими
DM> функциями пользоваться будет!? тогда им надо помнить о
DM> существовании таких функций, а что где должно быть неразрывно
DM> позвольте им самим решать или напишите когда именно нужна
DM> неразрывность и как часто.

Спасибо, принимается. Hапишу, в каких случаях это действительно
нужно.

DM> с12: Hе согласен полностью.
DM> (хотя в принципе следует из правил. *наконец-то*)
DM> там где *нужен* exit с помощью _exit его не заменить.
DM> Если atexit не пользовал может все таки стоит дать
DM> шанс буфера положить на диск. Могут конечно и
DM> SIGSEGV прислать но extremely unlikely. Hу повесить
DM> _exit на всё, чтоб уходить всегда достойно.

Если exit(3) нельзя заменить на _exit(2), то надо переделывать
программу. Hапример, устанавливать флаг sig_atomic_t и проверять
его из основного потока.

DM> c13: чем "любая ввода/вывода" показалось лучше чем любая способная
DM> вернуть EINTR? что будем делать со stdio?! оно не возвращает EINTR.

Согласно SUS, stdio как раз возвращает EINTR.

DM> // Ужаснулся представив прерванный fprintf - сколько там недописалось -
DM> загадка.

Хотя fprintf и возвращает кол-во успешно записанных байтов, не
очевидно, что с ним делать. Вывод: не надо прерывать *printf,
т.к. в их случае partial success не несет никакого смысла.

Кстати, я сейчас обнаружил (на FreeBSD 5-CURRENT), что вывод через
stdio может вообще не прерываться сигналами; ввод, правда, нормально
прерывается по EINTR. Дело в том, по-моему, что прерванный write(2)
возвращает partial success, если удалось хоть что-нибудь записать,
и stdio вызывает его еще раз в цикле, передавая остаток данных.

DM> Hу часть "что-делать" представлена скромно двумя вариантами,
DM> или нет - тремя. Третий вариант не пользоваться сигналами - но
DM> мы его не рассматриваем - он для фантастов приведен.

Почему же? Одно дело -- перехватывать основные сигналы вроде SIGINT,
чтобы аккуратно завершать приложение, а совсем другое -- пытаться
создать сложную схему взаимодействия между процессами на основе
сигналов.

DM> Второй вариант мы тоже использовать не будем потому как автор
DM> забыл разрешить нам пользоваться write (следствие 2)! а что делать
DM> с пайпой при помощи signal,alarm,_exit. я не знаю. но это еще мелочь
DM> по сравнению с тем что у нас filehandle может не влезть в sig_atomic_t,
DM> да и читать нам его запретили ;)

А поскольку этот вариант, вдобавок ко всему, грозит deadlock'ами
и потерей сигналов, то я его вообще уберу из новой редакции ;-)

DM> Первый метод также не избавит вас от необходимости думать что делаешь

А что, моя статья предлагает избавление от необходимости думать? Значит,
или я совсем плохо написал ;-( или ты не очень внимательно прочел ;-)

DM> Ex: текущий ftpd во фре для sigurg использует как раз описаный метод
DM> и слекгостью потеряет второй сигнал пришедший во время обработки
DM> первого.

В частном случае ftpd потерять второй SIGURG нестрашно. Если он и
потеряется, то последовательность IAC+IP+IAC+DM+"ABRT" будет без
проблем считана в основном цикле обработки команд. То же произойдет,
если доставка сигнала задержится (ведь время между посылкой и
доставкой ничем не ограничено). Так что хуже не становится.

--
Yar

Valentin Nechayev

unread,
Jul 11, 2003, 2:42:41 AM7/11/03
to

>>> Dmitry Miloserdov wrote:

VN>> Ты чрезмерно упрощаешь. И неентерабельный кусок кода не обязательно
VN>> оформлен в виде отдельной функции. И формулировка Яра, в отличие от
VN>> твоей, намекает (и правильно) не только на критические секции в виде
VN>> единственного куска кода, но и на множественные куски с доступом к
VN>> общему ресурсу, который следует защищать от асинхронного доступа.

DM> Кошмар!
DM> Нереентрабельный участок кода при этом неоформленный в виде функции.
DM> И вызывается из разных мест. И кто-то его хочет вызывать из сигнала...
DM> Ну в принципе я могу себе это представить...
DM> Но может в данном случае программисту стоит вернуться на более привычные
DM> ему языки, а такой код _никому_ _никогда_ не показывать.

f1() {
... что-то делаем...
mutex_lock( &zzz_m );
++zzz;
mutex_unlock( &zzz_m );
...
}

f2() {
...
mutx_lock( &zzz_m );
--zzz;
mutex_unlock( &zzz_m );
...
}

И какие возражения против того, что этот нереентерабельный кусок не будет
оформлен в виде функции? Или ты будешь настаивать, что захват лока,
модификация значения и возврат лока должны быть оформлены в виде
отдельной функции? А какого чёрта, собственно?

VN>> Смысл атомарной установки - спасение стека от переполнения. Представь
VN>> себе, что ты решил запрет сигналов вынести не на уровень sigaction(),
VN>> а на уровень кода обработчика сигнала. То есть первая команда -
VN>> sigprocmask() с запретом других сигналов того же обработчика (или
VN>> родственных), последняя - разрешение обратно. Ну и ещё представим себе
VN>> наличие SA_NODEFER, для полноты картины. Если тебе 1000 раз пошлют один
VN>> и тот же сигнал, у тебя получится рекурсия в 1000 уровней на
VN>> обработчике. OK, вернём SA_RESETHAND обратно. Ну не 1000 будет, а всего
VN>> лишь по количеству обрабатываемых сигналов - ну, скажем, 20. Тоже может
VN>> быть многовато (стеки не везде резиновые).

DM> Да уж!
DM> Поставить SA_NODEFER и первой командой делать sigprocmask()? Мсье знает толк
DM> в извращениях ;)

Я? Я такое не пользую.

DM> И почему тогда вы тут не сказали что запрещено использовать локальные
DM> переменные
DM> в обработчиках, а потом аргументировать экономией стека?

Потому что количество локальных переменных управляемо. В отличие от
количества сигналов, поступивших из внешнего источника.

DM>>> с6: не следует из правил и вообще непонятно откуда следует.
VN>> Это следует из дословного чтения текста SUSv3, которое тебе вообще-то
VN>> следовало бы почитать уже давно. Я приводил цитаты.

DM> можно узнать message-id или цитату еще раз?
DM> или дату если в этом треде?

<2003063006...@iv.nn.kiev.ua> в comp.unix.programmer.
Здесь действительно не цитировал, только упоминал.

VN>> Грубо неверный вывод. Переменная может быть и структурного типа,
VN>> присваивание структуры структуре давно работает и разрешено, а на
VN>> практике делается через имплицитный memmove(). Но даже если это не
VN>> структура, может быть, например, целочисленный тип, который не может
VN>> быть записан в память за одну команду процессора - как long long (он же
VN>> int64_t) на i386. sig_atomic_t потому и назначен отдельно, что
VN>> реализация гарантирует, что запись его в память будет атомарной (в
VN>> контексте одного процессора).

DM> Ты пытаешься доказать неатомарность изменений а я их и неоспаривал.
DM> Я оспаривал сам факт необходимости атомарности. (достаточность очевидна).
DM> Давай я приведу свою формулировку которая не противоречит c7
DM> (хотя и не является ее следствием). Правда она противоречит с6.
DM> Итак немного позитива в этом треде:
DM> c?: Вы можете обращаться из обработчика к любой переменной
DM> любого типа любым способом если она объявлена как volatile и
DM> при любом обращении к этой переменной запрещены сигналы
DM> которые тоже обращаются к этим переменным.

Согласен, но это уже другой случай: это случай __синхронного__ обработчика
сигнала. Ключевое слово подчёркнуто.
Более того, если верить гипотезе Яра, что обработчик сигнала может иметь
другую видимость памяти и другие странности, то и это не проходит.
А для асинхронного обработчика (то есть, когда заранее неизвестно, в какой
момент он вызовется, и известно, что имеет право вызваться в любой момент
в широком диапазоне) это неприменимо.

VN>> === cut ===
VN>> Следовательно, если обработчик вызывает б.ф., то он должен
VN>> вначале сохранить значение errno, а перед возвратом восстановить его.
VN>> === end cut ===
VN>> Это что, по-твоему, не конструктивный вывод? А что тогда
VN>> конструктивный? И про "не следует из правил" - недопонимание сложности
VN>> ситуации. Противоречие не у Яра, а у авторов стандартов.

DM> =====================
DM> Если стандартная библиотечная функция, вызванная из
DM> обработчика сигнала, вернула ошибку, то значение переменной errno
DM> может быть не определено.

DM> =====================
DM> Я трижды прочитал эту фразу прежде чем написать про нее.
DM> Вы считаете она не допускает двусмысленности? Ваше с Яром право.

Двусмысленность !== противоречие.
Я бы формулировку тоже исправил. Но проблема именно в стандартах.

DM> про с11:


VN>> Ты бессмысленно обструктивен. Если так думать, то выкинь вообще все
VN>> издания с рекомендациями, потому что они кому-то не потребуются, а
VN>> кому-то не подойдут. А я думаю, что своя голова на плечах должна быть
VN>> всегда, и если ей подскажут, что ещё можно вот так-то сделать, то это
VN>> как минимум не вредно.

DM> Рекомендации - наздоровье, вот только они должны быть по теме
DM> (не стоит включать в опывание автомобиля, что улицу нужно переходить на
DM> зеленый свет) и рекомендации должны _быть_. Вы нашли их в с11? Я так и
DM> не понял что там рекомендуют. Ну ладно это _ваш_ стиль и _вы_ за него горой.

Рекомендуется блокировку сигналов выполнять неразрывно с другой операцией.
Как/то: pselect(), sa_mask, sigsuspend() и так далее.

DM>>> с12: Не согласен полностью.
VN>> Обоснование?
VN>> Против exit() в асинхронном обработчике обоснование есть - он вызывает
VN>> массу н.б.ф. Ну а у тебя какое обоснование его использовать?
VN>> То, что тебе проще так написать?

DM> То что выход непосредственно из обработчика это метод сохранить модульность.
DM> Ты в Т.З. на функцию будешь писать что кроме прочего функция не реже 2 раз в
DM> секунду должна проверять флаг? Так разработчик будет alrm перехватывать чтоб
DM> чтоб твою задачу выполнить;)

Ну так кто виноват в том, что количество НБФ так велико и покрывает
фактически всю библиотеку?
Набери себе реентерабельных функций и делай через них что угодно со свободой
обращения с сигналами. Я ж не против.;))

DM>>> // Ужаснулся представив прерванный fprintf - сколько там
DM>>> недописалось - загадка.
VN>> Upon successful completion, the fprintf() and printf() functions
VN>> shall return the number of bytes transmitted.

DM> Угу а сколько хотелось? Как определить сколько там недописалось!?
DM> И как потом дописать кусок?

Это уже другой вопрос. Я бы вообще сделал asprintf(), копирование вывода
в буфер в памяти, а далее вывод из буфера сколько получится.
Ну кто виноват, что stdio тут не заточено на работу с сигналами, и
не может быть заточено - по крайней мере в некоторых местах - из-за его
дизайна?

VN>> write() есть async-signal-safe функция.

DM> Вы мне это говорите?! Скажите Яру!

Ему зачем? Он и так знает. Откуда вывод, что это надо отдельно говорить?

VN>> Ты можешь в обработчике сигнала читать и писать всё что угодно, если не
VN>> требуется взаимодействие с асинхронным обработчиком через такие
VN>> переменные. То есть если, например, записал дескриптор, а уже потом
VN>> включил обработчик, и дескриптор не меняешь, пока сигнал не
VN>> заблокирован.

DM> Вы мне это говорите?! Прочтите сами и прочтите то что вы сказали по поводу
DM> 6,7

То всё про _асинхронный_ обработчик. Ну и с поправкой на то, что Яр утверждает,
что обработчик сигнала имеет другую видимость памяти, а я говорю, что таких
архитектур у меня в поле зрения нет и долго не будет.
Все описанные правила где чего атомарно ставить - для _асинхронных_
обработчиков. Я это утверждал неоднократно. Если Яр это не пишет - его
проблема, в том варианте, что пойдёт в FAQ, я это в любом случае буду
добавлять.

DM> кстати про этот медод можно взглянуть на inetd во фре.
DM> volatile забыт, сигналы установлены до pipe(),NONBLOCK забыт.
DM> дедлоки не страшны.

Значит, он ещё не исправлен. Логично, да-а? ;)))

VN>> К которому? К тебе? См. выше. Даже слишком много вопросов, напоминает
VN>> как минимум погром;)))

DM> Ну разгром так разгром. Можете праздновать победу.

Я сказал "погром", а не "разгром". О победе речь не идёт, важно достижение
истины и её практической реализуемости.


-netch-

Vladimir V. Ivanov

unread,
Jul 11, 2003, 2:45:26 AM7/11/03
to
On Thu, 10 Jul 2003 19:03:11 +0400
Yar Tikhiy <y...@comp.chem.msu.su> wrote:

Hi!


> DM> по сравнению с тем что у нас filehandle может не влезть в

> DM> sig_atomic_t, да и читать нам его запретили ;)


>
> А поскольку этот вариант, вдобавок ко всему, грозит deadlock'ами
> и потерей сигналов, то я его вообще уберу из новой редакции ;-)

IMHO, не стоит убирать ибо вариант прост в реализации и
работоспособен, во многих случаях может быть весьма полезеным,
при следующем ограничении:
- количество пришедших сигналов одного с одним и тем же номером
не имеет значения для программы,

и правилах применения:
- дескриптор записи пайпа volatile static переменная,
инициализируемая _до_ установки обработчиков сигнала.
- дескриптор записи пайпа находится в режиме O_NONBLOCK
- обработчик сигнала пишет в пайп 1 байт (любой байт, причём
записался он или не записался из-за переполнения буфера
абсолютно не имеет значения)
и устанавливает, как предложил Валентин, флаг в массиве
sig_atomic_t (seen[sig] = 1)
- обработка сигналов происходит по выходу из poll/select путём
вычитывания данных из пайпа (только для сброса буфера) и,
последующей, проверки и сброса установленных флагов:
for (...i...)
if (seen[i]) { seen[i] = 0; handle_signal(i); }

--
Regards,
Vladimir Ivanov

Valentin Nechayev

unread,
Jul 11, 2003, 2:52:40 AM7/11/03
to

>>> Yar Tikhiy wrote:

YT>>> Я лично не вполне в этом уверен :-) Одно дело raise(), а другое --
YT>>> аппаратный trap...
VN>> Почему не уверен? Как аппаратный trap может быть отложенно обработан? ;)
VN>> Кстати, где-то я таки видел явное упоминание про его синхронную
VN>> обработку...

YT> Я не уверен в том, что аппаратные traps доставляются в контексте,
YT> позволяющем вести себя, как в синхронном обработчике: обращаться без
YT> разбору к статическим данным, вызывать н.б.ф. и так далее...

Я не знаю ни одной платформы, где бы было тут ограничение по контексту.
Да, мой опыт ограничен линуксами, фрями и солярисами. Но это... мнэ-э-э...
процентов 95 или больше местного юниксового мира.
Кстати:
YT> позволяющем вести себя, как в синхронном обработчике: обращаться без
YT> разбору к статическим данным, вызывать н.б.ф. и так далее...

вот этого я не понял. Синхронный обработчик - это тот, который вызывался
сразу после разрешающего sigprocmask()? Так с точки зрения ядра у него
и у асинхронного обработчика нет никакой разницы в построении контекста,
стека и так далее. Или по крайней мере не описано такой разницы.


-netch-

Dmitry Miloserdov

unread,
Jul 11, 2003, 8:12:57 AM7/11/03
to
Hello, Valentin!
You wrote to me on Fri, 11 Jul 2003 06:42:41 +0000 (UTC):

Мне кажется я понял причину нашего непонимания друг друга.
Лично я привык:
- читать что написано а не то что подразумевалось.
- ститать что если автор говорит "А" то на протежении всей статьи
он действительно считает что "А" действительно верно.
- быть уверенным что если утверждение называют "следствием" то
оно действительно является следствием вышеизложенного.
(возможны ссылки на общеизвестные факты при условии хотябы
упоминания этих фактов, приведение же самих формулировок
расценивают как акт уважения к читателю).
Ну это _моя_ позиция(может математическое образование сказывается).
Можете быть не согласны но именно с этой позиции прошу рассматривать
мои постинги.
Ты же готов согласится с фразой, потому как ты понял, что автор хотел
сказать, а не то, что он сказал. Для беседы вполне хорошо - быстрее к
консенсусу придете. Но к написанию статьи рассчитанной не на
конкретного человека ...

Ладно вернемся к барьеру ;)


Слишком много процитировал - сам виноват. Попробуем так:
VN>>> И неентерабельный кусок кода не обязательно


VN>>> оформлен в виде отдельной функции.

а. Участок кода один.
б. Он нереентерабельный.
в. Раз критична реентерабельность значит хотят вызывать из разных мест.
г. Он не оформлен в виде отдельной функции.
д. По контексту - язык С.
Вы считаете такой код приемлимым?

или все-таки нужна _глобальная_ замена
s/реентерабельный/async-signal-safe/g ?
btw: async-signal-safe сигналобезопасным что-ли обзывать?

Даже с учетом замены:
сигналоНЕбезопасный участок кода это либо вызов
сигналоНЕбезопасной функции либо НЕбезопасное обращение
к статическим данным. Всего два случая - может не стоить запутывать
читателя?

DM>> И почему тогда вы тут не сказали что запрещено использовать локальные
DM>> переменные
DM>> в обработчиках, а потом аргументировать экономией стека?

VN> Потому что количество локальных переменных управляемо. В отличие от
VN> количества сигналов, поступивших из внешнего источника.
Можно поспорить ну да ладно.
Просто когда ты начинаешь заботится о стеке не говоря что проблема только
в стеке то ты неадекватно описываешь проблему. Хоть бы приоритеты
поставили как в RFC например MUST/SHOULD/MAY.

VN> Согласен, но это уже другой случай: это случай __синхронного__
VN> обработчика сигнала. Ключевое слово подчёркнуто.
VN> Более того, если верить гипотезе Яра, что обработчик сигнала может
VN> иметь другую видимость памяти и другие странности, то и это не
VN> проходит. А для асинхронного обработчика (то есть, когда заранее
VN> неизвестно, в какой момент он вызовется, и известно, что имеет право
VN> вызваться в любой момент в широком диапазоне) это неприменимо.
void access_arbitrary_global_variable(int access_type,void *loc,void
*var,size_t len)
{ sigprocmask(..); memcpy(access_type?loc:var,access_type?var:loc,len);
sigprocmask(..); }
Это _синхронная_ обработка сигналов?! Если да то вопросов больше нет.

DM>> Я трижды прочитал эту фразу прежде чем написать про нее.
DM>> Вы считаете она не допускает двусмысленности? Ваше с Яром право.

VN> Двусмысленность !== противоречие.
Нет. Просто после троекратного прочтения я понял неправильно.
И противоречие показалось не только в стандартах.

VN> Рекомендуется блокировку сигналов выполнять неразрывно с другой
VN> операцией. Как/то: pselect(), sa_mask, sigsuspend() и так далее.
Ладно пусть так. Но это издевательство над читателем.

DM>> То что выход непосредственно из обработчика это метод сохранить

DM>> модульность. Ты в Т.З. на функцию будешь писать что кроме прочего
DM>> функция не реже 2 раз в секунду должна проверять флаг? Так разработчик
DM>> будет alrm перехватывать чтоб чтоб твою задачу выполнить;)
VN> Ну так кто виноват в том, что количество НБФ так велико и покрывает
VN> фактически всю библиотеку?
VN> Набери себе реентерабельных функций и делай через них что угодно со
VN> свободой обращения с сигналами. Я ж не против.;))
Ну-ну. только как это решает проблему exit vs _exit?
Если от модульности не хочется отказываться.

DM>> Угу а сколько хотелось? Как определить сколько там недописалось!?
DM>> И как потом дописать кусок?

VN> Это уже другой вопрос. Я бы вообще сделал asprintf(), копирование
VN> вывода в буфер в памяти, а далее вывод из буфера сколько получится.
VN> Ну кто виноват, что stdio тут не заточено на работу с сигналами, и
VN> не может быть заточено - по крайней мере в некоторых местах - из-за его
VN> дизайна?
asprintf - непортабельно.
fwopen - непортабельно.
Heavy voodoo с помощью snprintf возможно и сработает но тоже есть риск
нарваться на непортабельность де-факто.

VN>>> write() есть async-signal-safe функция.
DM>> Вы мне это говорите?! Скажите Яру!

VN> Ему зачем? Он и так знает. Откуда вывод, что это надо отдельно
VN> говорить?
Прочитай следствие 2 _до_ _конца_.

DM>> Вы мне это говорите?! Прочтите сами и прочтите то что вы сказали по

DM>> поводу 6,7
VN> То всё про _асинхронный_ обработчик.
Давайте определимся что такое _асинхронный_ обработчик.
Ну чтоб не только вам понятно было.

VN> Ну и с поправкой на то, что Яр утверждает, что обработчик сигнала
VN> имеет другую видимость памяти, а я говорю, что таких архитектур у меня
VN> в поле зрения нет и долго не будет.
Кстати, если не рассматривать таких гипотетических архитектур
то для флагов можно использовать любую volatile переменную.

DM>> кстати про этот медод можно взглянуть на inetd во фре.
DM>> volatile забыт, сигналы установлены до pipe(),NONBLOCK забыт.
DM>> дедлоки не страшны.

VN> Значит, он ещё не исправлен. Логично, да-а? ;)))
Парадокс в том что если четко следовать правилам Яра то найти
правильно написанную программу будет непростой труд.
И не потому что все неправильные(пусть и много) а потому
как рамки всеже жестковаты не для реальной жизни писаны.

Dmitry Miloserdov

unread,
Jul 11, 2003, 11:05:36 AM7/11/03
to
Hello, Lev!
You wrote to me on Thu, 10 Jul 2003 10:00:50 +0000 (UTC):
LW> К счастью, да.
LW> "The type of enumeration constants is EXPLICITLY
LW> defined as int":
LW> C99: 6.4.4.3/2
LW> Впрочем, есть в некоторых компиляторах специальные буратинские флаги,
LW> которые позволяют "ужимать" enum'ы.
LW> Но! Минимальная фракция - байты, а не биты.
Ну это-то как раз понятно.
размер любой переменной кратен байтам.
(по определению байта - его надеюсь не заменили пока)
Но сам факт записи в enum значения отличного от перечисленных
не является ли хотя-бы идеологической ошибкой?

И вот еще что такое char? раз пошли теоретизировать.
Может ли char!=byte?
Как случилось так что обращение к int может быть неатомарным?
Или размер int платформонезависим 32битный?

??>> s->hex1 - это ineteger type?
LW> Можно, я не буду это коментировать?.. Спасибо.
А то что в любом "integer type" уместится символ согласен?
Про то что в sig_atomic_t войдет int(ну возможно unsigned) это
и так понятно ну хотябы де факто.

Dmitry Miloserdov

unread,
Jul 11, 2003, 11:20:32 AM7/11/03
to
DM> Ну это-то как раз понятно.
DM> размер любой переменной кратен байтам.
DM> (по определению байта - его надеюсь не заменили пока)
DM> Но сам факт записи в enum значения отличного от перечисленных
DM> не является ли хотя-бы идеологической ошибкой?
Отправил а тут вот еще подумалось:
в char нельзя пихать 255 но никто на это ругаться не будет.
а вот гарантировано ли что -1==(char)255?

Valentin Nechayev

unread,
Jul 11, 2003, 2:30:25 PM7/11/03
to

>>> Dmitry Miloserdov wrote:

DM>> Но сам факт записи в enum значения отличного от перечисленных
DM>> не является ли хотя-бы идеологической ошибкой?

DM> Отправил а тут вот еще подумалось:
DM> в char нельзя пихать 255 но никто на это ругаться не будет.
DM> а вот гарантировано ли что -1==(char)255?

Нет.
Но можно проверить и послать нафиг, если платформа не такая,
на сейчас это вряд ли будет более 0.001% общего количества;))


-netch-

Valentin Nechayev

unread,
Jul 11, 2003, 2:30:25 PM7/11/03
to

>>> Dmitry Miloserdov wrote:

DM> И вот еще что такое char? раз пошли теоретизировать.
DM> Может ли char!=byte?

Не может.
См. архивы fido7.su.c-cpp, там доказывалось N+1 раз.

DM> Как случилось так что обращение к int может быть неатомарным?

А кто требует такое? Например, 16-разрядная система, но по ряду причин
сделан 32-битный int.

DM> Или размер int платформонезависим 32битный?

Нет.

DM> ??>> s->hex1 - это ineteger type?


LW>> Можно, я не буду это коментировать?.. Спасибо.

DM> А то что в любом "integer type" уместится символ согласен?
DM> Про то что в sig_atomic_t войдет int(ну возможно unsigned) это
DM> и так понятно ну хотябы де факто.

Вот это как раз... мнэ-э-э... я не уверен. Конечно, можно потестировать.


-netch-

Valentin Nechayev

unread,
Jul 12, 2003, 3:44:26 AM7/12/03
to

>>> Dmitry Miloserdov wrote:

DM> Мне кажется я понял причину нашего непонимания друг друга.
DM> Лично я привык:
DM> - читать что написано а не то что подразумевалось.
DM> - ститать что если автор говорит "А" то на протежении всей статьи
DM> он действительно считает что "А" действительно верно.
DM> - быть уверенным что если утверждение называют "следствием" то
DM> оно действительно является следствием вышеизложенного.
DM> (возможны ссылки на общеизвестные факты при условии хотябы
DM> упоминания этих фактов, приведение же самих формулировок
DM> расценивают как акт уважения к читателю).
DM> Ну это _моя_ позиция(может математическое образование сказывается).

1. Я тоже математик по образованию.
2. В любой математической области всегда присутствует энное количество
обычно не повторяемое в каждой статье постулатов, с учётом которой
и рассматривается её содержание.

DM> Можете быть не согласны но именно с этой позиции прошу рассматривать
DM> мои постинги.
DM> Ты же готов согласится с фразой, потому как ты понял, что автор хотел
DM> сказать, а не то, что он сказал. Для беседы вполне хорошо - быстрее к
DM> консенсусу придете. Но к написанию статьи рассчитанной не на
DM> конкретного человека ...

С этим я согласен, требуются уточнения во многих местах.

VN>>>> И неентерабельный кусок кода не обязательно
VN>>>> оформлен в виде отдельной функции.

DM> а. Участок кода один.
Неоправданное сужение рассмотрения.

DM> б. Он нереентерабельный.
DM> в. Раз критична реентерабельность значит хотят вызывать из разных мест.
DM> г. Он не оформлен в виде отдельной функции.
DM> д. По контексту - язык С.
DM> Вы считаете такой код приемлимым?

Без рассмотрения конкретного примера вывод сделать нельзя.

DM> или все-таки нужна _глобальная_ замена
DM> s/реентерабельный/async-signal-safe/g ?

Есть как минимум 4 разных вида реентерабельности.
1. Участок кода может быть выполнен ещё раз. (Ничего смешного. main(),
например, на это обычно не рассчитана.)
2. Участок кода может быть выполнен ещё раз рекурсивно с явным вызовом из этого
участка, непосредственно или через другие функции. (Реализации без стека,
как в классическом Фортране, на это не рассчитаны.)
3. Участок кода может быть выполнен ещё раз рекурсивно с асинхронным вызовом
(из обработчика сигнала).
4. Участок кода может одновременно выполняться в нескольких ветках.

В данном контексте у нас речь только про вид номер 3.

DM> btw: async-signal-safe сигналобезопасным что-ли обзывать?

Можно.

DM> Даже с учетом замены:
DM> сигналоНЕбезопасный участок кода это либо вызов
DM> сигналоНЕбезопасной функции либо НЕбезопасное обращение
DM> к статическим данным. Всего два случая - может не стоить запутывать
DM> читателя?

К кому вопрос? Есть SUSv3, которая не даёт никакой поблажки, несмотря
на то, что такую архитектуру, на которой могут быть проблемы со свободным
обращением к статическим данным в разрешённые моменты, ещё фиг найдёшь.

DM>>> И почему тогда вы тут не сказали что запрещено использовать локальные
DM>>> переменные
DM>>> в обработчиках, а потом аргументировать экономией стека?
VN>> Потому что количество локальных переменных управляемо. В отличие от
VN>> количества сигналов, поступивших из внешнего источника.

DM> Можно поспорить ну да ладно.
DM> Просто когда ты начинаешь заботится о стеке не говоря что проблема только
DM> в стеке то ты неадекватно описываешь проблему. Хоть бы приоритеты
DM> поставили как в RFC например MUST/SHOULD/MAY.

Можно и приоритеты.

VN>> Согласен, но это уже другой случай: это случай __синхронного__
VN>> обработчика сигнала. Ключевое слово подчёркнуто.
VN>> Более того, если верить гипотезе Яра, что обработчик сигнала может
VN>> иметь другую видимость памяти и другие странности, то и это не
VN>> проходит. А для асинхронного обработчика (то есть, когда заранее
VN>> неизвестно, в какой момент он вызовется, и известно, что имеет право
VN>> вызваться в любой момент в широком диапазоне) это неприменимо.

DM> void access_arbitrary_global_variable(int access_type,void *loc,void
DM> *var,size_t len)
DM> { sigprocmask(..); memcpy(access_type?loc:var,access_type?var:loc,len);
DM> sigprocmask(..); }
DM> Это _синхронная_ обработка сигналов?! Если да то вопросов больше нет.

Это как раз асинхронная. Но управляемая асинхронная.
Блин, надо с терминологией вокруг асинхронности/синхронности определиться.
А то скоро сам запутаюсь, не говоря про остальных;))

DM>>> То что выход непосредственно из обработчика это метод сохранить
DM>>> модульность. Ты в Т.З. на функцию будешь писать что кроме прочего
DM>>> функция не реже 2 раз в секунду должна проверять флаг? Так разработчик
DM>>> будет alrm перехватывать чтоб чтоб твою задачу выполнить;)
VN>> Ну так кто виноват в том, что количество НБФ так велико и покрывает
VN>> фактически всю библиотеку?
VN>> Набери себе реентерабельных функций и делай через них что угодно со
VN>> свободой обращения с сигналами. Я ж не против.;))

DM> Ну-ну. только как это решает проблему exit vs _exit?
DM> Если от модульности не хочется отказываться.

Проблему exit() в обработчике сигнала это, к сожалению, не решает.
Разве что если ты любую не свою функцию, подозрительную на НБФ, обернёшь
в запрет опасных для этого сигналов.

VN>>>> write() есть async-signal-safe функция.
DM>>> Вы мне это говорите?! Скажите Яру!
VN>> Ему зачем? Он и так знает. Откуда вывод, что это надо отдельно
VN>> говорить?

DM> Прочитай следствие 2 _до_ _конца_.

Я понял. Тяжело тут.

DM>>> Вы мне это говорите?! Прочтите сами и прочтите то что вы сказали по
DM>>> поводу 6,7
VN>> То всё про _асинхронный_ обработчик.

DM> Давайте определимся что такое _асинхронный_ обработчик.
DM> Ну чтоб не только вам понятно было.

Давайте.;)) Первичное определение: асинхронный обработчик - приходящий
в произвольные моменты времени, не управляемые программой.
Пойдёт? Альтернатива - синхронный обработчик - для которого открываются
короткие "окошки" (например, во время работы pselect() или sigsuspend()).

VN>> Ну и с поправкой на то, что Яр утверждает, что обработчик сигнала
VN>> имеет другую видимость памяти, а я говорю, что таких архитектур у меня
VN>> в поле зрения нет и долго не будет.

DM> Кстати, если не рассматривать таких гипотетических архитектур
DM> то для флагов можно использовать любую volatile переменную.

Не совсем так. При простых чтении или записи переменных - любую, операция
с которой атомарна для процессора. При более сложных операциях типа
инкремента или сравнения-с-обменом требуется платформенно-зависимая
поддержка.

DM>>> кстати про этот медод можно взглянуть на inetd во фре.
DM>>> volatile забыт, сигналы установлены до pipe(),NONBLOCK забыт.
DM>>> дедлоки не страшны.
VN>> Значит, он ещё не исправлен. Логично, да-а? ;)))

DM> Парадокс в том что если четко следовать правилам Яра то найти
DM> правильно написанную программу будет непростой труд.
DM> И не потому что все неправильные(пусть и много) а потому
DM> как рамки всеже жестковаты не для реальной жизни писаны.

Собственно, кроме "только писать и только в sig_atomic_t" и "не верьте
ни одной функции, даже, возможно, сисколлам" я никакой завышенной жёсткости
не вижу. Увы.


-netch-

Ivan Boldyrev

unread,
Jul 13, 2003, 2:28:16 AM7/13/03
to
"DM" == Dmitry Miloserdov writes:
DM>> Hу это-то как раз понятно.

DM>> размер любой переменной кратен байтам.
DM>> (по определению байта - его надеюсь не заменили пока)
DM>> Hо сам факт записи в enum значения отличного от перечисленных

DM>> не является ли хотя-бы идеологической ошибкой?
DM> Отправил а тут вот еще подумалось:
DM> в char нельзя пихать 255 но никто на это ругаться не будет.
DM> а вот гарантировано ли что -1==(char)255?

Если char по дефлту unsigned, то (char)255 == 255, и не равно -1.

--
Ivan Boldyrev

Perl -- это такой язык программирования, где 2 x 2 не равно 4.

Yar Tikhiy

unread,
Jul 14, 2003, 2:55:41 AM7/14/03
to
Valentin Nechayev <ne...@segfault.kiev.ua> wrote in
<2003071106...@iv.nn.kiev.ua>:

YT>> Я не уверен в том, что аппаратные traps доставляются в контексте,
YT>> позволяющем вести себя, как в синхронном обработчике: обращаться без
YT>> разбору к статическим данным, вызывать н.б.ф. и так далее...

VN> Я не знаю ни одной платформы, где бы было тут ограничение по контексту.
VN> Да, мой опыт ограничен линуксами, фрями и солярисами. Hо это... мнэ-э-э...
VN> процентов 95 или больше местного юниксового мира.

К тому же, на этих 95% из обработчика сигнала будут и longjmp(), и
обращение к любым статическим данным. Hо кто знает, что скрывается
в оставшихся 5%? :-)

VN> Кстати:


YT>> позволяющем вести себя, как в синхронном обработчике: обращаться без
YT>> разбору к статическим данным, вызывать н.б.ф. и так далее...

VN> вот этого я не понял. Синхронный обработчик - это тот, который вызывался
VN> сразу после разрешающего sigprocmask()? Так с точки зрения ядра у него
VN> и у асинхронного обработчика нет никакой разницы в построении контекста,
VN> стека и так далее. Или по крайней мере не описано такой разницы.

Под "синхронным" я имел ввиду обработчик, вызванный посредством
raise() или abort() -- в соответствии с тем, что гласит C99 (SUS
добавляет сюда также kill(), явно имея ввиду kill(getpid(), signo)).
Конечно, в упомянутых 95% разницы не будет.

В общем, я напишу, что обращаться к статическим данным можно
на свой страх и риск, т.к. это работает в большинстве Unix'ов.

--
Yar

It is loading more messages.
0 new messages