Пятница , Декабрь 6 2019
Главная / Студентам / ОСиС / Лекция № 13. ОПЕРАЦИИ ОТКРЫТИЯ, ЧТЕНИЯ, ЗАПИСИ И ЗАКРЫТИЯ ФАЙЛОВ

Лекция № 13. ОПЕРАЦИИ ОТКРЫТИЯ, ЧТЕНИЯ, ЗАПИСИ И ЗАКРЫТИЯ ФАЙЛОВ

13.1. Создание и открытие файла 

Первой функцией Windows, которую мы подробно опишем, является функция CreateFile, используемая как для создания новых, так и для открытия существующих файлов. Для этой функции, как и для всех остальных, сначала приводится прототип, а затем обсуждаются соответствующие параметры и порядок работы с ней. 

Простейшее использование функции CreateFile иллюстрирует приведенный в предыдущей лекции пример ознакомительной Windows-программы (программа 12.3), содержащей два вызова функций, в которых для параметров dwShareMode, lpSecurityAttributes и hTemplateFile были использованы значения по умолчанию. Параметр dwAccess может принимать значения GENERIC_READ и GENERIC_WRITE

HANDLE CreateFile ( 
LPCTSTR lpName,  
DWORD dwAccess,  
DWORD dwShareMode, 
LPSECURITY_ATTRIBUTES lpSecurityAttributes,  
DWORD dwCreate,  
DWORD dwAttrsAndFlags,  
HANDLE hTemplateFile) 

Возвращаемое значение: в случае успешного выполнения – дескриптор открытого файла (типа HANDLE), иначе – INVALID_HANDLE_VALUE

Имена параметров иллюстрируют некоторые соглашения Windows. Префикс dw используется в именах параметров типа DWORD (32-битовые целые без знака), в которых могут храниться флаги или числовые значения, например счетчики, тогда как префикс lpsz (длинный указатель на строку, завершающуюся нулем), или в упрощенной форме – lр, используется для строк, содержащих пути доступа, либо иных строковых значений, хотя документация Microsoft в этом отношении не всегда последовательна. В некоторых случаях для правильного определения типа данных вам придется обратиться к здравому смыслу или внимательно прочесть документацию. 

lpName – указатель на строку с завершающим нулевым символом, содержащую имя файла, канала или любого другого именованного объекта, который необходимо открыть или создать. Допустимое количество символов при указании путей доступа обычно ограничивается значением МАХ_РАТН (260), однако в Windows NT это ограничение можно обойти, поместив перед именем префикс \\?\, что обеспечивает возможность использования очень длинных имен (с числом символов вплоть до 32 К). Сам префикс в имя не входит. О типе данных LPCTSTR говорится в одном из последующих разделов, а пока достаточно знать, что он относится к строковым данным. 

dwAccess – определяет тип доступа к файлу – чтение или запись, что соответственно указывается флагами GENERIC_READ и GENERIC_WRITE. Ввиду отсутствия флаговых значений READ и WRITE использование префикса GENERIC_ может показаться излишним, однако он необходим для совместимости с именами макросов, определенных в заголовочном файле Windows WINNT.H. Мы еще неоднократно столкнемся с именами, которые кажутся длиннее, чем необходимо. 

Указанные значения можно объединять операцией поразрядного «или», и тогда для получения доступа к файлу как по чтению, так и по записи, следует воспользоваться таким выражением: 

GENERIC_READ | GENERIC_WRITE 

dwShareMode – может объединять с помощью операции поразрядного «или» следующие значения: 

  • 0 – запрещает разделение (совместное использование) файла. Более того, открытие второго дескриптора для данного файла запрещено даже в рамках одного и того же вызывающего процесса. 
  • FILE_SHARE_READ – другим процессам, включая и тот, который осуществил данный вызов функции, разрешается открывать этот файл для параллельного доступа по чтению. 
  • FILE_SHARE_WRITE разрешает параллельную запись в файл. 

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

lpSecurityAttributes – указывает на структуру SECURITY_ATTRIBUTES. При вызовах функции CreateFile и всех остальных функций достаточно использовать значение NULL

