[ Страница назад | Страница вперед | Содержание | Индекс | Библиотека | Юридическая информация | Поиск ]

Программирование: Разработка и отладка программ


Работа с большими файлами

Начиная с версии 4.2, в AIX можно создавать файлы размером более 2 гигабайт (2 Гб). В этом разделе описаны особенности больших файлов, которые программист должен учитывать при разработке новых и модификации старых программ. В AIX встроен новый набор интерфейсов программирования, предназначенных для работы с большими файлами.

Ограничение на размер файла напрямую зависит от типа данных off_t. В AIX версии 4.1 тип off_t был определен как 32-разрядное целое число со знаком. Как следствие, максимальный размер файла был равен 2 Гб без одного байта.

Особенности работы старых программ

Старая среда, в которой размер файла задавался 32-разрядными значениями, осталась без изменений. Программы, не рассчитанные на большие файлы, будут работать точно так же, как и раньше. Соответственно, они не смогут работать с большими файлами.

Например, поле st_size в структуре stat, в которой программе передается размер файла, представляет собой 32-разрядное целое число со знаком. В связи с этим, данная структура непригодна для работы с файлами, размер которых превышает LONG_MAX. Если программа попытается определить размер большого файла с помощью структуры stat, в процедуре stat произойдет ошибка EOVERFLOW, указывающая на то, что размер файла не умещается в предоставленном поле.

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

Ошибка EOVERFLOW может также возникать в процедурах lseek и fcntl, если значения, которые должны будут вернуть эти процедуры, превысят емкость типа данных, используемого программой. В процедуре lseek ошибка будет возникать в тех случаях, когда смещение превышает LONG_MAX. В процедуре fcntl ошибка будет возникать при попытке выполнения операции F_GETLK, если смещение превышает LONG_MAX.

Защита открытых файлов

Разработчики многих применяемых в настоящее время программ не предполагали, что 32-разрядного поля со знаком может оказаться недостаточно для хранения информации о размере файла. Использование таких программ для работы с большими файлами могло бы повлечь за собой непредсказуемые результаты, включая потерю данных. В связи с этим, начиная с AIX версии 4.2, в операционной системе предусмотрены средства защиты, запрещающие старым программам выполнять операции, которые могут повредить большие файлы.

Если программа, не рассчитанная на работу с большими файлами, попытается открыть файл размером более LONG_MAX, процедура open выдаст ошибку EOVERFLOW. Благодаря этому старые программы не могут открывать большие файлы, что снижает вероятность потери данных из-за неправильной работы таких программ. Для того чтобы старые программы смогли работать с большими файлами, в них нужно внести ряд изменений. Эти изменения описаны в разделе "Модификация программ для работы с большими файлами".

В операционной системе предусмотрены и другие механизмы защиты больших файлов. Суть этих механизмов сводится к тому, что старые программы работают в той же среде, что и ранее, не имея возможности повредить большие файлы. Например, если приложение попытается записать в файл более 2 Гб данных с помощью процедур семейства write, то будет записано только 2 Гб - 1 байт. Если приложение попытается пересечь эту границу, то процедура write выдаст ошибку EFBIG. Процедуры mmap, ftruncate и fclear работают аналогично.

Процедуры семейства read также снабжены механизмами защиты. Если приложение попытается прочитать более 2 Гб данных из файла с помощью этих процедур, то будет прочитано только 2 Гб - 1 байт. Если приложение явно запросит данные, расположенные за этой границей, то будет выдана ошибка EOVERFLOW.

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

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

Модификация программ для работы с большими файлами

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

Определение символа _LARGE_FILES более предпочтительно, поскольку в этом случае программа остается переносимой на другие платформы, так как она по-прежнему соответствует стандартам POSIX и XPG. Недостаток этого способа заключается в том, что за счет автоматического переопределения типов данных и подстановки новых процедур текст программы перестает отражать фактические типы данных.

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

Очень важно понимать, что в любом случае измененная программа обязательно должна быть тщательно проверена - вы должны убедиться, что она правильно работает в среде с большими файлами. В разделе "Типичные ошибки при работе с большими файлами" приведены некоторые часто встречающиеся ошибки программистов.

Определение _LARGE_FILES

В стандартной среде компиляции тип данных off_t определен как 32-разрядное целое число со знаком. Начиная с AIX версии 4.2, если перед включением файлов заголовков в программе будет определен символ _LARGE_FILES, то программа будет компилироваться с поддержкой больших файлов и тип данных off_t будет определен как 64-разрядное целое со знаком. Кроме того, вызовы всех процедур, в которых используются указатели смещения в файлах и размер файлов, будут заменены на вызовы аналогичных процедур, поддерживающих большие файлы. Соответственно, будут переопределены все структуры данных, содержащие поля размера файла и смещения в файле.

Если первоначально программа была написана таким образом, что ее работа никак не зависела от размера типа данных off_t, то после перекомпиляции она должна правильно работать в новой среде. На практике очень часто оказывается, что в программы требуется внести дополнительные изменения.

В следующей таблице перечислены символы, переопределяемые при компиляции программ в режиме _LARGE_FILES для AIX версии 4.2.


Символ Заменяется на Файл заголовка
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>
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_listio() aio_listio64() <sys/aio.h>
aio_return() aio_return64() <sys/aio.h>
aio_error aio_error64() <sys/aio.h>

Применение функций работы с 64-разрядной файловой системой

Перекомпиляция программы в режиме _LARGE_FILES может быть нежелательной из-за большого числа неявных изменений, вносимых в программу при такой перекомпиляции. Если в программе очень мало функций, работающих с файловой системой, то целесообразно просто заменить имена этих функций. Ниже перечислены типы данных, структуры и процедуры 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);
 
<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 *[]);

Типичные ошибки при работе с большими файлами

При переносе программы в среду с поддержкой больших файлов может оказаться, что для работы программы требуется внести в нее определенные изменения. Большинство ошибок бывает вызвано неаккуратным составлением программы, за счет чего она может правильно работать в среде с 32-разрядной структурой off_t, но неправильно работать в среде с 64-разрядной структурой off_t. Ниже приведены наиболее часто встречающиеся ошибки и способы их исправления.

Примечание: В следующих примерах предполагается, что длина структуры off_t равна 64 разрядам.

Неправильный выбор типов данных

Самая распространенная ошибка в программах - неправильный выбор типов данных. Если программа хранит размеры файлов и положения указателей в переменных типа 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);  

Наилучший вариант:

void fexample(off_t);
 
fexample(0); 

Арифметические переполнения

Даже если в программе применяются правильные типы данных, она может быть подвержена арифметическим переполнениям. Обычно ошибки такого рода возникают в ситуациях, когда арифметическое переполнение наступает до преобразования значения в 64-разрядное целое. В следующем примере blkno - это 32-разрядный номер блока. Умножение номера блока на размер блока выполняется до преобразования значения в 64-разрядное целое, и поэтому результат умножения может вызвать переполнение несмотря на то, что длина принимающей переменной достаточна. Эта ошибка особенно опасна, поскольку программа правильно работает с малыми величинами, но начинает неправильно работать с большими. Во избежание этой ошибки преобразование типов должно выполняться до арифметических вычислений.

Неправильный вариант

int blkno;
off_t offset;
 
offset = blkno * BLKSIZE; 

Правильный вариант

int blkno;
off_t offset;
offset = (off_t) blkno * BLKSIZE;

Аналогичная ошибка может возникнуть при передаче констант в функции, рассчитанные на 64-разрядные параметры. В следующем примере выражение LONG_MAX+1 дает отрицательный результат, который и передается в вызываемую функцию.

Неправильный вариант

void fexample(off_t);
 
 fexample(LONG_MAX+1);                                          

Правильный вариант

void fexample(off_t);
 
fexample((off_t)LONG_MAX+1);    

Fseek/Ftell

Процедуры fseek и ftell рассчитаны на тип данных long и их нельзя переопределить для работы в 64-разрядной среде при компиляции программ в режиме _LARGE_FILES. Программы, использующие fseek и ftell для работы с большими файлами, должны быть изменены. Существует много способов исправления. Функционально процедуры fseeko и ftello равносильны fseek и ftell за исключением того, что для указателя смещения используется тип данных off_t, а не long. Обязательно исправьте типы всех переменных, в которых могут храниться значения смещения.

Неправильный вариант

long cur_offset, new_offset;
 
cur_offset = ftell(fp);
fseek(fp, new_offset, SEEK_SET);
           

Правильный вариант

off_t cur_offset, new_offset;
 
cur_offset = ftello(fp);
fseeko(fp, new_offset, SEEK_SET);  

Применение числовых значений вместо констант из файлов заголовков

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

Во многих программах, вызывающих процедуры open и creat, не используется файл <fcntl.h> , в котором определены константы режимов открытия файлов. Как правило, в таких программах явно заданы значения режимов. Это может привести к ошибкам при работе программ, откомпилированных в режиме _LARGE_FILES, поскольку компилятор не сможет откорректировать вызов процедуры open для работы с большими файлами. В программы обязательно должны включаться файлы заголовков, особенно в среде _LARGE_FILES.

Неправильный вариант

fd = open("afile",2);
  

Правильный вариант

#include <fcntl.h>
 
fd = open("afile",O_RDWR);

Преобразование строк

Преобразование размеров файлов и положения указателей в строковые значения и обратно также может послужить причиной ошибок при работе с большими файлами. Команда printf форматирует 64-разрядные целые числа не так, как 32-разрядные. Если вам необходимы подобные преобразования в программе, в них обязательно должны указываться модификаторы формата. Эта задача может оказаться непростой, особенно если программа должна быть переносима между 32- и 64-разрядной средой, поскольку переносимого модификатора формата не существует. Один из способов обхода этой проблемы заключается в том, чтобы функции учитывали размер типа данных off_t при преобразовании размера файлов и положения указателей.

off_t
atooff(const char *s)
{
         off_t o;
 
         if (sizeof(off_t) == 4)
                 sscanf(s,"%d",&o);
         else if (sizeof(off_t) == 8)
                 sscanf(s,"%lld",&o);
         else
                 error();
         return o;
}
         main(int argc, char **argv)
{
         off_t offset;
         offset = atooff(argv[1]);
         fexample(offset);
}

Явное указание смещений в программах

Программы, в которых жестко заданы смещения и размеры файлов, могут неправильно работать после перекомпиляции с измененным размером типа данных off_t. Эти ошибки могут быть особенно серьезны, когда одна структура данных используется несколькими программами или когда эта структура данных записывается в файл. В таких случаях программист должен решить, не стоит ли перейти от 32-разрядных значений смещения к 64-разрядным. Если программе требуется 32-разрядное смещение даже в случаях, когда длина off_t равна 64 битам, в ней следует применять новый тип данных soff_t - короткий off_t. Длина этого типа данных равна 32 даже в среде с большими файлами. Если упоминавшаяся выше структура данных будет преобразована таким образом, что в ней будут использоваться 64-разрядные значения смещения, программисту потребуется соответствующим образом откорректировать все программы, работающие с этой структурой.

Ограничения на размер файлов

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

struct rlimit r;
 
r.rlim_cur = r.rlim_max = RLIM_INFINITY;
setrlimit(RLIMIT_FSIZE,&r);

Это ограничение можно также изменять с помощью команды ulimit оболочки Korn:

ulimit -f unlimited

Жесткое ограничение на размер файлов для конкретного пользователя можно изменить с помощью команды chuser:

Пример:chuser fsize_hard = -1 root

Ограничения на размер файлов в JFS

Максимальный размер файлов ограничен не только средой, в которой работают процессы, но и параметрами конкретной файловой системы. Для файловых систем JFS максимальный размер файлов выбирается в момент их создания. Для файловых систем JFS, поддерживающих большие файлы, максимальный размер файлов немногим меньше 64 Гб (0xff8400000). Для остальных файловых систем JFS максимальный размер файлов равен 2 Гб - 1 (0x7fffffff). Попытка создать слишком большой файл в такой файловой системе приведет к ошибке с кодом EFBIG.

Ограничения на размер файлов в JFS2

В JSF2 размер файлов ограничен размером файловой системы.

Связанная информация

Поддержка файлов размером более 2 Гб

Глава 5, Файловые системы и каталоги

Работа с файлами

Распределение памяти в JFS2


[ Страница назад | Страница вперед | Содержание | Индекс | Библиотека | Юридическая информация | Поиск ]