Я не сильно большой знаток *NIX систем и Linux, специализируюсь больше на разработке кросс-платформенных решений, а тут как раз представился удобный случай повнимательнее взглянуть на знаменитые сигналы UNIX.
Всерьез мои нижележащие мысли воспринимать не стоит, потому что, во-первых, не исключено, что какие-то моменты я понял не до конца, а во-вторых, потому что если вы пингвиноеб, то можете получить нешутейную порцию жопожжения.
Итак, что было обнаружено при курении man-pages.
#1. Сам подход с асинхронными вызовами в непонятно каком контексте может и был прогрессивным решением лет тридцать назад. В XXI же веке, когда без использования threads даже "хело ворлд" не напишешь, это выглядит откровеннымдолбоебиз архаизмом. Заводишь себе отдельный поток и ставишь в нем wait на интересующие тебя события... Правда, в Linux, по-моему, так и не добавили человеческий wait для множества разнородных объектов, но это уже детали.
#2. Изначально примитивный и очень простой механизм -- асинхронный вызов для аварийных ситуаций с передачей одного целого числа как кода аварии (см. signal() в сишной библиотеке) превратили в уродливое монстроподобное гавно, которое разве что за рисование окон не отвечает. А стандартную сишную функцию теперь, понятное дело, категорически не рекомендуют использовать.
Так как на механизм сигналов нацепили кучу свистелок и перделок, ребром встал вопрос о конфликтах их использования в рамках одного приложения, т.к., к примеру, о завершении асинхронной I/O операции (привет, I/O possible!) может быть интересно узнать далеко не в одном месте программы. Сигналы, вроде как, надо обрабатывать в дружеском кооперативном режиме, соединяя вызовы обработчиков в цепочки. Для этих целей в sigaction добавили возможность при выставлении своего обработчика узнать предыдущий. Решение чертовски неудачное, потому что у вас нет гарантированной возможности свой обработчик корректно снять с сигнала, не порушив всю цепочку.
Итак, что было обнаружено при курении man-pages.
#1. Сам подход с асинхронными вызовами в непонятно каком контексте может и был прогрессивным решением лет тридцать назад. В XXI же веке, когда без использования threads даже "хело ворлд" не напишешь, это выглядит откровенным
#2. Изначально примитивный и очень простой механизм -- асинхронный вызов для аварийных ситуаций с передачей одного целого числа как кода аварии (см. signal() в сишной библиотеке) превратили в уродливое монстроподобное гавно, которое разве что за рисование окон не отвечает. А стандартную сишную функцию теперь, понятное дело, категорически не рекомендуют использовать.
Так как на механизм сигналов нацепили кучу свистелок и перделок, ребром встал вопрос о конфликтах их использования в рамках одного приложения, т.к., к примеру, о завершении асинхронной I/O операции (привет, I/O possible!) может быть интересно узнать далеко не в одном месте программы. Сигналы, вроде как, надо обрабатывать в дружеском кооперативном режиме, соединяя вызовы обработчиков в цепочки. Для этих целей в sigaction добавили возможность при выставлении своего обработчика узнать предыдущий. Решение чертовски неудачное, потому что у вас нет гарантированной возможности свой обработчик корректно снять с сигнала, не порушив всю цепочку.
Одни идиоты придумали такое решение, которое не решает проблему, для которой оно было придумано.
А другие идиоты, таки да, не понимают, что sigaction придумали идиоты, и таки пытаются свой обработчик снять. В итоге получаем вылет программы и тот самый "I/O possible".
#3. Решение проблемы цепочки обработчиков для сигналов тривиально и до него додуматься может даже школьник, прогулявший половину уроков по турбо паскалю. Пара функций SetSignalHook(), ClearSignalHook() и гарантированно корректное управление цепочкой обработчиков под капотом (обычный связанный список и обеспечение с ним thread safe работы).
#4. Типичный *NIX подход -- насовывания кучи гавна в уже готовый API, вместо того, чтобы завести новый. Вместо того, чтобы четко различать такие вещи, как обработка сигнала завершения программы, реагирование на segfault, завершение асинхронной операции ввода-вывода и так далее, они пытаются объять необъятное, засунув никак не связанные вещи в одну единственную функцию.
И конечно же, нас ждет убер структура, для которой есть десяток вариантов трактовки, в зависимости от того, что именно вы обрабатываете.
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count; POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
long si_band; /* Band event (was int in
glibc 2.3.2 and earlier) */
int si_fd; /* File descriptor */
short si_addr_lsb; /* Least significant bit of address
(since kernel 2.6.32) */
}
И конечно же, ваш ждет куча другого легаси гавна, вроде двух различных видов функций для обработки сигналов, или лежащего рядом указателя sa_restorer, который как бэ obsolete и не должен использоваться... Кста, если вы загляните в signal.h, то увидите там # ifdef __KERNEL__ и #else секцию, которая начинается словами /* Here we must cater to libcs that poke about in kernel headers. */. Очень мило!
Отдельный большой вопрос, почему при запросе асинхронной I/O операции я не могу конкретно указать callback, куда я желаю получить нотификацию, для меня остается загадкой.
Кстати, другой хрестоматийный пример подобного "развития" API -- беркли сокеты, о которых я не могу говорить больше пяти секунд, не приходя в лютое бешенство от криворукости и скудоумия людей, их придумавших.
#5. Давайте о чем-то хорошем, а?...
Мне вот, к примеру, нравится, что при отсутствии обработчика завершения асинхронной I/O операции приложений падает. Я серьезно, без всякого сарказма.
Кстати, другой хрестоматийный пример подобного "развития" API -- беркли сокеты, о которых я не могу говорить больше пяти секунд, не приходя в лютое бешенство от криворукости и скудоумия людей, их придумавших.
#5. Давайте о чем-то хорошем, а?...
Мне вот, к примеру, нравится, что при отсутствии обработчика завершения асинхронной I/O операции приложений падает. Я серьезно, без всякого сарказма.
Нет, иногда конечно в unix-way практикуется, к примеру, падение в segfault при проблемах с парсингом своего конфигурационного файла (а чо? тебе в падлу взять gdb и посмотреть в чем проблема, бро?). Но в целом, стойкое игнорирование ошибок любой серьезности -- истинный путь джедая. Мы же серьезная ОС, на серверах работаем! Мы не может падать по любому пустяку, мы лучше молча продолжим работу, делая вид, что все в полном порядке!
Я понимаю, что такая фатальная реакция на I/O сигнал получилась случайно. Просто потому, что сигналы исторически несут в себе информацию о серьезных неполадках. Но все равно, как-то греет душу.
На этом все, любите Linux и друг друга.
Я понимаю, что такая фатальная реакция на I/O сигнал получилась случайно. Просто потому, что сигналы исторически несут в себе информацию о серьезных неполадках. Но все равно, как-то греет душу.
На этом все, любите Linux и друг друга.
No comments:
Post a Comment