Работа с большими файлами
В AIX поддерживаются файлы размером более 2 Гб. В этом разделе описаны особенности больших файлов, которые программист должен учитывать при разработке приложений. С помощью ряда программных интерфейсов существующие приложения можно изменить таким образом, чтобы они поддерживали большие файлы. Интерфейсы файловой системы обычно основаны на типе данных off_t.
Особенности работы старых программ
32-разрядная среда, применявшаяся всеми приложениями в версиях младше AIX 4.2, осталась без изменений. Однако старые приложения не поддерживают обработку больших файлов.
Например, поле st_size структуры stat, в котором программе возвращается размер файла, представляет собой 32-разрядное целое число со знаком. В связи с этим, структура stat непригодна для работы с файлами, размер которых превышает LONG_MAX. Если приложение попытается вызвать функцию stat для файла, размер которого больше LONG_MAX, то в функции stat произойдет ошибка EOVERFLOW, означающая, что размер файла не умещается в соответствующем поле структуры.
Это может приводит к ошибкам в программах, не рассчитанных на работу с большими файлами, даже если эти программы не будут пытаться выполнять какие-либо операции с такими файлами.
Ошибка EOVERFLOW может возникать и при выполнении функций lseek и fcntl, если возвращаемые ими значения превосходят размер типа данных или структуры, применяемой программой. В функции lseek ошибка EOVERFLOW будет возникать в тех случаях, когда смещение превышает LONG_MAX. В функции fcntl ошибка EOVERFLOW может возникать при попытке выполнения операции F_GETLK, если смещение или длина блокируемой области превышает значение LONG_MAX.
Защита открытых файлов
Использование существующих программ для работы с большими файлами может повлечь за собой непредсказуемые результаты, включая потерю данных. Для защиты приложений от таких сбоев в AIX предусмотрены средства защиты, запрещающие старым программам выполнять операции, которые могут повредить большие файлы.
В операционной системе предусмотрены и другие механизмы защиты больших файлов. Суть этих механизмов сводится к тому, что старые программы работают в той же среде, что и ранее, не имея возможности повредить большие файлы. Например, если приложение попытается записать в файл более 2 ГБ данных с помощью функций семейства write, то будет записано только 2 ГБ - 1 байт. Если приложение попытается превысить это ограничение, то функция write выдаст ошибку EFBIG. Функции mmap, ftruncate и fclear работают аналогично.
Функции семейства read также снабжены механизмами защиты. Если приложение попытается считать более 2 Гб данных, то будет прочитано только 2 Гб минус 1 байт. Если приложение явно запросит данные, расположенные за этой границей, то будет выдана ошибка EOVERFLOW.
Для защиты открытых файлов в описание открытого файла теперь добавлен специальный флаг. Текущее состояние этого флага можно определить с помощью команды F_GETFL процедуры fcntl. Изменить его состояние можно с помощью команды F_SETFL процедуры fcntl.
Поскольку описания открытых файлов наследуются в семействе функций exec, то программы, передающие описания больших файлов другим программам, должны проверять, могут ли принимающие программы правильно работать с такими файлами.
Модификация программ для работы с большими файлами
- Первый из них заключается в добавлении определения _LARGE_FILES, которое правильно переопределяет все зависимые типы данных и структуры, а вместо старых функций подставляет новые функции, поддерживающие большие файлы. Этот способ сохраняет переносимость программы, так как она по-прежнему соответствует стандартам POSIX и XPG. Недостаток этого способа заключается в том, что за счет автоматического переопределения типов данных и подстановки новых функций текст программы перестает отражать фактические типы данных.
- Второй способ заключается в явной замене старых функций на функции, поддерживающие большие файлы. Изменение кода программы требует больше усилий и снижает возможность переноса программы на другие платформы. Этот подход целесообразен только в тех случаях, когда определение символа _LARGE_FILES нежелательно по каким-либо причинам, и изменения затрагивают только малую часть текста программы.
В любом случае приложение необходимо тщательно проверить на его совместимость с большими файлами.
Определение _LARGE_FILES
В стандартной среде компиляции тип данных off_t определен как 32-разрядное целое число со знаком. Если определение _LARGE_FILES задано перед включением каких-либо заголовочных файлов, то активируется среда с поддержкой больших файлов, а тип данных off_t определяется как 64-разрядное целое число со знаком. Кроме того, вызовы всех процедур, в которых используются указатели смещения в файлах и размер файлов, будут заменены на вызовы аналогичных процедур, поддерживающих большие файлы. Соответственно, будут переопределены все структуры данных, содержащие поля размера файла и смещения в файле.
В следующей таблице указано, какие определения изменяются в среде _LARGE_FILES:
| Объект | Заменяется на | Заголовочный файл |
|---|---|---|
| Объект off_t | long long | <sys/types.h> |
| Объект fpos_t | long long | <sys/types.h> |
| Структура struct stat | struct stat64 | <sys/stat.h> |
| Функция stat | stat64() | <sys/stat.h> |
| Функция fstat | fstat64() | <sys/stat.h> |
| Функция lstat | lstat64() | <sys/stat.h> |
| Функция mmap | mmap64() | <sys/mman.h> |
| Функция lockf | lockf64() | <sys/lockf.h> |
| Структура struct flock | struct flock64 | <sys/flock.h> |
| Функция open | open64() | <fcntl.h> |
| Функция creat | creat64() | <fcntl.h> |
| Параметр команды F_GETLK | F_GETLK64 | <fcntl.h> |
| Параметр команды F_SETLK | F_SETLK64 | <fcntl.h> |
| Параметр команды F_SETLKW | F_SETLKW64 | <fcntl.h> |
| Функция ftw | ftw64() | <ftw.h> |
| Функция nftw | nftw64() | <ftw.h> |
| Функция fseeko | fseeko64() | <stdio.h> |
| Функция ftello | ftello64() | <stdio.h> |
| Функция fgetpos | fgetpos64() | <stdio.h> |
| Функция fsetpos | fsetpos64() | <stdio.h> |
| Функция fopen | fopen64() | <stdio.h> |
| Функция freopen | freopen64() | <stdio.h> |
| Функция lseek | lseek64() | <unistd.h> |
| Функция ftruncate | ftruncate64() | <unistd.h> |
| Функция truncate | truncate64() | <unistd.h> |
| Функция fclear | fclear64() | <unistd.h> |
| Функция pwrite | pwrite64() | <unistd.h> |
| Функция pread | pread64() | <unistd.h> |
| Структура struct aiocb | struct aiocb64 | <sys/aio.h> |
| Функция aio_read | aio_read64() | <sys/aio.h> |
| Функция aio_write | aio_write64() | <sys/aio.h> |
| Функция aio_cancel | aio_cancel64() | <sys/aio.h> |
| Функция aio_suspend | aio_suspend64() | <sys/aio.h> |
| Функция aio_return | aio_return64() | <sys/aio.h> |
| Функция aio_error | aio_error64() | <sys/aio.h> |
| Структура liocb | liocb64 | <sys/aio.h> |
| Структура lio_listio | lio_listio64() | <sys/aio.h> |
Применение функций с поддержкой 64-разрядной файловой системы
<sys/types.h>
typedef long long off64_t;
typedef long long fpos64_t;
<fcntl.h>
extern int open64(const char *, int, ...);
extern int creat64(const char *, mode_t);
#define F_GETLK64
#define F_SETLK64
#define F_SETLKW64
<ftw.h>
extern int ftw64(const char *, int (*)(const char *,const struct stat64 *, int), int);
extern int nftw64(const char *, int (*)(const char *, const struct stat64 *, int,struct FTW *),int, int);
<stdio.h>
extern int fgetpos64(FILE *, fpos64_t *);
extern FILE *fopen64(const char *, const char *);
extern FILE *freopen64(const char *, const char *, FILE *);
extern int fseeko64(FILE *, off64_t, int);
extern int fsetpos64(FILE *, fpos64_t *);
extern off64_t ftello64(FILE *);
<unistd.h>
extern off64_t lseek64(int, off64_t, int);
extern int ftruncate64(int, off64_t);
extern int truncate64(const char *, off64_t);
extern off64_t fclear64(int, off64_t);
extern ssize_t pread64(int, void *, size_t, off64_t);
extern ssize_t pwrite64(int, const void *, size_t, off64_t);
extern int fsync_range64(int, int, off64_t, off64_t);
<sys/flock.h>
struct flock64;
<sys/lockf.h>
extern int lockf64 (int, int, off64_t);
<sys/mman.h>
extern void *mmap64(void *, size_t, int, int, int, off64_t);
<sys/stat.h>
struct stat64;
extern int stat64(const char *, struct stat64 *);
extern int fstat64(int, struct stat64 *);
extern int lstat64(const char *, struct stat64 *);
<sys/aio.h>
struct aiocb64
int aio_read64(int, struct aiocb64 *):
int aio_write64(int, struct aiocb64 *);
int aio_listio64(int, struct aiocb64 *[],
int, struct sigevent *);
int aio_cancel64(int, struct aiocb64 *);
int aio_suspend64(int, struct aiocb64 *[]);
struct liocb64
int lio_listio64(int, struct liocb64 *[], int, void *);
Типичные ошибки при работе с большими файлами
При переносе программы в среду с поддержкой больших файлов может оказаться, что для работы программы требуется внести в нее определенные изменения. Большинство ошибок бывает связано с неаккуратным составлением программы, что не заметно в среде с 32-разрядной структурой off_t, но приводит к сбоям в среде с 64-разрядной структурой off_t. В этом разделе описаны наиболее распространенные ошибки и способы их исправления.
Неправильный выбор типов данных
Самая распространенная ошибка в программах - выбор неправильного типа данных. Если программа хранит размеры файлов и положения указателей в переменных типа int, то значения этих величин могут быть усечены. Размеры файлов и смещение указателя в файле следует хранить в переменных типа off_t.
Неправильно:
int file_size;
struct stat s;
file_size = s.st_size;
Лучше:
off_t file_size;
struct stat s;
file_size = s.st_size;
Если функция принимает в качестве аргументов или возвращает 64-разрядные целочисленные значения, то эта функция и та функция, из которой она вызывается, должны одинаково интерпретировать типы аргументов и возвращаемого значения.
Если вместо 64-разрядного целого в функцию будет передано 32-разрядное целое, то не только этот, но и другие аргументы вызова функции могут быть проинтерпретированы неправильно, что сделает результат вызова функции непредсказуемым. Эта ошибка особенно серьезна при передаче скалярных значений в функции, рассчитанные на 64-разрядные целые числа.
Для того чтобы избежать подобных ошибок, следует правильно задавать прототипы функций. В следующих примерах fexample() - это функция, ожидающая в качестве параметра 64-разрядное смещение указателя в файле. В первом примере компилятор создаст вызов функции, в которую передается 32-разрядное целое число. Во втором примере явно указан модификатор типа "LL", и поэтому компилятор создаст правильный вызов функции. В последнем примере перед вызовом функции в программу помещен ее прототип, в котором указан тип аргумента. Это оптимальное решение, поскольку в этом случае сохраняется возможность переноса программы в 32-разрядную среду.
Неправильно:
fexample(0);
Лучше:
fexample(0LL);
Лучше всего:
\est: