Чтение онлайн

ЖАНРЫ

Linux программирование в примерах
Шрифт:

off_t curpos;

...

curpos = lseek(fd, (off_t)0, SEEK_CUR);

Буква

l
в
lseek
означает
long
.
lseek
был введен в V7 Unix, когда размеры файлов были увеличены; в V6 был простой системный вызов
seek
. В результате большое количество старой документации (и кода) рассматривает параметр offset как имеющий тип
long
, и вместо приведения к типу
off_t
довольно часто можно видеть суффикс L в константных значениях смешений:

curpos = lseek(fd, 0L, SEEK_CUR);

На

системах с компилятором стандартного С, где
lseek
объявлена с прототипом, такой старый код продолжает работать, поскольку компилятор автоматически преобразует 0L из
long
в
off_t
, если это различные типы.

Одной интересной и важной особенностью

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

Следующая программа демонстрирует создание дыр. Она записывает три экземпляра

struct
в начало, середину и дальний конец файла. Выбранные смешения (строки 16–18, третий элемент каждой структуры) произвольны, но достаточно большие для демонстрации особенности:

1 /* ch04-holes.c --- Демонстрация lseek и дыр в файлах. */

2

3 #include <stdio.h> /* для fprintf, stderr, BUFSIZ */

4 #include <errno.h> /* объявление errno */

5 #include <fcntl.h> /* для flags для open */

6 #include <string.h> /* объявление strerror */

7 #include <unistd.h> /* для ssize_t */

8 #include <sys/types.h> /* для off_t, etc. */

9 #include <sys/stat.h> /* для mode_t */

10

11 struct person {

12 char name[10]; /* имя */

13 char id[10]; /* идентификатор */

14 off_t pos; /* положение в файле для демонстрации */

15 } people[] = {

16 { "arnold", "123456789", 0 },

17 { "miriam", "987654321", 10240 },

18 { "joe", "192837465", 81920 },

19 };

20

21 int

22 main(int argc, char **argv)

23 {

24 int fd;

25 int i, j;

26

27 if (argc < 2) {

28 fprintf(stderr, "usage: %s file\n", argv[0]);

29 return 1;

30 }

31

32 fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666);

33 if (fd < 0) {

34 fprintf(stderr, "%s: %s: cannot open for read/write: %s\n",

35 argv[0], argv[1], strerror(errno));

36 return 1;

37 }

38

39 j = sizeof(people) / sizeof(people[0]); /*
число элементов */

Строки 27–30 гарантируют, что программа была вызвана правильно. Строки 32–37 открывают именованный файл и проверяют успешность открытия.

Вычисление числа элементов

j
массива в строке 39 использует отличный переносимый трюк число элементов является размером всего массива, поделенного на размер первого элемента. Красота этого способа в том, что он всегда верен: неважно, сколько элементов вы добавляете в массив или удаляете из него, компилятор это выяснит. Он не требует также завершающей сигнальной метки; т.е. элемента, в котором все поля содержат нули,
NULL
или т.п.

Работа осуществляется в цикле (строки 41–55), который отыскивает смещение байтов, приведенное в каждой структуре (строка 42), а затем записывает всю структуру (строка 49):

41 for (i = 0; i < j; i++) {

42 if (lseek(fd, people[i].pos, SEEK_SET) < 0) {

43 fprintf(stderr, "%s: %s: seek error: %s\n",

44 argv[0], argv[1], strerror(errno));

45 (void)close(fd);

46 return 1;

47 }

48

49 if (write(fd, &people[i], sizeof(people[i])) != sizeof(people[i])) {

50 fprintf(stderr, "%s: %s: write error: %s\n",

51 argv[0], argv[1], strerror(errno));

52 (void)close(fd);

53 return 1;

54 }

55 }

56

57 /* здесь все нормально */

58 (void)close(fd);

59 return 0;

60 }

Вот результаты запуска программы:

$ ch04-holes peoplelist /* Запустить программу */

$ ls -ls peoplelist /* Показать использованные размеры и блоки */

16 -rw-r--r-- 1 arnold devel 81944 Mar 23 17:43 peoplelist

$ echo 81944 / 4096 | bc -l /* Показать блоки, если нет дыр */

20.00585937500000000000

Случайно мы знаем, что каждый дисковый блок файла использует 4096 байтов. (Откуда мы это знаем, обсуждается в разделе 5 4.2 «Получение информации о файле». Пока примите это как данное.) Финальная команда bc указывает, что файлу размером 81944 байтов нужен 21 дисковый блок. Однако, опция -s команды ls, которая сообщает нам, сколько блоков использует файл на самом деле, показывает, что файл использует лишь 16 блоков! [48] Отсутствующие блоки в файле являются дырами. Это показано на рис. 4.2.

48

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

Поделиться с друзьями: