Ю. Вахалия - UNIX изнутри (2003) (1114670), страница 32
Текст из файла (страница 32)
3.9.1. Модели выполнения программ Ядро системы !3)ь!1Х использует модель выполнения программ, называемую моделью прог4ессогь Каждая нить имеет стек ядра, востребуемый нитью, когда она переходит в режим ядра для выполнения системного вызова или обработки исключения. Если нить блокируется в ядре, ее стек содержит состояние ее выполнения, в том числе последовательность вызова' и автоматические переменные. Основным достоинством описанного подхода является его простота, поскольку нити ядра могут блокироваться без необходимости явного сохранения каких-либо состояний.
Главный недостаток модели процессов — это большой расход памяти. Некоторые операционные системы, такие как г4п!с!с9!! нег !14! и ч' !6), используют иную программную модель под названием модели прерьгваний. Ядро обрабатывает системные вызовы и исключения как прерывания, организуя для всех операций ядра единый стек (один для каждого процессора). Следовательно, если нити необходимо заблокироваться, находясь в ядре, ей в первую очередь нужно где-то сохранить свое состояние. Ядро использует сохраненную информацию для восстановления состояния нити при следующем ее запуске, Основным достоинством модели прерываний является экономия памяти, достигаемая за счет использования единого стека ядра.
Главным недостат- ' То есть когда одна функция вызывает другую; она, в свою очередь, — третью, и так далее, и на каком-то шаге происходит блокирование. — Примеч. ред. 3.9. Продолжения в системе Масп 137 ком является необходимость сохранения состояния нити при проведении любой операции, потенциально могущей привести к ее блокированию. Это осложняет применение модели, так как сохраняемая информация может пересечь границы модуля. Следовательно, если нить блокируется, находясь в глубоко вложенной процедуре, ей необходимо определить (для сохранения) состояние, необходимое для всех вызовов в последовательности. Условия, при которых нить должна быть блокирована, определяются той моделью, которая является наиболее пригодной.
Наиболее подходящим вариантом является задание блокируемой нитью условий, при которых необходимо использовать ту или иную модель. Если нить блокируется где-то глубоко внутри последовательности вызовов, ей больше подойдет модель процессов. Однако если нити требуется сохранить при блокировании информацию о состоянии небольшого размера, то в этом случае модель прерываний окажется более предпочтительной. К примеру, многие серверные программы периодически блокируются в ядре, ожидая запроса клиента, и затем обрабатывают запросы по мере их получения. Такая программа может легко освобождать свой стек.
Механизм продолжений системы МасЬ комбинирует преимущества обеих моделей и позволяет ядру выбрать метод блокирования в зависимости от условий. Следующий раздел книги посвящен описанию устройства и реализации этого средства. 3.9.2. Использование продолжений Для блокирования нити система МасЬ использует функцию О)тези Ыос1О. В МасЬ версии 3.0 эта функция была изменена и теперь обладает аргументом; Шгеаз Ыос~ (чс1с (*ссэгтэ)<)); где соогтпΠ— это 4ункция продолжения, которая будет запущена при следующем выполнении нити.
Передача функции аргумента йО~~ указывает на необходимость традиционного поведения при блокировании. При таком подходе нить может выбирать, какое продолжение задействовать далее. Если нить собирается использовать продолжение, то, в первую очередь, она нуждается в сохранении того состояния, которое будет ей необходимо при возобновлении выполнения.
Структура нити содержит для этой цели 28-байтовую временную область. Если потребуется больший объем пространства, то нить выделит дополнительную структуру данных. Ядро блокирует нити и забирает ее стек. После возобновления функционирования нити ядро дает ей новый стек и вызывает функцию продолжения. Эта функция восстанавливает состояние из сохраненной области. Такой подход требует, чтобы продолжение и вызываемая функция имели точное представление о том, какое состояние сохранено и где.
Использование продолжений показано на следующем примере. Листинг 3.1 демонстрирует традиционный подход к блокированию нити. 13В Глава 3. Нити и легковесные процессы Листинг 3.1. Блокировка нити бвэ использовании продолжений вувса11 1 (агд1) Влгеаб 'о)осд(): 12(агд1); гегцгп; 12(агд1) ( гевцгп: Листинг 3.2 иллюстрирует применение продолжений. Листинг 3.2.
Блокировка нити с использованием продолжений вувса11 1 (агд1) сохранение агд1 и другой инфорнации о состоянии Влгеаб о1 оск(12): (* сюда выполнение не доходит *у ) 12( ) ( восстановление агд1 и другой инфориации о состоянии Влгеао эувса11 гевцгп (вва1цв); Следует упомянуть о том, что в случае вызова функции 1))геа(( Ыос((о с аргументом не произойдет возврата в вызвавший ее код. После восстановления функционирования нити ядро передает управление 12(). Функция 1(1теа(( вувсай тетщп() используется для возврата на прикладной уровень из системного вызова. Весь этот процесс является прозрачным для разработчика„так как он видит лишь синхронный выход из системного вызова.
Ядро использует продолжения при условии, что сохраненное перед блокированием состояние будет небольшим. Например, одна из наиболее частых блокирующих операций происходит при ошибке обработки страницы. В традиционных реализациях 1)1н1Х код обработчика вызывается вследствие запроса чтения с диска и блокирует работу до тех пор, пока чтение не закончится. Затем ядро системы возвращает нить на прикладной уровень, после чего приложение может дальше продолжать функционирование. Работа, которая должна быть выполнена после окончания операции чтения с диска, 3.9. Продолжения в системе Масн 139 требует небольшого сохраненного состояния (например, указателя на считанную страницу и данные отображения памяти, которые должны быть обновлены). Этот пример показывает ситуацию, когда применение продолжений оправданно.
3.9.3. Оптимизация работы Главным достоинством продолжений можно назвать сокращение количества стеков в ядре системы. Продолжения также позволяют провести ряд важных оптимизаций. Представьте, что при переключении контекста ядро обнаружило, что предыдущая и последующая нити используют продолжения. Предыдущая нить уже освободила свой стек ядра, а следующая нить еще не имеет такового. В этом случае ядро может передать стек от старой нити новой напрямую, как это продемонстрировано на рис. 3.12. Помимо исключения перегрузок, которые бы происходили при выделении нового стека, такой подход помогает сократить кэш-промахи' и буферы ассоциативной трансляции (сгапз1ас1оп 1оо1саяс1е Ъц6ег, ТЕВ, см.
подробнее в разделе 13.3.1), ассоциированные с переключением контекста, так как используется та же область памяти. Преимущества продолжений используются также в реализации! РС (межпроцессное взаимодействие) системы МасЪ. Передача сообщения включает две стадии. Клиентская нить использует для отправки сообщения и ожидания ответа системный вызов шасЪ шзя, а серверная нить использует тот же вызов для отправки ответа клиентам и ожидания новых запросов. Сообщение отправляется в порт, а также принимается из порта, являющегося защищенной очередью сообщений. Отправка и получение сообщений осуществляются независимо друг от друга.
Если получатель не готов, ядро поместит сообщение в очередь порта. Выполнение 1н1 ыпопненне Ф Рис. 3.12. Передача стека при использовании продолжений: а — перед блокировкой нити Н1 с функцией продолжения; б — после контекстного переключения ' То есть когда требуемые данные в каше отсутствуют. — Прим. ред. 140 Глава 3. Нити и легковесные процессы Если получатель находится в режиме ожидания, то процедура пересылки может быть оптимизирована при помощи продолжений.
Когда отправитель обнаружит, что получатель находится в режиме ожидания, он передаст свой стек получателю и заблокируется на функции продолжения гласЬ шзд сопвлае(). Получающая нить восстановит свою работу, используя при этом стек отправителя, который уже содержит всю необходимую информацию о передаваемом сообщении, Такой подход предотвращает перегрузку, возникающую при помещении в очередь и извлечении из нее сообщения, а также ощутимо увеличивает скорость обмена сообщениями. После ответа сервера происходит передача его стека клиентской нити и возобновление работы клиента описанным выше способом. 3.9.4. Анализ производительности Механизм продолжений системы МасЬ показал себя очень эффективным. Так как его применение не является обязательным, нет необходимости менять программную модель целиком, и его использование может быть наращиваемым.
Механизм продолжений очень сильно сокращает количество запросов, размещаемых в памяти ядра. Измерения производительности показали [10), что в среднем в системе, которой требуется 2002 байт стека в ядре на каждый процессор, пространство ядра для каждой нити сокращается с 4664 до 690 байт. Операционная система МасЬ 3.0 очень неплохо подходит для продолжений, так как обладает микроядром, имеющим небольшое количество базовых элементов и предлагающим скромный интерфейс. В частности, та часть кода, которая сохраняла совместимость системы с 1)л11Х, была удалена из ядра, и ее реализация состоялась в виде серверов прикладного уровня 112). В результате оказалось только 60 потенциальных мест, в которых ядро может блокировать выполнение, а 99«всех случаев блокирования происходит в шести «горячих точках».
При концентрировании внимания на них обеспечивается определенное преимущество, заключающееся в уменьшении усилий, затрачиваемых на разработку приложений. В противоположность МасЬ, традиционные системы 13л)1Х могут производить блокирование в сотнях мест, при этом не имея ни одной так называемой «горячей точки». 3.10. Заключение В этой главе вы увидели несколько вариантов устройства многонитевых систем. Существует большое количество типов средств, относящихся к нитям, а комбинирование их системой дает возможность создания сложной с элементами одновременности программной среды.