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

12.7. Системные вызовы наследования пользовательских дескрипторов файла

Опишем теперь два системных вызова: dup и fcntl, использование которых позволяет создать копию пользовательского дескриптора и тем самым осуществить доступ к одному и тому же файлу из одного и того же пользовательского процесса с помощью двух различных пользовательских дескрипторов файла. Итак, системные вызовы dup и fcntl дают пользователю возможность осуществить операцию обмена с одним файлом, используя для этого поочередно несколько различных пользовательских дескрипторов файла.

Системный вызов dup. Системный вызов dup имеет следующий формат:

int dup(old_fd)

int old_fd;

Данный вызов обрабатывает свой единственный параметр как пользовательский дескриптор открытого файла и возвращает целое число, которое может быть использовано как еще один пользовательский дескриптор того же файла (его принято называть копией пользовательского дескриптора файла). Необходимо заметить, что с помощью копии пользовательского дескриптора файла к нему может быть осуществлен доступ того же типа и с использованием того же значения указателя записи – чтения, что и с помощью оригинального пользовательского дескриптора файла. Рассмотрим эту процедуру подробнее. Каждому пользовательскому процессу соответствует таблица открытых файлов процесса, а каждому пользовательскому дескриптору файла, открытого этим процессом, соответствует один элемент указанной таблицы открытых файлов процесса. Далее, элемент таблицы открытых файлов процесса, соответствующий данному пользовательскому дескриптору файла, содержит указатель местоположения одного элемента таблицы файлов. И, наконец, указанный элемент таблицы файлов содержит информацию, однозначно специфицирующую номер описателя файла, значение указателя чтение – запись и режим открытия файла. Таким образом, для того чтобы создать вторую ссылку на файл с сохранением всех атрибутов доступа к нему и значением указателя запись – чтение, достаточно создать в таблице открытых файлов процесса еще один элемент, являющийся точной копией элемента, порядковый номер которого был использован в качестве аргумента системного вызова dup. Порядковый номер созданного с помощью системного вызова dup элемента копии таблицы открытых файлов процесса и станет копией пользовательского дескриптора файла. Именно этот номер будет возвращен системным вызовом dup в результате его осуществления. Например, new_fd = dup(old_fd). Вызов dup возвращает дескриптор файла new_fd , который является синонимом дескриптора old_fd . По сути, в качестве нового дескриптора выдается номер первого свободного элемента в таблице открытых файлов. Таким образом, системный вызов dup находит самый маленький свободный элемент дескриптора файла и направляет его на тот файл, с которым связан дескриптор old_fd. Аварийное завершение системного вызова dup возможно в том случае, если указанный в качестве параметра системного вызова пользовательский дескриптор не соответствует открытому файлу или исчерпаны все файлы, которые могут быть одновременно открыты процессом. Если дескриптор файла задан с ошибкой, или уже открыто слишком много файлов, возвращается значение -1. При неудачном завершении, переменная errno может принимать следующие значения:

  • EBADF - некорректное значение параметров;

  • EMFILE - переполнена таблица открытых файлов процесса.

Хорошим примером использования системного вызова dup может послужить способ реализации гарантии открытости для пользовательского процесса файлов с пользовательскими дескрипторами файлов 0, 1 и 2 и назначение на них соответственно стандартного ввода, стандартного вывода и стандартного протокола. Итак, рассмотрим следующий фрагмент исходного текста программы на языке Си:

stdin = open(“/dev/tty”, O_RDONLY); /* fd0 */

stdout = open(“/dev/tty”, O_WRONLY); /* fd1 */

stderr = dup(1);

При внимательном изучении этого фрагмента могут возникнуть сомнения в том, что переменные stdin, stdout и stderr принимают значения, равные соответственно 0, 1 и 2. Действительно, если к моменту выполнения этого фрагмента программы пользовательским процессом, функционирующим в ее рамках, уже были открыты некоторые файлы, то значения переменных stdin, stdout и stderr могут оказаться, вообще говоря, произвольными. Чтобы избежать этих трудностей, модифицируем предлагаемый фрагмент программы следующим образом:

close(0); close(1); close(2); /* игнорировать возвращаемые значения */

stdin = open(“/dev/tty”, O_RDONLY); /* fd0 */

stdout = open(“/dev/tty”, O_WRONLY); /* fd1 */

