th - ожидаемый объем данных, count - количество байт, фактически переданных пользовательской программе. Флаги (flags) могут быть установлены таким образом, что поступившее сообщение пос- ле чтения и анализа его содержимого не будет удалено из очереди, или настро- ены на получение данных out-of-band. В дейтаграммных версиях указанных функ- ций, sendto и recvfrom, в качестве дополнительных параметров указываются ад- реса. После выполнения подключения к гнездам потокового типа процессы могут вместо функций send и recv использовать функции read и write. Таким образом, согласовав тип протокола, серверы могли бы порождать процессы, работающие только с функциями read и write, словно имеют дело с обычными файлами. Функция shutdown закрывает гнездовую связь: shutdown(sd,mode) где mode указывает, какой из сторон (посылающей, принимающей или обеим вмес- те) отныне запрещено участие в процессе передачи данных. Функция сообщает используемому протоколу о завершении сеанса сетевого взаимодействия, остав- ляя, тем не менее, дескрипторы гнезд в неприкосновенности. Освобождается дескриптор гнезда только в результате выполнения функции close. Системная функция getsockname получает имя гнездовой связи, установлен- ной ранее с помощью функции bind: getsockname(sd,name,length); Функции getsockopt и setsockopt получают и устанавливают значения раз- личных связанных с гнездом параметров в соответствии с типом домена и прото- кола. Рассмотрим обслуживающую программу, представленную на Рисунке 11.20. Процесс создает в домене "UNIX system" гнездо потокового типа и присваивает ему имя sockname. Затем с помощью функции listen устанавливается длина оче- реди поступающих сообщений и начинается цикл ожидания поступления запросов. Функция accept приостанавливает свое выполнение до тех пор, пока протоколом не будет зарегистрирован запрос на подключение к гнезду с означенным именем; после этого функция завершается, возвращая поступившему запросу новый деск- риптор гнезда. Процесс-сервер порождает потомка, через которого будет под- держиваться связь с процессом-клиентом; родитель и потомок при этом закрыва- ют свои дескрипторы, чтобы они не становились помехой для коммуникационного траффика другого процесса. Процесс-потомок ведет разговор с клиентом и за- вершается после выхода из функции read. Процесс-сервер возвраща- ется к началу цикла и ждет поступления следующего запроса на подключение. На Рисунке 11.21 показан пример процесса-клиента, ведущего общение с сервером. Клиент создает гнездо в том же домене, что и сервер, и посылает запрос на подключение к гнезду с именем sockname. В результате подключения 358 +------------------------------------------------------------+ | #include | | #include | | | | main() | | { | | int sd,ns; | | char buf[256]; | | struct sockaddr sockaddr; | | int fromlen; | | | | sd = socket(AF_UNIX,SOCK_STREAM,0); | | | | /* имя гнезда - не может включать пустой символ */ | | bind(sd,"sockname",sizeof("sockname") - 1); | | listen(sd,1); | | | | for (;;) | | { | | | | ns = accept(sd,&sockaddr,&fromlen); | | if (fork() == 0) | | { | | /* потомок */ | | close(sd); | | read(ns,buf,sizeof(buf)); | | printf("сервер читает '%s'\n",buf); | | exit(); | | } | | close(ns); | | } | | } | +------------------------------------------------------------+ Рисунок 11.20. Процесс-сервер в домене "UNIX system" +------------------------------------------------------------+ | #include | | #include | | | | main() | | { | | int sd,ns; | | char buf[256]; | | struct sockaddr sockaddr; | | int fromlen; | | | | sd = socket(AF_UNIX,SOCK_STREAM,0); | | | | /* имя в запросе на подключение не может включать | | /* пустой символ */ | | if (connect(sd,"sockname",sizeof("sockname") - 1) == -1)| | exit(); | | | | write(sd,"hi guy",6); | | } | +------------------------------------------------------------+ Рисунок 11.21. Процесс-клиент в домене "UNIX system" 359 процесс-клиент получает виртуальный канал связи с сервером. В рассматривае- мом примере клиент передает одно сообщение и завершается. Если сервер обслуживает процессы в сети, указание о том, что гнездо при- надлежит домену "Internet", можно сделать следующим образом: socket(AF_INET,SOCK_STREAM,0); и связаться с сетевым адресом, полученным от сервера. В системе BSD имеются библиотечные функции, выполняющие эти действия. Второй параметр вызываемой клиентом функции connect содержит адресную информацию, необходимую для иден- тификации машины в сети (или адреса маршрутов посылки сообщений через проме- жуточные машины), а также дополнительную информацию, идентифицирующую прием- ное гнездо машины-адресата. Если серверу нужно одновременно следить за сос- тоянием сети и выполнением локальных процессов, он использует два гнезда и с помощью функции select определяет, с каким клиентом устанавливается связь в данный момент. 11.5 ВЫВОДЫ Мы рассмотрели несколько форм взаимодействия процессов. Первой формой, положившей начало обсуждению, явилась трассировка процессов - взаимодействие двух процессов, выступающее в качестве полезного средства отладки программ. При всех своих преимуществах трассировка процессов с помощью функции ptrace все же достаточно дорогостоящее и примитивное мероприятие, поскольку за один сеанс функция способна передать строго ограниченный объем данных, требуется большое количество переключений контекста, взаимодействие ограничивается только формой отношений родитель-потомок, и наконец, сама трассировка произ- водится только по обоюдному согласию участвующих в ней процессов. В версии V системы UNIX имеется пакет взаимодействия процессов (IPC), включающий в себя механизмы обмена сообщениями, работы с семафорами и разделения памяти. К со- жалению, все эти механизмы имеют узкоспециальное назначение, не имеют хоро- шей стыковки с другими элементами операционной системы и не действуют в се- ти. Тем не менее, они используются во многих приложениях и по сравнению с другими схемами отличаются более высокой эффективностью. Система UNIX поддерживает широкий спектр вычислительных сетей. Традици- онные методы согласования протоколов в сильной степени полагаются на помощь системной функции ioctl, однако в разных типах сетей они реализуются по-раз- ному. В системе BSD имеются системные функции для работы с гнездами, поддер- живающие более универсальную структуру сетевого взаимодействия. В будущем в версию V предполагается включить описанный в главе 10 потоковый механизм, повышающий согласованность работы в сети. 11.6 УПРАЖНЕНИЯ 1. Что произойдет в том случае, если в программе debug будет отсутствовать вызов функции wait (Рисунок 11.3) ? (Намек: возможны два исхода.) 2. С помощью функции ptrace отладчик считывает данные из пространства трассируемого процесса по одному слову за одну операцию. Какие измене- ния следует произвести в ядре операционной системы для того, чтобы уве- личить количество считываемых слов ? Какие изменения при этом необходи- мо сделать в самой функции ptrace ? 3. Расширьте область действия функции ptrace так, чтобы в качестве пара- метра pid можно было указывать идентификатор процесса, не являющегося потомком текущего процесса. Подумайте над вопросами, связанными с защи- той информации: При каких обстоятельствах процессу может быть позволено 360 читать данные из адресного пространства другого, произвольного процесса ? При каких обстоятельствах разрешается вести запись в адресное прост- ранство другого процесса ? 4. Организуйте из функций работы с сообщениями библиотеку пользовательско- го уровня с использованием обычных файлов, поименованных каналов и эле- ментов блокировки. Создавая очередь сообщений, откройте управляющий файл для записи в него информации о состоянии очереди; защитите файл с помощью средств захвата файлов и других удобных для вас механизмов. По- сылая сообщение данного типа, создавайте поименованный канал для всех сообщений этого типа, если такого канала еще не было, и передавайте со- общение через него (с подсчетом переданных байт). Управляющий файл дол- жен соотносить тип сообщения с именем поименованного канала. При чтении сообщений управляющий файл направляет процесс к соответствующему поиме- нованному каналу. Сравните эту схему с механизмом, описанным в настоя- щей главе, по эффективности, сложности реализации и функциональным воз- можностям. 5. Какие действия пытается выполнить программа, представленная на Рисунке 11.22 ? *6. Напишите программу, которая подключала бы область разделяемой памяти слишком близко к вершине стека задачи и позволяла бы стеку при увеличе- нии пересекать границу разделяемой области. В какой момент произойдет фатальная ошибка памяти ? 7. Используйте в программе, представленной на Рисунке 11.14, флаг IPC_NOWAIT, реализуя условный тип семафора. Продемонстрируйте, как за счет этого можно избежать возникновения взаимных блокировок. 8. Покажите, как операции над семафорами типа P и V реализуются при работе с поименованными каналами. Как бы вы реализовали операцию P условного типа ? 9. Составьте программы захвата ресурсов, использующие (а) поименованные каналы, (б) системные функции creat и unlink, (в) функции обмена сооб- щениями. Проведите сравнительный анализ их эффективности. 10. На практических примерах работы с поименованными каналами сравните эф- фективность использования функций обмена сообщениями, с одной стороны, с функциями read и write, с другой. 11. Сравните на конкретных программах скорость передачи данных при работе с разделяемой памятью и при использовании механизма обмена сообщениями. Программы, использующие разделяемую память, для синхронизации заверше- ния операций чтения-записи должны опираться на семафоры. +------------------------------------------------------------+ | #include | | #include | | #include | | #define ALLTYPES 0 | | | | main() | | { | | struct msgform | | { | | long mtype; | | char mtext[1024]; | | } msg; | | register unsigned int id; | | | | for (id = 0; ; id++) | | while (msgrcv(id,&msg,1024,ALLTYPES,IPC_NOWAIT) > 0)| | ; | | } | +------------------------------------------------------------+ 361