Если в потоковой библиотеке NPTL размер стека (RLIMIT_STACK) не является неограниченным, он используется по умолчанию при создании новых потоков. Это значение устанавливается до запуска программы, обычно с помощью встроенной команды ulimit — s (которая ограничивает размер стека в командной оболочке С). Для задания ограничения недостаточно вызвать функцию setrlimit() внутри главной программы, поскольку библиотека NPTL определяет размер стека во время инициализации, которая происходит до запуска функции main().
Модель сигналов в UNIX была спроектирована с учетом особенностей процессов в этой системе и опередила на два десятилетия появление программного интерфейса Pthreads. В итоге между сигналами и потоками возникают заметные конфликты. В основе этих конфликтов, как правило, лежит необходимость сохранения традиционной для однопоточных процессов семантики (то есть интерфейс Pthreads не должен менять семантику сигналов старых программ) в сочетании с разработкой такой модели сигналов, которая была бы уместной в условиях многопоточности.
Эти расхождения между моделями сигналов и потоков приводят к тому, что их совместное использование сопряжено с трудностями и по возможности его следует избегать. Но иногда нам все же приходится работать с сигналами в многопоточных программах. В этом разделе мы обсудим взаимодействие между потоками и сигналами и опишем различные функции, которые могут при этом пригодиться.
33.2.1. Как модель сигналов в UNIX соотносится с потоками
Чтобы понять, как UNIX-сигналы соотносятся с моделью Pthreads, необходимо понимать, какие из аспектов сигнальной модели распространяются на весь процесс (то есть являются общими для всех потоков в процессе), а какие относятся к отдельным потокам. В следующем списке собраны ключевые моменты.
• Назначение сигналов распространяется на весь процесс. Если какой-либо необрабатываемый сигнал с действием
• Действия сигналов распространяются на весь процесс; все потоки в процессе разделяют одно и то же действие для каждого сигнала. Если один поток использует вызов sigaction(), чтобы установить обработчик для какого-нибудь сигнала (например, SIGINT), этот обработчик можно будет вызвать из любого потока, которому доставлен данный сигнал. Аналогично, если один поток решит сделать действие сигнала
• Сигнал может быть направлен как процессу в целом, так и отдельному потоку. Сигнал направлен в поток, если:
• он сгенерирован в качестве непосредственного результата выполнения определенной аппаратной инструкции в рамках контекста потока (то есть аппаратных исключений SIGBUS, SIGFPE, SIGILL и SIGSEGV; подробности — в разделе 22.4);
• это сигнал SIGPIPE, сгенерированный в момент, когда поток пытался выполнить запись в поврежденный конвейер;
• он был отправлен с помощью функций pthread_kill() или pthread_sigqueue() (описаны в подразделе 33.2.3), которые позволяют потокам одного процесса обмениваться между собой сигналами.
• Все сигналы, сгенерированные другими механизмами, направлены на весь процесс. Например, это могут быть сигналы, отправленные другим процессом с помощью вызовов kill() или sigqueue(); или такие сигналы, как SIGINT и SIGTSTP, генерируемые в момент, когда пользователь нажимает соответствующее сочетание клавиш; или сигналы, сгенерированные для таких программных событий, как изменение размера окна терминала (SIGWINCH) или истечение срока действия таймера (например, SIGALRM).
• Когда сигнал доставляется многопоточному процессу, у которого есть подходящий обработчик, ядро наугад выбирает один поток, чтобы доставить ему этот сигнал и дать возможность его обработать. Такой подход соответствует традиционной семантике сигналов. Обработка одного и того же сигнала несколько раз не имела бы никакого смысла.
• Маска сигнала относится к каждому отдельному потоку (понятия глобальной маски, распространяемой на все потоки в многопоточном процессе, не существует). Потоки могут независимо друг от друга блокировать и разблокировать разные сигналы, используя новую функцию pthread_sigmask(), которая входит в состав программного интерфейса Pthreads. С помощью индивидуальных масок сигнала приложение может контролировать то, какие потоки должны получить сигнал, направленный всему процессу.