Главная страница Взаимодействие нетривиальных процессов мы понимаем, что один экземпляр демона уже запущен, поэтому выводится сообщение об ощибке и программа завершает работу. ПРИМЕЧАНИЕ Во многих версиях Unix демоны записывают свои идентификаторы в файл. Solaris 2.6 хранит подобные файлы в каталоге /etc, а Digital Unix 4.0В и BSD/OS - в каталоге / var/run. Запись идентификатора процесса в файл 18-21 Мы укорачиваем файл до О байт, а затем записываем в него строку с нашим идентификатором. Причина, по которой нужно укорачивать файл, заключается в том, что у предыдущего экземпляра демона идентификатор мог быть представлен более длинным числом, чем у данного, поэтому в результате в файле может образоваться смесь двух идентификаторов. Вот результат работы программы из листинга 9.8: Solaris % onedaemon & запускаем первый экземпляр [1] 22388 Solaris % cat pidfile проверяем идентификатор 22388 Solaris % onedaemon пытаемся запустить второй экземпляр unable to lock pidfile. is onedaenran already running? Существуют и другие способы предотвращения запуска нескольких экземпляров демонов, например семафоры. Преимущество данного метода в том, что многим демонам и так приходится записывать в файл свои идентификаторы, а при досрочном завершении работы демона блокировка с файла снимается автоматически. 9.8. Блокирование файлов Стандарт Posix. 1 гарантирует, что если функция open вызывается с флагами D CREAT (создать файл, если он еще не существует) и D EXCL (ис1слючающее открытие), функция возвращает ошибку, если файл уже существует. Более того, проверка существования файла и его создание (если он еще не существует) должны представлять собой атомарную по отношению к другим процессам операцию. Следовательно, мы можем использовать создаваемый таким методом файл как блокировку. Можно быть уверенным, что только один процесс сможет создать файл (то есть получить блокировку), а для снятия этой блокировки файл можно удалить командой unlink. В листинге 9.9 приведен текст наших функций установки и снятия блокировки, использующих этот метод. При успешном выполнении функции open мы считаем, что блокировка установлена, и успешно возвращаемся из функции ту 1оск. . Файл мы закрываем, потому что его дескриптор нам не нужен. О наличии блокировки свидетельствует само существование файла вне зависимости от того, открыт он или нет. Если функция open возвращает ошибку EEXIST, значит, файл существует и мы должны еще раз попытаться открыть его. У этого метода есть три недостатка. 1. Если процесс, установивший блокировку, завершится досрочно, не сняв ее, файл не будет удален. Существуют способы борьбы с этой проблемой, например проверка времени доступа к файлу и удаление его спустя некоторый определенный промежуток времени, - но все они несовершенны. Другое решение заключается в записи в файл идентификатора процесса, чтобы другие процессы могли считать его и проверить, существует ли еще такой процесс. Этот метод также несовершенен, поскольку идентификатор может быть использован повторно. В такой ситуации лзше пользоваться блокировкой fcntl, которая автоматически снимается по завершении процесса. 2. Если файл открыт каким-либо другим процессом, мы должны еще раз вызвать open, повторяя эти вызовы в бесконечном цикле. Это называется опросом и является напрасной тратой времени процессора. Альтернативным методом является вызов sleep на 1 секунду, а затем повторный вызов open (эта проблема обсуждалась в связи с листингом 7.4). Эта проблема также исчезает при использовании блокировки fcntl, если использовать команду F SETLKW. Ядро автоматически приостанавливает выполнение процесса до тех пор, пока ресурс не станет доступен. 3. Создание и удаление файла вызовом open и unl i nk приводит к обращению к файловой системе, что обычно занимает существенно больше времени, чем вызов fcntl (обращение производится дважды: один раз для получения блокировки, а второй - для снятия). При использовании fcntl программа выполнила 10 ООО повторов цикла с увеличением порядкового номера в 75 раз быстрее, чем программа, вызывавшая open и unl ink. Листинг 9.9. Функции блокировки с использованием open с флагами 0 CREAT и 0 EXCL lock/lockopen.c 1 linclude unpipc.h 2 Idefine LOCKFILE /tmp/seqno.lock 3 void 4 my lock(int fd) 5 { 6 int tempfd: 7 while ( (tempfd - open(LOCKFILE. 0 RDWR0 CREAT0 EXCL. FILE MODE)) < 0) { 8 if (errno 1= EEXIST) 9 err sys( open error for lock file ): 10 /* блокировка установлена кем-то другим, повторяем попытку */ 11 } 12 Close(tempfd): /* открыли файл, блокировка установлена */ 13 } 14 void 15 my unlock(int fd) 16 { 17 Unlink(LOCKFILE): /* снимаем блокировку удалением файла */ 18 ) Есть еще две особенности файловой системы Unix, которые использовались для реализации блокировок. Первая заключается в том, что функция link возвращает ощибку, если имя новой ссылки уже существует. Для получения блокировки создается уникальный временный файл, полное имя которого содержит в себе его идентификатор процесса (или комбинацию идентификаторов процесса и потока, если требуется осуществлять блокировку между отдельными потоками). Затем вызывается функция link для создания ссылки на этот файл с каким-либо определенным заранее именем. После успещного создания сам файл может быть удален вызовом unl ink. После осуществления работы с блокировкой файл с известным именем удаляется командой unl i nk. Если link возвращает ошибку EEXIST, поток должен попытаться создать ссылку еще раз (аналогично листингу 9.9). Одно из требований к этому методу - необходимо, чтобы и временный файл, и ссылка находились в одной файловой системе, поскольку большинство версий Unix не допускают создания жестких ссылок (результат вызова 1 i пк) в разных файловых системах. Вторая особенность заключается в том, что функция open возвращает ошибку в слзае существования файла, если указан флаг D TRUNC и запрещен доступ на запись. Для получения блокировки мы вызываем open, указывая флаги D CREAT D WRDNLY I D TRUNC и аргумент mode со значением О (то есть разрешения на доступ к файлу установлены в 0). Если вызов оказывается успешным, блокировка установлена и мы просто удаляем файл вызовом unl i пк после завершения работы. Если вызов open возвращает ошибку EACESS, поток должен сделать еще одну попытку (аналогично листингу 9.9). Этот трюк не срабатывает, если поток обладает правами привилегированного пользователя. Урок, который можно извлечь из этих примеров, прост: нужно пользоваться блокировкой fcntl. Тем не менее вы можете столкнуться с программой, в которой используются старые методы блокировки, и особенно часто это будет встречаться в программах, написанных до широкого распространения реализации с блокировкой fcntl. 9.9. Блокирование в NFS Аббревиатура NFS расшифровывается как Network File System (сетевая файловая система); эта система подробно обсуждается в главе 29 [22]. Блокировка записей fcntl представляет собой расширение NFS, поддерживаемое большинством ее реализаций. Обслуживается эта блокировка двумя дополнительными демонами: lockd и statd. При вызове fcntl для получения блокировки ядро обнаруживает, что файл находится в файловой системе NFS. Тогда локальный демон lockd посылает демону 1 ockd сервера запрос на получение блокировки. Демон statd хранит информацию о клиентах, установивших блокировку, и взаимодействует с 1 ockd для обеспечения снятия блокировок в случае завершения процессов. Установка блокировки записи в NFS должна занимать в среднем больше времени, чем для локального файла, поскольку для установки и снятия блокировки требуется передача информации по сети. Для проверки работы блокировки NFS нужно всего лишь изменить имя файла, определяемое константой SEQFILE в листинге 9.2. Если измерить время, требуемое для выполнения 10 ООО операций по
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |