| Векторное чтение: readv() |
|
Низкоуровневые механизмы ввода-вывода в Linux не ограничиваются только системными вызовами creat(), open(), close(), read(), write() И lseek(). В этом разделе будет описана еще одна концепция низкоуровневого ввода-вывода — векторное чтение файла. Сначала файл (или его фрагмент) условно разделяется на блоки фиксированной длины, каждый из которых в процессе чтения будет помещаться в отдельный буфер. Векторное чтение осуществляется через системный вызов readv(). В качестве абстракции файла здесь выступает обычный файловый дескриптор. Системный вызов readv() объявлен в файле sys/uio.h следующим образом: ssize_t readv (int FD, struct iovec * VECTOR, int VSIZE); Этот системный вызов записывает данные из файла с дескриптором fd в массив vector, состоящий из vsiZE-элементов. Возвращаемое значение— число прочитанных байтов. В случае ошибки возвращается -1. Структура iovec объявлена в файле sys/uio.h следующим образом: struct iovec {
На самом деле структура iovec объявлена в другом заголовочном файле (bits/uio.h), который включается в файл sys/uio.h. Но вас это никак не должно волновать. Элемент iov_base — это буфер, в который читается блок информации, a iov_ien — размер буфера. В качестве примера рассмотрим программу (листинг 8.5), которая читает информацию о первом файле из архива, созданного программой tar. Для сохранения компактности будем считывать только следующую информацию:
Каждому пользователю в системе присваивается индивидуальное число, которое называется идентификатором пользователя (User EDentifier). Аналогичным образом, каждая группа в системе имеет свой уникальный номер, называемый идентификатором группы (Group IDentifier). При помощи следующей команды пользователь может узнать свой UID: $ id
В tar-архивах перед каждым упакованным файлом расположен заголовок, содержащий информацию об этом файле. Заголовок состоит из полей фиксированной длины. Обычно в заголовке каждого заархивированного файла содержится 16 полей, но мы рассмотрим только 5 полей первого файла, общей протяженностью 136 байт:
Полный список полей можно посмотреть в исходниках программы tar (файл src/tar.h, структура posix_header). В нашей программе массив структур iovec будет статическим, а каждый буфер в этом массиве будет выделяться в куче (heap) при помощи функции malloc(). Программа reacitar.с #include <sys/uio.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #define ARR_SIZE(array) (sizeof(array)/sizeof(array[0])) int main (int argc, char ** argv) ( int i, fd; int tfieldsU = { 100, /* Name */ 8, /* Mode */ 8, /* UID */ 8, /* GID */ 12 }; /* Size (octal) */ struct iovec tarhead [ARR_SIZE(tfields) ] ,- if (argc < 2) { fprintf (stderr, "Too few arguments\n"),- return 1; } for (i = 0; i < ARR_SIZE (tfields); i++) { tarhead[i].iov_base = (char *) malloc (sizeof (char) * tfields[i]); if (tarhead[i].iov_base == NULL) { fprintf (stderr, "Cannot allocate " "memory for tarhead[%d]\n", i), return 1; } tarhead[i].iov_len = tfields[i]; fd = open (argvtl], 0_RDONLY); if (fd == -1) { fprintf (stderr, "Cannot open file "(%s)\n\ argvtl]); return 1; if (readv (fd, tarhead, ARR_SIZE (tarhead)) <= 0) { fprintf (stderr, "Cannot read header\n"); return 1; } printf ("name: %s\n", tarhead[0].iov_base); printf ("mode: %s\n", tarhead[1].iov_base); printf ("uid: %d\n", strtol (tarhead[2].iov_base, NULL, 8)); printf ("gid: %d\n", strtol (tarhead[3].iov_base, NULL, 8)); printf ("size: %d\n", strtol (tarhead[4] .iov_base, NULL, 8)); for (i = 0; i < ARR_SIZE (tarhead); i++) free (tarhead [ i ] . iov_base) ; close (fd); return 0; }
Рассмотрим программу по порядку. Сначала в нее включаются следующие заголовочные файлы:
Затем создается уже знакомый нам макрос arr_size(), вычисляющий на стадии компиляции размер статического массива. В функции main о сначала объявляются две целые переменные: i (счетчик цикла) и fd (файловый дескриптор). После этого создается вспомогательный статический массив tfields, состоящий из целых чисел. Эти числа показывают размеры полей tar-архива. Потом объявляется массив структур iovec, в который будут счи-i тываться данные. После проверки наличия аргумента выделяется память для буферов массива ? tarhead. Если выделение памяти прошло успешно, файл открывается в ре-i жиме "только для чтения". Все данные читаются одной инструкцией: readv (fd, tarhead, ARR_SIZE (tarhead)); Затем эти данные выводятся на экран. Восьмеричные числа переводятся в десятичные функцией strtol(). И, наконец, буферы освобождаются и файл закрывается.
Related Articles
Set as favorite
Bookmark
Email This
Hits: 216 Комментарии (0)RSS feed CommentsНаписать комментарий |