Ю. Вахалия - UNIX изнутри (2003) (1114670), страница 26
Текст из файла (страница 26)
Такой вариант является более эффективным. Он также весьма удобен в тех случэях, если процесс-потомок после своего появления запустит в себе новую программу при помощи ехес. Однако подобное взаимодействие имеет и некоторые проблемы 120]. 1ЛЪ'Р часто используются для поддержки нитевых библиотек прикладного уровня. Такие библиотеки представляют каждую прикладную нить в виде структуры данных в пространстве процесса. Если вызов 1ог1 продублирует только вызываюший его 1.йгР, то новый процесс будет содержать прикладные нити, не З.З. Легковесные процессы: основные проблемы 113 относящиеся ни к одному своему 1.ЪЪгР.
Более того, процесс-потомок не должен пытаться снять блокировку, удерживаемую нитями, не существующими в нем, так как это может привести к состоянию клинча. В реальности иногда сложно избежать конфликта, так как библиотеки часто создают скрытые нити, о которых ничего не знает разработчик приложения, Представим иной случай, когда вызов Гоги дублирует все 1.ЪЪгР родительского процесса.
Такой вариант наиболее предпочтителен в случае, когда необходимо сделать именно копию всего процесса, нежели выполнить новую программу. Однако и в этом случае возникают определенные проблемы. Какой-либо ЕЪЪгР родителя может быть заблокирован системным вызовом, и это состояние не будет определено в потомке. Возможность обойти данную ситуацию заключается в том, чтобы заставить системный вызов вернуть код ошибки НМТй (системный вызов прерван), что позволило бы 1.ЪЪгР сделать его заново по мере необходимости.
1ЛЪгр также может иметь открытые сетевые соединения. Закрытие соединения в потомке может стать причиной отправки на удаленный узел незапланированных сообщений. Некоторые ЕЪЪгР умеют обрабатывать внешние общие структуры данных, которые могут быть повреждены при клонировании такого 1ЪЪгР вызовом 1огК Ни одно из решений не в силах правильно обработать все возможные ситуации. Во многих системах определенный компромисс достигнут при помощи двух различных вариантов Гоги, один из которых применяется для дублирования процесса полностью, а второй — копирует только одну нить. Для последнего варианта в таких системах определен набор безопасных функций, которые могут быть вызваны потомком перед выполнением екес.
Альтернативным вариантом является разрешение на регистрацию процессом одного или нескольких обработчиков Гоги, которые являются функциями, запускаемыми в родителе или потомке до или после вызова Гоги в зависимости от параметров, указанных при их регистрации. 3.3.2. Другие системные вызовы Для корректной работы в многонитевых системах необходимо пересматривать не только 1огк, но и многие другие системные вызовы.
Все ЕЪЪгР процесса разделяют между собой общий набор файловых дескрипторов. Это может стать причиной возникновения конфликта в случае, если один из ЕЪЪгР закроет файл, который в текущий момент времени считывается или в него ведется запись другим ЕЪЪгР. Файловый указатель на смещение' также используется совместно всеми нитями через дескриптор, поэтому применение функции авек одним из ЕЪЪгР повлияет на работу с этим файлом всех остальных нитей. Эта проблема проиллюстрирована на рис.
3,6. Легковесному процессу Л1 ' Используется при считывании из файла или записи в файл для запоминания той позиции, отку- да необходимо начинать чтение или запись при сведующем вызове геао или нпсе. — Прим, ред, 114 Глава 3. Нити и легковесные процессы необходимо прочесть данные из файла, начиная со смещения отт1, для чего он вызывает функцию реек, а затем — геад. Между двумя означенными вызовами другой процесс Л2 применяет Веем в отношении того же файла, указывая при этом другое смещение. Такая ситуация приведет к тому, что Л1 считает не те данные.
Приложение должно решать подобные проблемы самостоятельно, используя какой-либо протокол блокирования файлов, Альтернативным решением являются механизмы ядра системы, которые позволяют производить произвольный ввод-вывод атомарно (см, подробнее в разделе З.б.б). Рис. 3.6. Проблемы, возникающие при одновременном доступе к файлу Каждый процесс имеет один текущий рабочий каталог и использует одну структуру пользовательских полномочий. Так как полномочия могут изменяться в любой момент времени, ядро должно использовать их атомарно и только однажды перед системным вызовом. Все 1ЮР процесса используют совместно одно и то же адресное пространство и могут манипулировать им одновременно при помощи различных системных вызовов, таких как пипар или ой.
Такие вызовы должны быть безопасными (в отношении нитей) во избежание повреждения адресного пространства процесса. Программистам следует внимательно относиться к последовательности таких операций, так как в противном случае результат может быть непредсказуем. 3.3.3. Доставка и обработка сигналов В системах 13Х1Х доставка и обработка сигналов производится на уровне процесса. В многонитевых системах необходимо определять, какой из 1ЛЪ'Р процесса будет заниматься обработкой сигналов. При использовании прикладных нитей имеется аналогичная проблема: после того, как ядро передаст сигнал в Етту'Р, нитевая библиотека должна определить, в какую нить его направить.
Существует несколько вариантов решения данной проблемы; + пересылка сигналов каждой нити; + объявление одной из нитей процесса «главнойтп после чего все сигна- лы передаются только этой нити; 3.3. Легковесные процессы: основные проблемы 115 + отправка сигналов в любую произвольно выбранную нить; + использование эвристических методов для определения, какой из нитей необходимо отправить данный сигнал; + создание новой нити для обработки каждого сигнала. Первый метод является очень затратным, и, более того, он несовместим с большинством обычных ситуаций, в которых используются сигналы. Однако в некоторых случаях он весьма удобнен.
Например, если пользователь нажимает комбинацию Сгг1+2 на терминале, он может желать приостановки всех нитей процесса. Второй метод приводит к асимметричной обработке нитей, что несовместимо с современным подходом к нитям и с симметричными многопроцессными системами, часто ассоциируемыми с многонитевыми ядрами. Последнее решение, приведенное в списке, подходит только для определенных ситуаций. Выбор между двумя оставшимися методами зависит от природы выработанного сигнала. Некоторые сигналы, например 5165Е6Н (ошибка сегментации) и 5161Ы (непредусмотренное исключение), создаются вследствие действий нити. Наиболее удобным решением в данном случае представляется доставка такого сигнала той нити, которая и стала причиной его возникновения.
Другие сигналы, такие как 5165ТР (сигнал остановки, вырабатываемый терминалом) или 5161ИТ (сигнал прерывания), создаются при возникновении внешних событий и не могут быть как-то ассоциированы с конкретной нитью процесса. Еще одним аспектом, о котором стоит упомянуть, является применяемый метод обработки и маскирования сигналов.
Должны ли все нити использовать общий набор обработчиков сигналов или каждая будет определять свой собственный? Хотя последний вариант является более гибким и универсальным, он привносит каждой нити дополнительные затраты, что противоречит главной цели применения многонитевых процессов. Такие же проблемы возникают при маскировании сигналов, поскольку обычно маскирование происходит с целью защиты важных участков кода.
Следовательно, лучшим вариантом представляется разрешение каждой нити на указание собственной маски сигналов. Перегрузки, возникающие при применении таких масок, менее значительны и поэтому более приемлемы. 3.3.4. Видимость Важно определить, в какой степени Т.Ъ'Р будет видимым вне процесса. Бесспорно„ядро системы знает о существующих 1Т1гР и планирует их выполнение независимо.
Однако большинство реализаций систем не позволяет процессам обладать информацией о конкретных 1ЛЧР других процессов, а также взаимодействовать с ними, Вместе с тем внутри процесса необходимо предоставлять какому-либо 1.ЮР возможность получать информацию о существовании остальных 1.%'Р в рамках своего процесса. Многие системы предлагают для этой цели специаль- 116 Глава 3. Нити и легковесные процессы ные системные вызовы, которые позволяют одному 1ЛзгР отправлять сигна- лы другому 1.ЖР, принадлежащему тому же самому процессу. 3.3.5. Рост стека Если какой-нибудь из процессов в системе Пт!!Х переполняет свой стек, в результате этого возникает ошибка нарушения сегментации.
Ядро распознает появление таких ситуаций в сегменте стека и автоматически увеличивает размер стека', не посылая никаких сигналов процессу. Многонитевые процессы обладают несколькими стеками, по одному на каждую прикладную нить. Эти нити размещаются на прикладном уровне при помощи нитевых библиотек. Если ядро системы попытается расширить стек, то возникнет определенная проблема, так как такая операция может привести к конфликту с обработчиком стека в нитевой библиотеке прикладного уровня. Даже в многонитевой системе ядро не имеет никакого представления о стеках прикладных нитей'.
Такие стеки не всегда являются специальными областями и могут быть взяты прямо из кучи. Обычно если нить указывает размер необходимого ей стека, то библиотека может загцитить стек от переполнения при помощи размещения страницы памяти, защищенной от записи, сразу после конца стека. Такой подход приводит к ошибке защиты при возникновении переполнения стека', и в этом случае ядро системы посылает сигнал Е1СБЕгсЧ соответствующей нити. После этого нить может либо увеличить размер стека, либо решить проблему иным путем'.