Предположим, архитектура x86. И ОС основана на Linux. Учитывая многопоточный процесс, в котором один поток выполняет int 3
инструкция, останавливает ли обработчик прерывания выполнение всего процесса или только поток, который выполнил int 3
инструкция?
Поскольку вопрос касается Linux, давайте углубимся в исходники ядра! Мы знаем int 3
будет генерировать SIGTRAP, как мы можем видеть в do_int3
. поведение по умолчанию SIGTRAP это завершить процесс и сбросить ядро.
do_int3
звонки do_trap
который после многих косвенных обращений complete_signal
, где происходит большая часть магии. После комментариев становится совершенно ясно, что происходит без особого объяснения:
РЕДАКТИРОВАТЬ: Чтобы ответить на комментарий:
Когда процесс идет ptrace
г, поведение довольно хорошо задокументировано в страница справочника (см. «Сигнал-доставка-остановка»). По сути, после того, как ядро выберет произвольный поток, который обрабатывает сигнал, если выбранный поток отслеживается, он входит в сигнал-доставка-остановка — это означает, что сигнал еще не доставлен процессу и может быть подавлен процессом трассировщика , Так обстоит дело с отладчиком: мертвый процесс бесполезен для нас при отладке (это не совсем так, но давайте рассмотрим сценарий оперативной отладки, который является единственным, который имеет смысл в этом контексте), поэтому по умолчанию мы блокируем SIGTRAP, если пользователь не укажет иное. В этом случае не имеет значения, как отслеживаемый процесс обрабатывает SIGTRAP (SIG_IGN или SIG_DFL или пользовательский обработчик), потому что он никогда не узнает, что это произошло.
Обратите внимание, что в случае SIGTRAP процесс трассировки должен учитывать различные сценарии, кроме процесса, который останавливается, как также подробно описано на странице руководства под каждым действием ptrace.
Достаточно легко проверить:
#include <thread>
#include <vector>
void f(int v) {
std::this_thread::sleep_for(std::chrono::seconds(2));
if (v == 2) asm("int $3");
std::this_thread::sleep_for(std::chrono::seconds(1));
printf("%d\n", v); // no sync here to keep it simple
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 4; i++) threads.emplace_back(f, i);
for (auto& thread : threads) thread.join();
return 0;
}
Если был остановлен только поток, он все равно должен печатать сообщение из потоков, отличных от 2, но это не так, и весь процесс останавливается перед печатью чего-либо (или вызывает точку останова при отладке). На Ubuntu вы получите следующее сообщение:
Ловушка трассировки / точки останова (ядро сброшено)
int 3 является привилегированной инструкцией, которую пользовательский код не может запускать.
Затем ядро отправит сигнал SIGTRAP вашему процессу, и действие по умолчанию для сигнала SIGTRAP — завершить весь процесс.
Ответ на самом деле ни один. Int 3 используется для запуска точки останова. Обработчик прерываний крошечный, и ни прерывание, ни его обработчик не останавливают никакие потоки.
Если отладчик не загружен, обработчик либо проигнорирует его, либо вызовет ОС, чтобы предпринять какое-то действие по ошибке, например, подать сигнал (возможно, SIGTRAP). Нет темы не пострадали.
Если есть внутрипроцессный отладчик, ISR точки останова передает ему управление. Точка останова не останавливает никакие потоки, кроме той, которая разрывается. Отладчик может попытаться приостановить работу других.
Если есть отладчик вне процесса, обработчик вызовет его, но он должен быть передан через ОС, чтобы сделать подходящее переключение контекста. Как часть этого переключателя ОС приостанавливает отладчик, что означает, что все его потоки будут остановлены.