stderr = dup(1); /* fd2 */

Итак, как видно из приведенного текста, прежде чем осуществить операцию открытия файлов с пользовательскими дескрипторами stdin, stdout и stderr, файлы с пользовательскими дескрипторами 0, 1 и 2 будут закрыты, в результате чего соответствующие элементы таблицы открытых файлов процесса окажутся свободными. Остается добавить, что системный вызов open, как, впрочем, и системный вызов dup, в результате своего выполнения возвращает порядковый номер первого от начала свободного элемента таблицы открытых файлов процесса, уменьшенный на 1.

Системный вызов dup2. Системный вызов dup2 имеет следующий формат:

int dup2(old_fd, new_fd)

int new_fd, old_fd;

Данный вызов предоставляет пользователям возможность самостоятельно назначать открываемым файлам конкретные пользовательские дескрипторы файлов. Системный вызов dup2 имеет два входных параметра, обрабатывая первый из них как оригинальный пользовательский дескриптор файла, а второй – как копию пользовательского дескриптора файла. Для того, чтобы осуществить системный вызов dup2, достаточно поместить в исходный текст программы на языке Си оператор вида: new_value = dup2(old_fd, new_fd);. Системный вызов dup2 в основном используется для переназначения стандартного ввода – вывода.

Пример. Ниже приведен пример программы, которая просматривает текущий справочник, а результат выводит в файл, указанный в командной строке.

#include <stdio.h>

main (argc, argv)

char *argv[];

{

int fd;

externint errno;

if(argc < 2) {

fprintf (stderr, “No file\n”);

exit(1);

}

/* Создаем файл с указанным именем */

if((fd = creat(argv[1], 0777)) < 0 {

fprintf(stderr, “Cannot creat file %s\n”, argv[1]);

exit(1);

}

/* Порождаем новый процесс */

switch(fork()) {

case -1: /* аварийное завершение */

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

exit(-1);

case 0: /* процесс потомок */

close(1); /* закрываем стандартный вывод */

dup(fd); /* дублируем дескриптор fd */

close(fd); /* закрываем дескриптор fd */

/* Теперь дескриптор 1 указывает на созданный ранее файл. Запускает команду ls

без аргументов, то есть просматривает текущий справочник. Результат будет

выводиться не на экран дисплея, а в файл */

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

perror(“Exec”); /* если ошибка */

break;

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

wait(0);

/* ожидаем завершения процесса потомка */

close(fd);

}

exit(0);

}

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

#include <fcntl.h>

result = fcntl(fd, command, argument);

Аргумент command может принимать одно из пяти значений, являющихся символьными константами, и в зависимости от значения этого аргумента системный вызов fcntl выполняет те или иные действия. Сопоставим значения аргумента command и выполняемые системным вызовом fcntl действия:

  • F_DUPFD - системный вызов fcntl возвращает первый свободный пользовательский дескриптор файла, значение которого не меньше значения аргумента argument. Этот пользовательский дескриптор файла должен быть копией пользовательского дескриптора файла, заданного аргументом fd;

  • F_SETFD - системный вызов устанавливает ассоциированный с пользовательским дескриптором файла fd флаг close-on-exec в состояние, заданное младшим битом аргумента argument. Напомним, если флаг close-on-exec установлен в состояние 1, то файл, имеющий пользовательский дескриптор файла fd, будет закрыт немедленно при осуществлении пользовательским процессом системного вызова exec;

  • F_GETFD - системный вызов fcntl возвращает значение флага close-on-exec, ассоциированного с пользовательским дескриптором файла fd;

  • F_GETFL - целое число, являющееся возвращенным значением системного вызова fcntl, специфицирует режим открытия файла, имеющего пользовательский дескриптор файла fd. При этом 0 соответствует режиму доступа только чтение, 1 – только запись, 2 – запись – чтение;

  • F_SETFL - системный вызов fcntl переустанавливает режим открытия уже открытого файла, имеющего пользовательский дескриптор файла fd.

С помощью системного вызова fcntl можно эмулировать системный вызов dup2 следующим образом:

dup2(old, nfd)

int ofd, nfd;

{

close(nfd);

return(fcntl(ofd, F_DUPFD, nfd));

}

С помощью системных вызовов dup и fcntl интерпретатор команд shell реализует каналы и переназначение стандартного ввода и стандартного вывода на файл.