Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Особенности поведения при работе с файлами



 

 

При работе с файлами информация записывается в файл или читается из файла, начиная с места, определяемого указателем текущей позиции в файле. Значение указателя увеличивается на количество реально прочитанных или записанных байт. При чтении информации из файла она не пропадает из него. Если системный вызов read возвращает значение 0, то это означает, что файл прочитан до конца.

 

 

Системный вызов close()

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

 

Прототип системного вызова:

#include < unistd.h>

int. close (int fd);

Описание системного вызова

Системный вызов close предназначен для корректного завершения работы с файлами и другими объектами ввода-вывода, которые описываются в операционной системе через файловые дескрипторы: pipe, FIFO, socket.

Параметр fd является дескриптором соответствующего объекта, т, е. значением, которое вернул один из системных вызовов open(), pipe() или socket().

 

Возвращаемые значения

 

Системный вызов возвращает значение 0 при нормальном завершении и значение -1 при возникновении ошибки.

 

Пример программы для записи информации в файл

Для иллюстрации сказанного давайте рассмотрим следующую программу:

/* Программа, иллюстрирующая использование системных вызовов open(), write() и close() длязаписи информации в файл*/

#include < sys/types.h>

#include < fcntl.h>

#include < stdio.h>

int main(){

int fd;

size__t size;

char string[] = " Hello, world! ";

/* Обнуляем маску создания файлов текущего процесса длятого, чтобы права доступа у создаваемого файла точно соответствовали параметру вызова open() */

(void)umask(0);

/* Попытаемся открыть файл с именем myfile в текущей директории только для операций вывода. Если файла не существует, попробуем его создать с правами доступа 0666, т. е. read-write для всех категорий пользователей */

if((fd = open(" myfile", O_WRONLY | O_CREAT, 0666)) < 0){

/* Если файл открыть не удалось, печатаем об этом сообщение и прекращаем работу */

printf(" Can\'t open file\n" );

exit(-1);

}

/* Пробуем записать в файл 14 байт из нашего массива, т.е. всю строку " Hello, world! " вместе с признаком конца строки */

size = write(fd, string, 14);

if(size! = 14){

/* Если записалось меньшее количество байт, сообщаем об ошибке */

printf(" Can\'t write all string\n" );

exit (-1);

}

/* Закрываем файл */

if (close(fd). < 0) {

printf(" Can\'t close file\n" );

}

return 0;

}

 

Обратите внимание на использование системного вызова umask() с параметром 0 для того, чтобы права доступа к созданному файлу точно соответствовали указанным в системном вызове open().

 

 

Понятие неименованного канала. Системный вызов pipe()

 

Наиболее простым способом для передачи информации с помощью потоковой модели между различными процессами или даже внутри одного процесса в операционной системе UNIX является pipe (канал, труба, конвейер).

Важное отличие pip'a от файла заключается в том, что прочитанная информация немедленно удаляется из него и не может быть прочитана повторно.

 

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

 

 

Прототип системного вызова:

#include < unistd.h>

int pipe(int *fd);

Описание системного вызова

Системный вызов pipe предназначен для создания pip'a внутри операционной системы.

Параметр fd является указателем на массив из двух целых переменных. При нормальном завершении вызова в первый элемент массива - fd[0] - будет занесен файловый дескриптор, соответствующий выходному потоку данных pip'a и позволяющий выполнять только операцию чтения, а во второй элемент массива – fd[1] - будет занесен файловый дескриптор, соответствующий входному потоку данных и позволяющий выполнять только операцию записи.

Возвращаемые значения

 

Системный вызов возвращает значение 0 при нормальном завершении и значение -1 при возникновении ошибок.

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

Естественно, по окончании использования входного или/и выходного потока данных, нужно закрыть соответствующий поток с помощью системного вызова close () для освобождения системных ресурсов. Необходимо отметить, что, когда все процессы, использующие pipe, закрывают все ассоциированные с ним файловые дескрипторы, операционная система ликвидирует pipe. Таким образом, время существования pip'a в системе не может превышать время жизни процессов, работающих с ним.

 

Организация связи через pipe между процессом-родителем и процессом-потомком.Наследование файловых дескрипторов при вызовах fork() и ехес().

Понятно, что если бы все достоинство pip'oв сводилось к замене функции копирования из памяти в память внутри одного процесса на пересылку информации через операционную систему, то овчинка не стоила бы выделки.

Однако таблица открытых файлов наследуется процессом-ребенком при порождении нового процесса системным вызовом fork() и входит в состав неизменяемой части системного контекста процесса при системном вызове exec() (за исключением тех потоков данных, для файловых дескрипторов которых был специальными средствами выставлен признак, побуждающий операционную систему закрыть их при выполнении exec(), однако их рассмотрение выходит за рамки нашего курса). Это обстоятельство позволяет организовать передачу информации через pipe между родственными процессами, имеющими общего прародителя, создавшего pipe.

Пример программы для организации однонаправленной связи между родственными процессами через pipe

Давайте рассмотрим программу, осуществляющую однонаправленную связь между процессом-родителем и процессом-ребенком:

/* Программа, осуществляющая однонаправленную связь через pipe между процессом-родителем и процессом-ребенком */

#include < sys/types.h>

#include < unistd.h>

#include < stdio.h>

Int main()

{

int fd[2], result;
size_t size;

char resstring[14];

/* Попытаемся создать pipe */

if(pipe(fd) < 0){

/* Если создать pipe не удалось, печатаем об этом сообщение и прекращаем работу */

printf(" Can\'t create pipe\n" );

exit(-1);

}

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

result = fork();

if(result){

/* Если создать процесс не удалось, сообщаем об этом и завершаем работу */

printf (" Can\'t fork child\n" );

exit (-1);

}

else if (result > 0) {

/* Мы находимся в родительском процессе, которыйбудет передавать информацию процессу-ребенку. В этом процессе выходной поток данных нам не понадобится, поэтому закрываем его.*/

close(fd[0]);

/* Пробуем записать в pipe 14 байт, т.е. всю строку " Hello, world! " вместе с признаком конца строки */

size = write(fd[l], " Hello, world! ", 14);

if(size! = 14) {

/* Если записалось меньшее количество байт, сообщаем об ошибке и завершаем работу */

printf(" Can\'t write all string\n" );

exit(-1);

}

/* Закрываем входной поток данных, на этом родитель прекращает работу */

close(fd[1]);

printf(" Parent exit\n" );

}

else {

/* Мы находимся в порожденном процессе, который будет получать информацию от процесса-родителя. Он унаследовал от родителя таблицу открытых файлов и, зная файловые дескрипторы, соответствующие pip'у, может его использовать. В этом процессе входной поток данных нам не понадобится, поэтому закрываем его.*/

close(fd[l]);

/* Пробуем прочитать из pip'a 14 байт в массив, т.е. всю записанную строку */

size = read(fd[0], resstring, 14);

if(size < 0) {

/* Если прочитать не смогли, сообщаем об ошибке и завершаем работу */

printf(" Can\'t read string\n" };

exit(-1); }

/* Печатаем прочитанную строку */

printf(" %s\n", resstring);

/* Закрываем входной поток и завершаем работу */

close(fd[0]);

}

return 0;

}


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-05-03; Просмотров: 521; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.026 с.)
Главная | Случайная страница | Обратная связь