mirror of
https://github.com/Nekrolm/ubbook.git
synced 2026-06-09 13:14:18 +03:00
signal unsafety
This commit is contained in:
@@ -74,6 +74,7 @@
|
||||
2. [shared_ptr](concurrency/shared_ptr.md)
|
||||
3. [thread::join](concurrency/jthread.md)
|
||||
4. [Повторный захват мьютекса](concurrency/double_lock.md)
|
||||
5. [Signal-unsafe](concurrency/signal_unsafe.md)
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# Сигнало(не)безопасность
|
||||
|
||||
Разработчик любого сколько-нибудь серьезного приложения рано или поздно вынужден озаботиться
|
||||
вопросами поведения программы в различных краевых и внештатных ситуациях: запрос досрочного завершения, внезапное закрытие терминала, обработка маловероятных ошибочных состояний. Во многих этих случаях приходится иметь дело с довольно примитивным механизмом межпроцессного взаимодействия — с обработкой сигналов.
|
||||
|
||||
Программист регистрирует обработчики нужных ему сигналов и забот не знает, очень часть допуская серьезную ошибку —
|
||||
выполняет в обработчике сигналов код, который там выполнять небезопасно: выделяет память, делает I/O, захватывает блокировки...
|
||||
|
||||
Сигналы прерывают нормальный ход исполнения программы и могут быть обработаны в произвольном потоке.
|
||||
Поток мог начать выделять память, захватить блокировку в аллокаторе и в этот момент быть прерванным сигналом. Если обработчик сигнала в свою очередь запросит выделение памяти... Будет повторный захват блокировки в одном и том же потоке. Неопределенное поведение.
|
||||
|
||||
Очень легко можно продемонстрировать проблему на следующем примере
|
||||
```C++
|
||||
std::mutex global_lock;
|
||||
|
||||
int main() {
|
||||
std::signal(SIGINT, [](int){
|
||||
std::scoped_lock lock {global_lock};
|
||||
printf("SIGINT!\n");
|
||||
});
|
||||
|
||||
{
|
||||
std::scoped_lock lock {global_lock};
|
||||
printf("start long job\n");
|
||||
sleep(10);
|
||||
printf("end long job\n");
|
||||
}
|
||||
sleep(10);
|
||||
}
|
||||
```
|
||||
|
||||
Если мы скомпилируем эту программу под Linux (не забыв указать `-pthread`), запустим и нажмем `Ctrl+C`, то она зависнет навсегда из-за повторного захвата мьютекса одним и тем же потоком. Если же забудем `-pthread`, то не зависнет и отработает «ожидаемым» образом.
|
||||
|
||||
Под Windows эта программа также работает «ожидаемо» из-за специфики обработки сигналов — там для обработки SIGINT/SIGTERM всегда неявно порождается новый поток.
|
||||
|
||||
В любом случае этот код некорректен из-за использования сигналонебезопасной функции внутри обработчика сигналов.
|
||||
|
||||
### Как бороться?
|
||||
|
||||
1. Делать обработчики сигналов как можно более простыми
|
||||
2. Сверяться с документацией, безопасно ли использование той или иной функции в контексте обработчика сигналов
|
||||
3. Отключать автоматический прием сигналов и выполнять их обработку в рамках обычного исполнения программы (см., например, `sigprocmask` и `sigwait`)
|
||||
|
||||
### Полезные ссылки
|
||||
1. https://man7.org/linux/man-pages/man7/signal-safety.7.html
|
||||
2. https://www.gnu.org/software/libc/manual/html_node/Blocking-Signals.html
|
||||
Reference in New Issue
Block a user