Отправляет email-рассылки с помощью сервиса Sendsay

Программирование и др.

  Все выпуски  

Программирование в Linux с нуля - исправленная версия - низкоуровневый ввод-вывод (часть II)


Программирование в Linux с нуля - низкоуровневый ввод-вывод (часть II) - исправленная версия

Сайт: http://www.lindevel.ru
Автор: Nikolay N. Ivanov <nn at lindevel dot ru>

Предисловие

Здравствуйте, уважаемые подписчики! Мы продолжаем изучать низкоуровневый ввод-вывод в Linux и сегодня поговорим о перемещении внутри файла.

1. Позиции

1.1. Основные понятия

Каждый раз открывая файл, вы попадаете в начальную позицию. Иными словами, первая операция чтения/записи будет обращена к началу файла. То есть ваша текущая позиция совпадает с началом файла. Каждый прочитанный системным вызовом read() или записанный системным вызовом write() байт перемещает текущую позицию вперед. Текущая позиция открытого файла находится в специальной таблице ядра, на которую указывает дескриптор файла. Если текущая позиция при чтении совпадает с концом файла, то файл счтитается полностью прочитанным. Если файл находится, например, на диске, то драйвер устройства избавляет вас от надобности постоянно контролировать позиции чтения/записи. Однако иногда требуется взять контроль в свои руки. Представьте себе, что вам надо дважды повторить побайтовое чтение файла. Если бы мы не имели возможности менять текущую позицию открытого файла, нам пришлось бы сначала закрыть файл, а затем опять открыть тот же файл. Идеология Unix не терпит таких трюков!

1.2. Смещение позиции

Возникает вопрос: куда можно переместить текущую позицию? Прежде чем ответить на это вопрос, давайте рассмотрим общепринятое соглашение по обозначению позиций. Существуют три константы:

  • SEEK_SET - начало файла
  • SEEK_CUR - текущая позиция
  • SEEK_END - конец файла
Перемещаясь по файлу обычно используется следующий формат записи:
КОНСТАНТА +/- СМЕЩЕНИЕ
В качестве смещения может выступать любое целое знаковое число. Рассмотрим несколько примеров:
  • SEEK_CUR - 1 - предыдущий байт
  • SEEK_SET + 20 - 20-й байт от начала файла
  • SEEK_END - 2 - предпоследний байт в файле
  • SEEK_CUR + 1 - следующий байт после конца файла (для записи)
В Linux для обозначения позиций используется специальный тип off_t, объявленный в заголовочном файле sys/types.h.

1.3. Системный вызов lseek()

Системный вызов lseek() позволяет перемещаться на указанную позицию в файле. Если перемещение прошло успешно, то возвращается новая текущая позиция, в случае ошибки возвращается отрицательное значение. Отрицательное значение, например, может быть возвращено, если применить к SEEK_SET отрицательное смещение. lseek() принимает три аргумента: дескриптор файла, позицию отсчета (одну из вышеупомянутых костант) и смещение:

off_t lseek (ДЕСКРИПТОР, СМЕЩЕНИЕ, ПОЗИЦИЯ);

Рассмотрим несколько примеров:

  • lseek (fd, 20, SEEK_CUR); - "перепрыгивает" на 20 байт вперед от текущей позиции.
  • lseek (fd, 64, SEEK_SET); - "перепрыгивает" на 64-й байт в файле.
  • lseek (fd, -2, SEEK_END); - "перепрыгивает" на предпоследний байт в файле.

2. Пример

В качестве примера рассмотрим простую программу, которая дважды читает один и тот же файл и выводит его на экран.


/* twice.c */
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void read_file (int fd)
{
 char ch;
 while (read (fd, &ch, 1) != 0)
  { write (1, &ch, 1); }; 
}

int main (int argc, char** argv)
{
 const char* usage = "Usage: twice <filename>\n";
 const char* warning = "Can't open file\n";
 if (argc != 2) {
  write (2, usage, strlen (usage));
  exit (1);
 }

 int fd = open (argv[1], O_RDONLY);
 if (fd < 0) {
  write (2, warning, strlen (warning));
  exit (2);
 }

 read_file (fd);
 lseek (fd, 0, SEEK_SET);
 read_file (fd);
 
 close (fd);
 exit (0);
}


Заключение

В следующем выпуске рассылки мы рассмотрим некоторые тонкости низкоуровневого ввода-вывода, а также познакомимся с несколькими новыми системными вызовами. Поздравляю прекрасную половину человечества с праздником 8 марта!

Если возникнут вопросы, направляйте их в форум на Lindevel.Ru (http://www.lindevel.ru) или на e-mail: nn@lindevel.ru. Спасибо за внимание!

Николай.


Рассылки Subscribe.Ru
FreeBSD

В избранное