Skip to content

Home Программирование Векторное чтение: readv()
Векторное чтение: 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 {
void * iov_base;
size_t iov_len; };

На самом деле структура iovec объявлена в другом заголовочном файле (bits/uio.h), который включается в файл sys/uio.h. Но вас это никак не должно волновать.

Элемент iov_base — это буфер, в который читается блок информации, a iov_ien — размер буфера.

В качестве примера рассмотрим программу (листинг 8.5), которая читает информацию о первом файле из архива, созданного программой tar. Для сохранения компактности будем считывать только следующую информацию:

  • имя файла (каталога);
  • режим файла (восьмеричное представление);
  • идентификатор пользователя (UID);
  • идентификатор группы (GID);
  • размер файла.

Каждому пользователю в системе присваивается индивидуальное число, которое называется идентификатором пользователя (User EDentifier). Аналогичным образом, каждая группа в системе имеет свой уникальный номер, называемый идентификатором группы (Group IDentifier). При помощи следующей команды пользователь может узнать свой UID:

$ id
uid=500(nn) gid=100(users) groups=6(disk),11(floppy)

В tar-архивах перед каждым упакованным файлом расположен заголовок, содержащий информацию об этом файле. Заголовок состоит из полей фиксированной длины. Обычно в заголовке каждого заархивированного файла содержится 16 полей, но мы рассмотрим только 5 полей первого файла, общей протяженностью 136 байт:

  1. Имя файла — 100 байт. Режим файла (восьмеричное представление) — 8 байт.
  2. UID в восьмеричной системе счисления — 8 байт.
  3. GID в восьмеричной системе счисления — 8 байт. Размер файла в восьмеричной системе счисления — 12 байт.

Полный список полей можно посмотреть в исходниках программы 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;

}

 

Рассмотрим программу по порядку. Сначала в нее включаются следующие заголовочные файлы:

  • sys/uio.h объявляет системный вызов readv() и структуру iovec;
  • fcntl.h объявляет системный вызов open(), а также флаг ojrdonly;
  • unistd.h объявляет системный вызов close();
  • stdio.h объявляет механизмы ввода-вывода стандартной библиотеки языка С;
  • Stdlib.h объявляет функции malloc(), free(), strtol() и atoi().

Затем создается уже знакомый нам макрос arr_size(), вычисляющий на стадии компиляции размер статического массива. В функции main о сначала объявляются две целые переменные: i (счетчик цикла) и fd (файловый дескриптор). После этого создается вспомогательный статический массив tfields, состоящий из целых чисел. Эти числа показывают размеры полей tar-архива. Потом объявляется массив структур iovec, в который будут счи-i тываться данные.

После проверки наличия аргумента выделяется память для буферов массива ? tarhead. Если выделение памяти прошло успешно, файл открывается в ре-i жиме "только для чтения". Все данные читаются одной инструкцией:

readv (fd, tarhead, ARR_SIZE (tarhead));

Затем эти данные выводятся на экран. Восьмеричные числа переводятся в десятичные функцией strtol(). И, наконец, буферы освобождаются и файл  закрывается.

Комментарии (0)

RSS feed Comments

Написать комментарий

smaller | bigger

busy
 

Регистрация




Top