Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Неименованные коммуникационные каналы Unix

СРЕДСТВА КОММУНИКАЦИИ ПРОЦЕССОВ

Коммуникационные каналы (pipe) относятся к основным средствам межпроцессорных взаимодействий (IPC – Inter Process Communication). Эти каналы в русскоязычной технической литературе называются также каналами передачи данных. Возникли они в составе операционной системы Unix, где до сих пор играют большую неявную роль при построении командных конвейеров, широко используемых в программировании на уровне командных оболочек.

Канал передачи данных отчетливо ассоциируется с трубопроводами, которые передают данные между процессами. Такой канал (или "труба") имеет два конца, в один данные поступают, из другого выходят. С точки зрения программиста, использующего канал, чтение данных из него и запись данных в канал можно выполнять, используя обычные операции чтения из файла и записи в файл на основе хэндлов.

В ходе своего исторического развития каналы передачи данных "мутировали" и к настоящему времени мы имеем две их разновидности: неименованные каналы и именованные каналы. Первая разновидность до сих пор широко используется в Unix, где ею обычно и ограничиваются. В дальнейшей части текущего раздела будем называть для сокращения неименованные каналы просто каналами, оставляя уточнения для дальнейшего изложения.

По классическому решению в Unix канал после создания имеет два конца, один из них может быть использован только для записи, а другой – только для чтения. (Ряд версий Unix расширил такое понимание канала и теперь в общем случае можно использовать оба конца канала как для записи, так и для чтения, но делать так не рекомендуется, потому что подобные действия чрезвычайно усложняют для программиста процесс действий с каналом.) По внутреннему строению неименованный канал представляет собой кольцевой буфер в памяти, разделяемой процессами, причем этот буфер снабжен внутренними указателями. Один из них задает место, с которого можно далее писать данные, а второй – место с которого начинаются еще не считанные данные.

Для создания канала передачи данных в Unix предназначена функция с прототипом

int pipe(int hpipe[2]).

Аргументом этой системной функции служит адрес массива для двух хэндлов, младший элемент этого массива при удачном выполнении функции дает хэндл для конца канала, с которого можно считывать данные, а второй элемент дает при этом хэндл конца канала, в который можно записывать данные.

Для записи в канал используется функция write, а для чтения – функция read. Причем применение функции read к каналам имеет небольшую специфику, которая на самом деле присутствует и для аналогичной операции с обычными файлами, но там она не бросается в глаза. Дело в том, что функция read возвращает как собственное значение число реально прочитанных байтов. Для файлов отличие числа прочитанных байтов от числа запрошенных оказывается равносильным ситуации конца файла. Строго говоря, согласно документации, ситуацию конца файла определяет только возврат нулевого значения в качестве числа байтов, прочитанных функцией read чтения из файла. Но для обычных файлов после получения меньшего числа байтов чем запрошено, не существует альтернативы обязательному получению нулевого числа байтов в результате следующего выполнения функции чтения.

Для каналов же передачи данных вычислительный поток (нить процесса в общем случае) приостанавливается на функции чтения из канала до тех пор, пока канал функционирует, но данные еще не поступили. Если же в выходной конец канала поступило сколько-то байтов, то независимо от их числа функция чтения возвращает сколько возможно с учетом запрошенного. В частности, возвращает меньше байтов чем запрошено, но такое получение данных может не иметь никакого отношения к завершению данных в канале и возникновению ситуации конец файла. После чтения какого-то числа байтов при очередном запросе данных функцией read канал может выдать следующую порцию данных, в лучшем случае ровно столько, сколько запрошено, но может опять вернуть меньше запрошенного.

Можно сказать, что инженерной "изюминкой" работы с каналами передачи данных является предоставление каналом того числа байтов, которые уже поступили через канал, не дожидаясь поступления всей запрошенной партии байтов. Таким образом, при работе с каналами передачи данных ситуация конца передачи данных определяется возвращением нулевого значения функцией чтения.

Другим существенным моментом организации программистом работы с каналом является формирование ситуации конца передачи данных (конца файла для читающей стороны). Таким средством является принудительное закрытие канала со стороны конца, в который происходит запись данных. С этой стороны выполняется операция close(хэндл_конца_записи).