dwCreate – конкретизирует запрашиваемую операцию: создать новый файл, перезаписать существующий файл и тому подобное. Может принимать одно из приведенных ниже значений, которые могут объединяться при помощи операции поразрядного «или» языка С. 

  • CREATE_NEW – создать новый файл; если указанный файл уже существует, выполнение функции завершается неудачей. 
  • CREATE_ALWAYS – создать новый файл; если указанный файл уже существует, функция перезапишет его. 
  • OPEN_E_XI STING – открыть файл; если указанный файл не существует, выполнение функции завершается неудачей. 
  • OPEN_ALWAYS – открыть файл; если указанный файл не существует, функция создаст его. 
  • TRUNCATE_EXISTING – открыть файл; размер файла будет установлен равным нулю. Уровень доступа к файлу, установленный параметром dwAccess, должен быть не ниже GENERIC_WRITE. Если указанный файл существует, его содержимое будет уничтожено. В отличие от случая CREATENEW выполнение функции будет успешным даже в тех случаях, когда указанный файл не существует. 

dwAttrsAndFlags – позволяет указать атрибуты файла и флаги. Всего имеется 16 флагов и атрибутов. Атрибуты являются характеристиками файла, а не открытого дескриптора, и игнорируются, если открывается существующий файл. Некоторые из наиболее важных флаговых значений приводятся ниже. 

  • FILE_ATTRIBUTE_NORMAL – этот атрибут можно использовать лишь при условии, что одновременно с ним не устанавливаются никакие другие атрибуты (тогда как для всех остальных флагов одновременная установка допускается). 
  • FILE_ATTRIBUTE_READONLY – этот атрибут запрещает приложениям осуществлять запись в данный файл или удалять его. 
  • FILE_FLAG_DELETE_ON_CLOSE – этот флаг полезно применять в случае временных файлов. Файл будет удален сразу же после закрытия последнего из его открытых дескрипторов. 
  • FILE_FLAG_OVERLAPPED – этот флаг играет важную роль при выполнении операций асинхронного ввода/вывода. 

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

  • FILE_FLAG_WRITE_THROUGH – устанавливает режим сквозной записи промежуточных данных непосредственно в файл на диске, минуя кэш. 
  • FILE_FLAG_NO_BUFFERING – устанавливает режим отсутствия промежуточной буферизации или кэширования, при котором обмен данными происходит непосредственно с буферами данных программы, указанными при вызове функций ReadFile или WriteFile (описаны далее). Соответственно требуется, чтобы начала программных буферов совпадали с границами секторов, а их размеры были кратными размеру сектора тома. Чтобы определить размер сектора при указании этого флага, можно воспользоваться функцией GetDiskFreeSpace
  • FILE_FLAG_RANDOM_ACCESS – предполагается открытие файла для произвольного доступа; Windows будет пытаться оптимизировать кэширование файла применительно к этому виду доступа. 
  • FILE_FLAG_SEQUENTIAL_SCAN – предполагается открытие файла для последовательного доступа; Windows будет пытаться оптимизировать кэширование файла применительно к этому виду доступа. Оба последних режима реализуются системой лишь по мере возможностей. 

hTemplateFile – дескриптор с правами доступа GENERIC_READ к шаблону файла, предоставляющему расширенные атрибуты, которые будут применены к создаваемому файлу вместо атрибутов, указанных в параметре dwAttrsAndFlags. Обычно значение этого параметра устанавливается равным NULL. При открытии существующего  файла параметр hTemplateFile  игнорируется. Этот параметр используется в тех случаях, когда требуется, чтобы атрибуты вновь создаваемого файла совпадали с атрибутами уже существующего файла. 

Оба вызова функции CreateFile в программе 12.3 максимально упрощены за счет использования для параметров значений по умолчанию, и, тем не менее, они вполне справляются со своими задачами. В обоих случаях было бы целесообразно использовать флаг FILE_FLAG_SEQUENTIAL_SCAN.  

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

В Windows Server 2003 предоставляется функция ReOpenFile, которая возвращает новый дескриптор с иными флагами, правами доступа и прочим, нежели те, которые были указаны при первоначальном открытии файла, если только это не приводит к возникновению конфликта между новыми и прежними правами доступа. 

13.2. Закрытие файла 

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

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

Рассмотрим следующий пример. 

BOOL CloseHandle (HANDLE hObject) 

Возвращаемое значение: в случае успешного выполнения функции – TRUE, иначе  –  FALSE

