Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лаб.практикумСП.doc
Скачиваний:
39
Добавлен:
31.03.2015
Размер:
5.99 Mб
Скачать

13.7. Неименованные каналы

Каналы в ОС Unix реализованы в виде снабженных специальным механизмом управления буферов ввода – вывода, и для получения доступа к ним может быть использован тот же аппарат, что и при осуществлении доступа к файлам ОС Unix. Это последнее обстоятельство особенно приятно, так как избавляет пользователя от необходимости модифицировать разработанные им программы для обеспечения возможности построения из них конвейера.

Каждому каналу ОС Unix ставится в соответствие один описатель файла, и все дальнейшее управление каналом со стороны ОС Unix мало чем отличается от управления любым файлом. Назовем лишь два отличия канала от файла. Во-первых, каналу ОС Unix может, вообще говоря, не соответствовать ни один элемент ни одного каталога ОС Unix, и в этом случае время его существования определяется временем функционирования процесса, выполнившего операцию создания канала. Во-вторых, размер канала ограничен числом блоков диска, указатели, местоположения которых на диске содержатся в самом описателе файла, чаще всего размер канала бывает ограничен 5120 байтами. Ограничение размера канала является залогом высокой эффективности операций обмена с ним. Объяснением этому факту может служить следующее обстоятельство: при осуществлении операций обмена с каналом ОС Unix отводит для их реализации системные буферы ввода – вывода, а в виду небольшого размера канала он почти целиком умещается внутри этих буферов; таким образом, фактический обмен информацией между двумя процессами, для которых был создан канал, осуществляется без использования магнитного диска. В заключение добавим, что если процесс, осуществляющий вывод информации в канал, делает это быстрее, чем процесс, осуществляющий ввод информации из канала, то функционирование первого процесса приостанавливается. Такой подход обеспечивает надежную синхронизацию взаимного обмена информацией двух пользовательских процессов, использующих для этого канал.

Различные версии ОС Unix поддерживают, вообще говоря, различные типы каналов. Остановимся для начала на простейшем из них – неименованных каналах. Создать неименованный канал можно с помощью системного вызова pipe, формат которого приведен ниже:

pipe(fd);

int fd[2];

Здесь fd - идентификатор целого массива, содержащего два элемента. Системный вызов pipe возвращает в случае удачного завершения своего выполнения два дескриптора файла, первый из которых хранится в первом элементе упомянутого массива fd[0] и должен в дальнейшем использоваться для осуществления операций ввода из канала, а второй – во втором элементе массива fd[1] и должен в дальнейшем использоваться для осуществления вывода в канал. В случае успешного завершения системный вызов pipe возвращает значение щ, иначе возвращает значение -1 и код ошибки в переменной errno. Полученные в результате осуществления системного вызова pipe дескрипторы файлов могут быть использованы точно так же, как и любые другие дескрипторы файлов ОС Unix, например, они могут быть использованы в качестве аргументов системных вызовов dup и fcntl. Рассмотрим, например, фрагмент исходного текста программы на языке Си, в результате выполнения которого стандартный вывод программы будет переназначен на созданный канал:

int fd[2];

pipe(fd);

close(1);

fcntl(fd[1], F_DUPFD, 1);

close(fd[1]);

Примерно таким образом решается интерпретатором команд shell проблема реализации возможности переназначения стандартного вывода одной команды ОС Unix на стандартный ввод другой.

Далее, если пользовательский процесс создал канал и после этого осуществил системный вызов fork, породил тем самым процесс потомок, последний, как известно, унаследует оба дескриптора файла, соответствующих созданному каналу. В результате автоматически возникнет возможность установления коммуникаций между процессом предком и процессом потомком. Ниже приведен фрагмент исходного текста программы на языке Си, иллюстрирующий сказанное:

pipe(fd);

if(fork()) {

/* процесс предок */

close(fd[0]);

……………

} else {

/* процесс потомок */

close(fd[1]);

…………….

}

Понятно, что в такой ситуации закрытие процессом предком файла с дескриптором, используемым для выполнения операций вывода информации в канал, означает автоматическое закрытие файла с дескриптором, использовавшимся процессом потомком для выполнения операций ввода информации из канала. Приведем фрагмент исходного текста программы на языке Си, который может быть использован при создании канала для команд ls и wc:

if((pid = fork())) {

/* процесс предок */

dead = wait(&status);

} else {

pipe(fd); /* создание канала */

if(fork()) {

close(1);

fcntl(fd[1], F_DUPFD, 1);

close(fd[1]);

close(fd[0]);

execl(“/bin/ls”, “ls”, 0);

} else {

close(0);

fcntl(fd[0], F_DUPFD, 0);

close(fd[0]);

close(fd[1]);

exec(“/bin/wc”, “wc”, 0);

}

}

Рассмотрим исходный текст еще одной программы, назначением которой является копирование файлов.

#define BLOCK 512

char buffer[BLOCK];

main(argc, argv)

int argc;

char *argv[];

{

int i = atoi(argv[1]); /* число блоков для передачи */

int pipe_fd[2]; /* дескриптор файла канала */

pipe(pipe_fd); /* создать канал */

while(i--) {

write(pipe_fd[1], buffer, BLOCK);

read(pipe_fd[0], buffer, BLOCK);

}

}

Пример. Программа иллюстрирует применение системного вызова pipe.

#include <stdio.h>

main(argc, argv)

int argc;

char *argv[];

{

int fd[2], pid, status;

if(argc > 1) arg = argv[1];

if(pipe(fd) < 0) { /* Создание программного канала */

fprintf(stderr, “Pipe error\n”);

exit(1);

}

if((pid = fork()) < 0) { /* порождение нового процесса */

fprintf(stderr, “Fork error\n”);

exit(1);

}

if(pid > 0) { /* Родительский процесс */

/* Перенаправление стандартного ввода в программный канал */

close(fd[1]);

close(0);

dup(fd[0]);

wait(&status); /* Ждем окончания процесса потомка */

execl(“/bin/more”, “more”, 0); /*Запуск команды more */

}

else { /* Процесс потомок */

/* Перенаправление стандартного вывода в программный канал */

close(fd[0]);

close(1);

dup(fd[1]);

execl(“/bin/ls”, “ls”,”-l” 0); /*Запуск команды more */

}

}