Следующая программа, приведенная в листинге 11.1.1, демонстрирует использование канала передачи данных в Unix. Для такой демонстрации создается два вычислительных процесса на основе одной общей программы. Процесс-родитель, остающийся от исходного процесса после выполнения системного вызова fork(), определив по ненулевому коду возврата этой функции, что он именно родитель, выдает сообщение об этом и закрывает свой хэндл hpipe [1] для конца записи. Сам передающий конец канала при этом не закрывается, так как у него остается еще продублированный в дочернем процессе хэндл этого конца канала (Системный вызов fork() дублирует все, что возможно, для дочернего экземпляра процесса, в частности таблицы, которые задают соответствия хэндлов дескрипторам файлов.) Заметим, что для собственно закрытия передающего конца канала теперь необходимо закрытие хэндла hpipe [1] дочерним процессом, что последний и выполнит в свое время.

 

#include <stdio.h>

int main()

{int hpipe[2];

char buffer[18];

char txt[ ]="Text, for sending and demonstration pipes into Unix";

int off=0, actread, lentxt=sizeof(txt), len, pid;

 

printf("Begin working\n");

if (pipe(hpipe)= =-1) {perror("pipe");exit(1);}

if ((pid=fork())!=0)

{printf("I am Parent, and my Child ID=%d\n",pid);

close(hpipe[1]);

while(1)

{sleep(2);

actread=read(hpipe[0],buffer,11);

if (actread= =0) break;

buffer[actread]='\0';

printf("From pipe: %s len=%d\n",buffer,actread);

}

close(hpipe[0]);

printf("End Parent\n");

}

else

{printf("I am Child\n");

close(hpipe[0]);

while(1)

{len=(4<lentxt-off)? 4:lentxt-off;

write(hpipe[1],&txt[off],len);

off+=len;

if (len!=4) break;

sleep(1);

}

close(hpipe[1]);

printf("End Child\n");

}

return 0;

}

Листинг 11.1.1. Использование неименованных каналов в Unix

 

Затем, используя дополнительную задержку на пару секунд для демонстрации продолжительности процесса, родитель запрашивает 11 байтов данных у канала. Возвращаемое значение actread функции read проверяется на предмет определения закрытия канала со стороны передающей стороны. При определения такого закрытия "бесконечный" цикл чтения прерывается. Любое число байтов, полученное в цикле от функции чтения из канала, выводится для наблюдения на экран консоли. По прерыванию цикла чтения (со стороны родителя) читающий конец канала закрывается, выдается сообщение о завершении родителя, и процесс завершается командой return 0.

Дочерний процесс, обнаружив по нулевому значению функции fork(), что он именно дочерний, в составном операторе после else сообщает о себе и выполняет закрытие hpipe [0]. (Читающий конец канала остается управляемым тем же исходном дескриптором этого конца канала, доступ к которому имеется в родительском процессе через хэндл hpipe [0].) В "бесконечном" цикле дочерний процесс подготавливает значение переменной len, задающей число подлежащих передачи следующих байтов данных через канал. (Это число выбирается равным 4, за исключением последней порции передачи данных, когда она может оказаться меньшей величины – в связи с некратностью длины передаваемого текста числу 4.) Очередная порция передаваемого текста, задаваемая смещением в переменной off и числом в переменной len передается посредством хэндла hpipe [1] через канал. После передачи корректируется величина смещения off и выполняется проверка, не передана ли последняя порция данных. Последняя ситуация определяется по тому, что значение вспомогательной переменной len оказывается меньшим начального значения 4 (последняя переданная порция меньше или равна нулю). По прерыванию оператором break цикла выполняется закрытие передающей стороны канала через закрытие хэндла hpipe [1]. Выдается сообщение о завершении дочернего процесса и он завершается оператором return 0.

 

<== предыдущая лекция | следующая лекция ==>
Динамически распределяемая память | Переназначение хэндлов для доступа к каналу
Поделиться с друзьями:


Дата добавления: 2014-01-05; Просмотров: 596; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.021 сек.