Как стать автором
Обновить

Комментарии 6

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

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

Вообще-то, в этом сценарии прикладной код не получает управление.
Как и для организации подкачки страниц из pagefile.

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

Прикладной код может делать похожие вещи выставив VEH обработчик (можно и SEH, но VEH вызывается раньше и его за счёт этого сложнее испортить кодом 3rd-party где-то на стеке.)
Я так делал на коленке свою реализацию memory mapped files для сжатых файлов

Мне тоже не нравится категоричный тон автора статьи

Это вполне сознательная провокация.
А как работают с исключениями, на самом деле? Вот список всех кодов исключений. Мне просто интересно, какие из них можно «обработать» и после этого продолжить работу приложения, с конкретными примерами из жизни.
Это не исчерпывающий список. Обычный throw из C++ тоже использует SEH с кастомными кодами.
Я совершенно уверен в том, что сама изначальная идея SEH ошибочна… Совершенно нереально исправить такие ошибки во время исполнения программы

Категорично сказано, но, пожалуй, я соглашусь. В большинстве «нормально написанных» программ такие ошибки исправить невозможно.

Более того, они вообще не должны возникать в нормальных программах. А если всё-таки возникли — то это признак семантической ошибки (бага) в коде, или дефекта железа. Чтобы исправить последствия подобного (или хотя бы обеспечить «плавную деградацию») — потребуется настолько тщательная проработка программы, что трудозатраты на её создание вырастут во много раз.

Не побоюсь сказать, что для прикладных программ такая «защитная» проработка никогда не оправдана. Что говорить — даже ядро операционной системы не имеет устойчивости к неожиданно возникшим исключениям. Обработка неожиданных исключений в ядре Windows сводится к вызову KeBugCheckEx или, в простонародии, Blue Screen Of Death.

В этой связи любопытно проследить параллель. В ядре Windows имеется такой механизм, как APC — Asynchronous Procedure Call. С помощью APC можно устроить любому потоку (Thread) прерывание: поток приостановит исполнение текущей программы, выполнит функцию-обработчик APC, а потом вернётся. Казалось бы, вещь полезная. Однако разработчики Windows приняли следующие решения:
1) Механизм APC доступен только из режима ядра. В режиме пользователя невозможно запросить другому потоку исполнение APC;
2) Механизм APC недокументирован даже для разработчиков драйверов. Документацию можно найти в сети, но она неофициальная.
3) Для пользовательских приложений оставлена урезанная, неполноценная версия (QueueUserAPC), которая не может прервать поток в произвольном месте, а только в чётко обозначенных им точках (при вызове им функций SleepEx, WaitForSingleObjectEx и т.д.)

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

Кое-какие «следы» APC доступны в режиме пользователя. Это функции SuspendThread и TerminateThread. Обе они прерывают поток, а обработчики, соответственно, ждут или завершают его.

Ну и много ли счастья от этих функций? Особенно от второй. Документация говорит прямо: «вы должны точно знать, что делает поток, когда даёте ему TerminateThread». И действительно. Если не знать, что делает поток в момент «прибития» — то он может оставить состояние программы и системных библиотек повреждённым. Продолжение работы программы после этого — хождение вслепую по граблям.

Так вот, между SEH и APC много общего. В обоих случаях в неожиданном месте программы вызывается функция-обработчик. Да, иногда можно в обработчике сделать что-то полезное. Но, когда неизвестно, где была прервана программа и что именно она делала в этот момент — то обычно нет.

Примеры программ, устойчивых к критическим ошибкам, встречаются в области т.н. «защитного программирования» (defensive programming). Это распространено во встраиваемых системах, где микроконтроллер управляет критичным процессом. Промышленность, авиация, космос, военные применения. И тогда, в случае сбоя, необходимо принять все возможные и невозможные меры к скорейшему восстановлению.

Но всё равно, нашпиговать программу защитами от сбоев — это очень, очень трудоёмко.

Хорошо, конечно, что Windows предоставляет такую возможность посредством SEH. Но, с другой стороны, много ли толку от защитного программирования одного приложения, если ядро ОС и остальные приложения не имеют такой оснастки? С APC ведь тоже можно, если сильно постараться, сделать что-то интересное. Однако же не решили давать приложениям такую возможность. SEH — граната того же калибра, что и APC.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации