Технически оба этих вызова являются расширениями базового стандарта POSIX:
fsync
в расширении «Синхронизация файлов» (FSC), a
fdatasync
в расширении «Синхронизированный ввод и вывод». Тем не менее, можно без проблем использовать их в системе GNU/Linux
4.8. Установка длины файла
Два системных вызова позволяют настраивать размер файла:
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length); /* XSI */
int ftruncate(int fd, off_t length); /* POSIX */
Как
должно быть очевидно из параметров,
truncate
принимает аргумент имени файла, тогда как
ftruncate
работает с дескриптором открытого файла. (Обычным является соглашение по именованию пар системных вызовов
xxx
и
fxxxx
, работающих с именами файлов и дескрипторами файлов. Мы увидим несколько примеров в данной и последующих главах.) В обоих случаях аргумент
length
является новым размером файла.
Этот системный вызов происходит от 4.2 BSD Unix, и на ранних системах мог использоваться лишь для сокращения длины файла, отсюда и название. (Он был создан, чтобы облегчить реализацию операции урезания в Фортране.) На современных системах, включая Linux, имя является неправильным, поскольку с помощью этих вызовов можно также увеличить, а не только сократить длину файла. (Однако, POSIX указывает, что возможность увеличения размера файла относится к расширению XSI.)
Для этих вызовов сокращаемый файл должен иметь разрешение на запись (для
truncate
), или должен быть открыт для записи (для
ftruncate
). Если файл сокращается, все данные после нового конца файла теряются. (Поэтому вы не можете сократить файл, снова удлинить его и найти там первоначальные данные.) Если файл-расширен, как в случае записи данных после
lseek
, данные между старым концом файла и новым концом файла читаются как нули.
Эти вызовы сильно отличаются от '
open(file, ... | O_TRUNC, mode)
', который полностью урезает файл, отбрасывая все его данные. Эти же вызовы просто устанавливают абсолютную длину файла в данное значение.
Эти функции довольно специализированы; они используются лишь четыре раза во всем коде GNU Coreutils. Мы представляем пример использования
ftruncate
в разделе 5.5.3 «Изменение отметок времени:
utime
».
4.9. Резюме
• Когда системный вызов завершается неудачей, он обычно возвращает -1, а в глобальной переменной errno устанавливается предопределенное значение, указывающее на проблему. Для сообщений об ошибках могут использоваться функции
perror
и
strerror
.
• Доступ к файлам осуществляется через небольшие целые, которые называются дескрипторами. Дескрипторы файлов для стандартного ввода, стандартного вывода и стандартной ошибки наследуются от родительского процесса программы. Другие получаются через
open
или
creat
. Для их закрытия используется
close
, a
getdtablesize
возвращает разрешенное максимальное число открытых файлов. Значение
umask
(устанавливаемое с помощью
umask
) влияет на права доступа, получаемые новыми файлами при создании с помощью
creat
или с флагом
O_CREAT
для
open
.
• Системные вызовы
read
и
write
соответственно читают и записывают данные. Их интерфейс прост. В частности, они не интерпретируют данные, файлы представлены линейными потоками байтов. Системный вызов
lseek
осуществляет ввод/выводе произвольным доступом: возможность перемещаться внутри файла.
• Для синхронного ввода/вывода предусмотрены дополнительные флаги для
open
, при этом данные записываются на физический носитель данных до возвращения
write
или
read
. Можно также форсировать запись данных на диск на управляемой основе с помощью
fsync
или
fdatasync
.
• Системные вызовы
truncate
и
ftruncate
устанавливают абсолютную длину файла. (На более старых системах они могут использоваться лишь для сокращения длины файла; на современных системах они могут также увеличивать файл.)
Упражнения
1. Используя лишь
open
,
read,
write
и
close
, напишите простую программу
copy
, которая копирует файл, имя которого дается в первом аргументе, в файл с именем во втором аргументе.
2. Усовершенствуйте программу
copy
так, чтобы она принимала "
–
" в значении «стандартный ввод» при использовании в качестве первого аргумента и в значении «стандартный вывод» в качестве второго аргумента. Правильно ли работает '
copy - -
'?
3. Просмотрите страничку справки для proc(5) на системе GNU/Linux. В частности, посмотрите подраздел
fd
. Выполните '
ls -l /dev/fd
' и непосредственно проверьте файлы в
/proc/self/fd
. Если бы
/dev/stdin
и дружественные устройства были бы в ранних версиях Unix, как это упростило бы код для программы V7
cat
? (Во многих других современных системах Unix есть каталог или файловая система
/dev/fd
. Если вы не используете GNU/Linux, посмотрите, что вы можете обнаружить в своей версии Unix.)
4. Даже если вы пока этого не понимаете, постарайтесь скопировать сегмент кода из V7
cat.c
, который использует
struct stat
и функцию
fstat
, в
ch04-cat.c
, чтобы она также сообщала об ошибке для '
cat file >> file
'.
5. (Простое) Предположив наличие
strerror
, напишите свою версию
perror
.
6. Каков результат выполнения '
ulimit -n
' на вашей системе?
7. Напишите простую версию программы
umask
, назвав ее
myumask
, которая принимает в командной строке восьмеричную маску. Используйте
strtol
с основанием 8 для преобразования строки символов аргумента командной строки в целое значение. Измените
umask
с помощью системного вызова
umask
.
Откомпилируйте и запустите
myumask
, затем проверьте значение
umask
с помощью стандартной команды
umask
. Объясните результаты. (Подсказка: в оболочке Bash введите '
type umask
'.)
8. Измените простую программу
copy
, которую вы написали ранее, для использования
open
с флагом
O_SYNC
. Используя команду
time
, сравните характеристики первоначальной и новой версии большого файла.
9. Мы сказали, что для
ftruncate
файл должен быть открыт для записи. Как можно открыть файл для записи, когда у самого файла нет доступа записи?