Функции UNIX, сопоставимые с рассмотренными выше, отличаются от них в нескольких отношениях. Функция (системный вызов) UNIX open возвращает целочисленный дескриптор (descriptor) файла, а не дескриптор типа HANDLE, причем для указания всех параметров доступа, разделения и создания файлов, а также атрибутов и флагов используется единственный целочисленный параметр of lag. Возможные варианты выбора, доступные в обеих системах, перекрываются, однако набор опций, предлагаемый Windows, отличается большим разнообразием. 

В UNIX отсутствует параметр, эквивалентный параметру dwShareMode. Файлы UNIX всегда являются разделяемыми. В обеих системах при создании файла используется информация, касающаяся его защиты. В UNIX для задания хорошо известных разрешений на доступ к файлу для владельца, членов группы и прочих пользователей используется аргумент mode. Функция close, хотя ее и можно сопоставить с функцией CloseHandle, отличается от последней меньшей универсальностью. Функции библиотеки С, описанные в заголовочном файле <stdio.h>, используют объекты FILE, которые можно поставить в соответствие дескрипторам (дисковые файлы, терминалы, ленточные устройства и тому подобные), связанным с потоками. Параметр mode функции fopen позволяет указать, должны ли содержащиеся в файле данные обрабатываться как двоичные или как текстовые. Имеются также опции открытия файла в режиме «только чтение», обновления файла, присоединения к другому файлу и так далее. Функция freopen обеспечивает возможность повторного использования объектов FILE без их предварительного закрытия. Средства для задания параметров защиты стандартной библиотекой С не предоставляются. Для закрытия объектов типа FILE предназначена функция fclose. Имена большинства функций стандартной библиотеки С, предназначенных для работы с объектами FILE, снабжены префиксом «f». 

13.3. Чтение файла 

Чтение файла выполняется с помощью следующей функции: 

BOOL ReadFile ( 
HANDLE hFile, 
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,  
LPDWORD lpNumberOfBytesRead,  
LPOVERLAPPED lpOverlapped) 

Возвращаемое значение: в случае успешного выполнения (которое считается таковым, даже если не был считан ни один байт из-за попытки чтения с выходом за пределы файла) – TRUE, иначе – FALSE

Пока мы будем предполагать, что дескрипторы файлов создаются без указания флага перекрывающегося ввода/вывода FILE_FLAG_OVERLAPPED в параметре dwAttrsAndFlags. В этом случае функция ReadFile начинает чтение с текущей позиции указателя файла, и указатель файла сдвигается на число считанных байтов. 

Если значения дескриптора файла или иных параметров, используемых при вызове функции, оказались недействительными, возникает ошибка, и функция возвращает значение FALSE. Попытка выполнения чтения в ситуациях, когда указатель файла позиционирован в конце файла, не приводит к ошибке; вместо этого количество считанных байтов (*lpNumberOfBytesRead) устанавливается равным 0

Параметры 

Описательные имена переменных и естественный порядок расположения параметров во многом говорят сами за себя. Тем не менее, ниже приводятся некоторые краткие пояснения. 

hFile – дескриптор считываемого файла, который должен быть создан с правами доступа GENERIC_READ. lpBuffer является указателем на буфер в памяти, куда помещаются считываемые данные. nNumberOfBytesToRead – количество байт, которые должны быть считаны из файла. 

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

lpOverlapped – указатель на структуру OVERLAPPED. На данном этапе просто устанавливайте значение этого параметра равным NULL

13.4. Запись в файл 

Запись в файл выполняется с помощью следующей функции: 

BOOL WritedFile ( 
HANDLE hFile,  
LPCVOID lpBuffer,  
DWORD nNumberOfBytesToWrite,  
LPDWORD lpNumberOfBytesWritten,  
LPOVERLAPPED lpOverlapped) 

Возвращаемое значение: в случае успешного выполнения – TRUE, иначе – FALSE

Все параметры этой функции вам уже знакомы. Заметьте, что успешное выполнение записи еще не говорит о том, что данные действительно оказались записанными на диск, если только при создании файла с помощью функции СгеateFile  не  был  использован флаг FILE_FLAG_WRITE_THROUGH.  Если во время вызова функции указатель файла был позиционирован в конце файла, Windows увеличит длину существующего файла. 

Функции ReadFileGather и WriteFileGather позволяют выполнять операции чтения и записи с использованием набора буферов различного размера.