Если в потоковой библиотеке NPTL размер стека (RLIMIT_STACK) не является неограниченным, он используется по умолчанию при создании новых потоков. Это значение устанавливается до запуска программы, обычно с помощью встроенной команды ulimit — s (которая ограничивает размер стека в командной оболочке С). Для задания ограничения недостаточно вызвать функцию setrlimit() внутри главной программы, поскольку библиотека NPTL определяет размер стека во время инициализации, которая происходит до запуска функции main().

33.2. Потоки и сигналы

Модель сигналов в 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. С помощью индивидуальных масок сигнала приложение может контролировать то, какие потоки должны получить сигнал, направленный всему процессу.

Перейти на страницу:

Похожие книги