Lseek изменить позицию в файле


Содержание

Описание функций языка Си

All | _ | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

fseek– установка позиции в потоке данных.

#include
int fseek(FILE *stream, long int offset, int whence);

stream – указатель на управляющую таблицу потока данных.
offset – смещение позиции.
whence – точка отсчета смещения.

0 – при успешной установки позиции.

Отличное от нуля значение, если при работе функции произошли ошибки. При этом переменной ernno будет присвоен код ошибки:
&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp [EINVAL] – неверное значение аргумента whence
&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp [ESPIPE] – недопустимое значение аргумента offset

Функция fseek() устанавливает позицию в потоке данных, заданным аргументом stream. Относительно установленной позиции будет осуществляться чтение и запись данных.

Точка отсчета устанавливаемой позиции определяется аргументом whence, который может принимать значения:
&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp SEEK_SET – смещение отсчитывается от начала файла
&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp SEEK_CUR – смещение отсчитывается от текущей позиции
&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp SEEK_END – смещение отсчитывается от конца файла

Смещение задается аргументом offset, причем положительное значение аргумента означает смещения вправо от указанной аргументом whence позиции, отрицательное – смещение влево.

Для двоичных потоков данных, смещение (offset) – это количество байт.

Для текстовых потоков данных смещение (offset) должно быть равно 0, либо получено с помощью функции ftell(), при этом точка отсчета (whence) должна иметь значение SEEK_SET.

В примере считывается строка из начала файла, а затем из позиции, смещенной относительно начала файла на 5 байт. Смещение задается с помощью функции fseek. В файле записана следующая строка: 123456789.

Вывод на консоль:

Открытие файла: выполнено
Установка позиции на начало файла: выполнено
Считываем строку: 123456789
Установка позиции на пятый байт: выполнено
Считываем строку: 6789
Закрытие файла: выполнено

Lseek изменить позицию в файле

НАЗВАНИЕ
lseek — передвижение указателя чтения/записи

ОПИСАНИЕ
Аргумент fildes — это дескриптор файла, полученный после выполнения системных вызовов creat, open, dup или fcntl. Системный вызов lseek устанавливает указатель текущей позиции файла, ассоциированного с дескриптором fildes, следующим образом, в зависимости от значения аргумента whence:

0 Значение указателя устанавливается равным offset байт.
1 К текущему значению указателя прибавляется значение offset.
2 Значением указателя становится размер файла плюс offset.

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

Системный вызов lseek завершается неудачей и указатель текущей позиции не изменяется, если выполнено хотя бы одно из следующих условий: [EBADF] Аргумент fildes не является корректным дескриптором открытого файла. [PIPE] Дескриптор файла fildes ассоциирован с каналом. [EINVAL и сигнал SIGSYS] Значение аргумента whence не равно 0, 1 или 2. [EINVAL] Дескриптор fildes не является дескриптором файла, расположенного на удаленном компьютере, и результирующее значение указателя текущей позиции отрицательно.

Некоторые устройства не поддерживают передвижение указателя текущей позиции. Для файлов, ассоциированных с такими устройствами, значение указателя не определено.

ДИАГНОСТИКА
При успешном завершении результатом служит неотрицательное целое число — указатель текущей позиции в файле; в случае ошибки возвращается -1, а переменной errno присваивается код ошибки

Способ перезаписать несколько позиций в файле

Я правильно читаю значения с некоторых позиций в моем файле, но у меня возникает проблема, когда мне нужно переписать эти значения. Все это портится в файле, после чего мой файл-ридер не находит то, что ему нужно, в нужных местах. Есть ли способ перезаписать всю строку и избежать этих ситуаций?
Я использую функцию lseek () для перемещения файлового указателя в нужную мне позицию.

Решение

Поскольку вы говорите «строка», я предполагаю, что вы говорите о текстовых файлах.

В этом случае: нет, нет способа перезаписать одну строку, Кроме если новая строка, которую вы хотите написать именно так одинаковой длины

Это потому, что абстракция файла на диске не работает, как редактирование файла, например, в. текстовый редактор, вы не можете сделать вставку / удаление в середине, не переписав все завершающие данные после точки изменения.

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

Другие решения

  1. прочитайте хвост после строки, чтобы изменить, и запишите это вместе с изменением, или
  2. прибегайте к файлам, отображенным в память: вы делаете изменения так же, как и в случае с C-строками, а остальная часть заботится об ОС.

Я бы предпочел файл с отображенной памятью. Когда переносимость является проблемой, у boost есть переносимая абстракция для них. Я хотел бы рассмотреть это, прежде чем прибегнуть к варианту 1.

lseek

Функция lseek() является частью UNIX-подобной системы ввода/вывода и не определена в стан­дарте ANSI С.

Функция lseek() устанавливает указатель положения в файле, указанном дескриптором handle, в положение, указанное аргументами offset и origin.

Действия lseek() зависят от значений аргументов origin и offset. Аргумент origin может прини­мать значения 0, 1 или 2. В следующей таблице показано, как интерпретируется смещение offset для каждого значения origin:

Origin Результат обращения к lseek()
Смещение отсчитывается от начала файла
1 Смещение отсчитывается от текущей позиции
2 Смещение отсчитывается от конца файла

В файле io.h определены перечисленные ниже макросы. Они могут использоваться в качестве зна­чения origin в порядке от 0 до 2.

SEEK_SET
SEEK_CUR
SEEK_END

В случае успеха функция lseek() возвращает offset, т.е. целое число типа long. При неудаче возвращается — 1L и errno устанавливается в одно из следующих значений:

Разработка приложений в среде Linux. Второе издание (44 стр.)

12: /* открыть файл, создавая его, если он не существовал, и удаляя

13: его содержимое в противном случае */

14: if ((fd = open(«hw», O_TRUNC | O_CREAT | O_WRONLY, 0644))

19: /* магическое число 18 — это кол-во символов, которые

20: будут записаны */

21: if (write(fd, «Добро пожаловать!\n», 18) != 18) <

Ниже показано, что получится, если запустить hwwrite .

cat: hw: No such file or directory

cat: hw: Файл или каталог не существует

Для изменения этой программы, чтобы она читала файл, нужно просто изменить open() , как показано ниже, и заменить write() статической строки на read() в буфер.

Файлы Unix можно разделить на две категории: просматриваемые (seekable) и непросматриваемые (nonseekable). Непросматриваемые файлы представляют собой каналы, работающие в режиме «первый вошел — первый вышел» (FIFO), которые не поддерживают произвольное чтение или запись, их данные не могут быть перечитаны или перезаписаны. Просматриваемые файлы позволяют читать и писать в произвольное место файла. Каналы и сокеты являются не просматриваемыми файлами; блоковые устройства и обычные файлы являются просматриваемыми.

Поскольку FIFO — это непросматриваемые файлы, то, очевидно, что read() читает их с начала, a write() пишет в конец. С другой стороны, просматриваемые файлы не имеют очевидной точки для этих операций. Вместо этого здесь вводится понятие «текущего» положения, которое перемещается вперед после операции. Когда просматриваемый файл изначально открывается, текущее положение устанавливается в его начало, со смещением 0. Если затем из него читается 10 байт, то текущее положение перемещается в точку со смещением 10 от начала, а запись 5 байт переписывает данные, начиная с одиннадцатого байта в файле (то есть, со смещения 10, где расположена текущая позиция после чтения). После такой записи текущая позиция находится в позиции, смещенной на 15 относительно начала файла — сразу после перезаписанных данных.

Если текущая позиция совпадает с концом файла и процесс пытается читать их этого файла, то read() возвращает 0 вместо ошибки. Если же данные записываются в конец файла, то он растет с тем, чтобы вместить дополнительные данные, и его текущая позиция устанавливается в конец файла. Каждый файловый дескриптор отслеживает независимую текущую позицию (она не хранится в файловом inode), поэтому если файл открыт множество раз множеством процессов, или несколько раз одним и тем же процессом, то чтения и записи, выполняемые через один дескриптор, никак не влияют на позиции чтения и записи, выполненные через другой дескриптор. Конечно, множественные операции записи могут повредить файл другими способами, поэтому блокировка определенного рода в такой ситуации может понадобиться.

Файлы, открытые с флагом O_APPEND ведут себя несколько иначе. Для таких файлов текущая позиция перемещается в конец файла перед тем, как ядро осуществит в него запись. После записи текущая позиция перемещается в конец записанных данных, как обычно. Для файлов, работающих в режиме только для добавления, это гарантирует, что текущая позиция файла всегда будет находиться в его конце немедленно после вызова write() .

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

int lseek(int fd, off_t offset, int whence);

Текущая позиция для файла fd перемещается на offset байт относительно whence , где whence принимает одно из следующих значений:

SEEK_SET Начало файла.

SEEK_CUR Текущая позиция в файле.

SEEK_END Конец файла.

Для SEEK_CUR и SEEK_END значение offset может быть отрицательным. В этом случае текущая позиция перемещается в сторону начала файла (от whence ), а не в сторону конца. Например, приведенный ниже код перемещает текущую позицию на 5 байт назад от конца файла.

lseek(fd, -5, SEEK_END);

Системный вызов lseek() возвращает новую текущую позицию в файле относительно его начала, либо -1 в случае ошибки. То есть lseek(fd, 0, SEEK_END) — это просто способ определения размера файла, но следует убедиться, что вы сбросили текущую позицию, прежде чем читать из fd .

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

Процесс A Процесс B


lseek(fd, 0, SEEK_END);

lseek(fd, 0, SEEK_END);

write (fd, buf, 10);

В этом случае процесс А перепишет первые 5 байт данных, которые запишет процесс В, а это наверняка не то, чего вы хотели. Если множеству процессов нужно совместно писать данные в конец файла, должен быть использован флаг O_APPEND , который делает эту операцию атомарной.

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

Части файлов, которые «создаются» подобным образом, называют «дырками» (holes). Чтение из такой «дырки» в файле возвращает буфер, полный двоичных нулей, а запись в них может завершиться ошибкой по причине отсутствия свободного пространства на диске. Все это значит, что вызов lseek() не должен применяться для резервирования дискового пространства для позднейшего использования, поскольку такое пространство может быть и не выделено. Если ваше приложение нуждается в выделении некоторого дискового пространства для последующего использования, вы должны применять write() . Файлы с «дырками» часто используют для хранения редко расположенных в них данных, таких как файлы, представляющие хеш-таблицы.

Linux.yaroslavl.ru

Эта глава описывает функции для выполнения операций ввод-вывода низкого уровня с помощью дескрипторов. Эти функции включают примитивы для функций ввода — вывода с более высоким уровнем, описанные в Главе 7 [Ввод — вывод на Потоках], также как функции для выполнения операций управления низкого уровня, для которых не имеется никаких эквивалентов на потоках. Ввод-вывод на уровне потоков более гибок и обычно более удобен; поэтому, программисты используют функции дескрипторного уровня только при необходимости. Вот несколько ситуаций, в которых могут понабиться дескрипторы:

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

Этот раздел описывает примитивы для открытия и закрытия файлов, используя дескрипторы файла. Функции creat и open объявлены в файле ‘fcntl.h’, в то время как close объявлена в ‘unistd.h’. -Функция: int open (const char *filename, int flags[, mode_t mode])

Функция open создает и возвращает новый описатель файла для указанного файла. Первоначально, индикатор файловой позиции для файла находится в начале файла. Аргумент mode используется только, когда файл создан.

Аргумент flags управляет тем, как файл будет открыт. Это ­ битовая маска; Вы создаете значение поразрядным ИЛИ соответствующих параметров (используя оператор `|’ в C).

Аргумент flags должен включять точно одно из этих значений, для задания режима доступа к файлу:

Файл существует, но не читаем/перезаписываем как запрошено аргументом flags. EEXIST

И O_CREAT и O_EXCL — установлены, а именованный файл уже существует. EINTR

Операция open была прервана сигналом. См. Раздел 21.5 [Прерванные Примитивы] 3. EISDIR

Аргумент flags задает доступ для записи, а файл — каталог. EMFILE

Процесс имеет слишком много открытых файлов. ENFILE

Вся система, или возможно файловая система, которая содержит каталог, не может поддерживать любые дополнительные файлы открытыми в настоящее время. (Эта проблема не может случаться в системе GNU.) ENOENT

Именованный файл не существует, но O_CREAT не определен. ENOSPC

Каталог или файловая система, которая содержала бы новый файл,не может быть расширена, т. к. там не осталось дискового пространства. ENXIO

O_NONBLOCK и O_WRONLY установлены в аргументе flags, файл, именованный filename — FIFO (см. Главу 10 [Каналы и FIFO]), и никакой процесс имеет файл открытым для чтения. EROFS

Файл постоянно находится в файловой системе только для чтения и любой из O_WRONLY, O_RDWR, O_CREAT, и O_TRUNC ­ установлены в аргументе flags.

Функция open — основной примитив для fopen и freopen функций, которые создают потоки.

-Функция: int creat (const char *filename, mode_t mode)

Эта функция устаревает. Обращение: creat (filename, mode) эквивалентно: open (filename, O_WRONLY | O_CREAT | O_TRUNC, mode)

-Функция: int close (int filedes)

Функция закрывает дескриптор файла filedes. Закрытие файла имеет следующие последствия:

  • Описатель файла освобожден.
  • Любые блокировки записи, принадлежащие процессу на файле разблокирутся.
  • Когда все описатели файла, связанные с каналом или в порядке поступления были закрыты, любые читаемые обратно данные отброшены.

Нормальное возвращаемое значение — 0; значение -1 возвращается в случае ошибки.

Следующие errno условия ошибки определены для этой функции:

  • EBADF Filedes аргумент — не допустимый дескриптор файла.
  • EINTR Обращение было прервано сигналом. См. Раздел 21.5 [Прерванные Примитивы]. Вот пример правильной обработки EINTR:

Этот раздел описывает функции для выполнения ввода и вывода с помощьюю дескрипторов файла: read, write, и lseek. Эти функции объявлены в файле ‘ unistd.h ‘. -Тип данных: ssize_t

Этот тип данных используется для представления размеров блоков, которые могут быть прочитаны или записаны одиночной операцией. Он подобен size _t, но должен быть знаковым типом.

-Функция: ssize_t read (int filedes, void *buffer, size_t size)

Функция read читает до size байтов из файла с описателем filedes, сохраняя результаты в буфере. (Это — не обязательно символьная строка и не имеется никакого добавленного пустого символа завершения.)

Возвращаемое значение — число байтов фактически прочитанных. Оно может быть меньше чем size.

Нулевое значение указывает конец файла (за исключением того, если значение аргумента size является также нулем). Это не является ошибкой.

Если read возвращает по крайней мере один символ, не имеется никакого способа, которым Вы можете узнать, был ли конец файла достигнут. Но если Вы достигали конца, следующий read возвратит нуль.

В случае ошибки read возвращает -1. Следующие errno условия ошибки определены для этой функции: EAGAIN

Обычно, когда никакой ввод недоступен, read ждет какого-нибудь ввода. Но если установлен флаг O_NONBLOCK (см. Раздел 8.10 [Флаги Состояния Файла]), read возвращается немедленно не читая никаких данных, и сообщает эту ошибку.

Примечание о Совместимости: Большинство версий BSD UNIX использует различный код ошибки для EWOULDBLOCK. В библиотеке GNU, EWOULDBLOCK — синоним EAGAIN, так что не имеет значения, акое название Вы используете.

На некоторых системах, чтение большого количества данных из символьного специального файла может также выдавать ошибку EAGAIN, если ядро не может найти достаточно памяти для страниц пользователя. Это ограничение для устройств, которые передают с прямым доступом в память, т. е. не включают терминалы, так как они всегда используют отдельные буфера внутри ядра. EBADF

Filedes аргумент — не допустимый описатель файла. EINTR

Чтение было прервано сигналом, в то время как он ждал ввода.

См. Раздел 21.5 [Прерванные Примитивы]. EIO Для многих устройств, и для файлов на диске, этот код ошибки указывает аппаратную ошибку. EIO также происходит, когда фоновый процесс пробует читать с терминала управления, и нормальное действие остановки процесса, т. е. Посылка сигнала SIGTTIN не работает. Это может случиться, если сигнал блокируется или игнорируется, или т. к. группа процесса — orphaned. См. Главу 24 [Управление заданиями].

Функция read — основной примитив для всех функций, которые читают из потоков, типа fgetc.

-Функция: ssize_t write (int filedes, const void *buffer, size_t size)

Функция write пишет до size байтов из буфера в файл с описателем filedes. Данные в буфере — не обязательно символьная строка и вывод пустого символа подобен любому другому.

Возвращаемое значение — число байтов, фактически записанных. Это — обычно size, но могло бы быть и меньшее количество. В случае ошибки, write возвращает -1. Следующие errno условия ошибки определены для этой функции: EAGAIN

Обычно, write блокируется до завершения операции записи. Но если для файла устанавлен O_NONBLOCK флаг (см. Раздел 8.7 [Операции Управления]), она возвращается немедленно не позводя записи любых данных, и сообщает эту ошибку. Пример ситуации, котаря могла бы вызывать блокировани процесса на выводе, запись в терминальное устройство, которое поддерживает управление потоком данных, где вывод был приостановлен получением символа STOP.

Примечание Совместимости: Большинство версий BSD UNIX использует различный код ошибки для EWOULDBLOCK. В библиотеке GNU, EWOULDBLOCK ­ синоним для EAGAIN, так что не имеет значения, какое название Вы используете.

Аргумент Filedes — не допустимый дескриптор файла. EFBIG

Размер файла больший чем реализация, может поддерживать. EINTR

Операция write была прервана сигналом, в то время как она была блокирована, и ждала завершения. См. Раздел 21.5 [Прерванные Примитивы]. EIO

Для многих устройств, и для файлов на диске, этот код ошибки указывает аппаратную ошибку. EIO также происходит, когда фоновый процесс пробует читать с терминала управления, и нормальное действие остановки процесса, посылая сигнал SIGTTIN не работает. Это может случиться, если сигнал блокируется или игнорируется, или т. к. группа процесса — orphaned. См. Главу 24 [Управление заданиями]. ENOSPC

переполнение устройства. EPIPE

Эта ошибка возвращается, когда Вы пробуете писать в канал или в FIFO, который не открыт для чтения процессом. Когда это случается, сигнал SIGPIPE также посылается в процесс; см. Главу 21 [Обработка Сигнала]. Если Вы не предотвращаете EINTR ошибки, Вам следует проверять errno после каждого выдающего ошибку обращения к write, и если ошибка была EINTR, Вы должны просто повторить обращение. См. Раздел 21.5 [Прерванные Примитивы]. Простой способ реализовать это — макрокомандой TEMP_FAILURE_RETRY, следующим образом:

Точно как Вы можете устанавливать файловую позицию потока функцией fseek, Вы можете устанавливать файловую позицию дескриптора функцией lseek. Она определяет позицию в файле для следующей операции read или write. См. Раздел 7.15 [Позиционирование Файла], для подробной информации относительно файловой позиции и что это означает.

Для получения текущего значения файловой позиции из описателя, используйте lseek (desc, 0, SEEK_CUR). -Функция: off_t lseek (int filedes, off_t offset, int whence)

Функция lseek используется, чтобы изменить файловую позицию файла с описателем filedes. Аргумент whence определяет, как смещение должно интерпретироваться, таким же образом как в функции fseek, и может быть одной из символических констант SEEK_SET, SEEK_CUR, или SEEK_END. SEEK_SET

Определяет, что whence — яисло символов от начала файла. SEEK_CUR

Определяет, что whence — число символов от текущей файловой позиции. Этот число может быть положительно или отрицательно. SEEK_END

Определяет, что whence — число символов с конца файла. Отрицательное число определяет позицию внутри текущего тела файла; положительное число определяет позицию после текущего конца. Если Вы устанавливаете позицию после текущего конца, и фактически записываете данные, Вы расширяете файл нулями до этой позиции.

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

Вы можете устанавливать файловую позицию после текущего конца файла. Это делает файл больше; lseek никогда не изменяет файл. Но последующий вывод в ту позицию расширит файла.

Если файловая позиция не может быть изменена, или операция выполняется некоторым недопустимым способом, lseek возвращает значение -1. Следующие errno условия ошибки определены для этой функции: EBADF

Filedes — не допустимый описатель файла. EINVAL

Значение аргумента whence не допустимо, или возникающее в результате смещение файла не допустимо.


filedes соответствует каналу или FIFO, который не может быть позиционирован. ( Могут иметься другие виды файлов, которые также не могут быть позиционированы, но в этих случаях поведение не определено.) Функция lseek — основной примитив для fseek, ftell и rewind функций, которые функционируют на потоках вместо описателей файла.

Вы можете иметь многократные описатели для того же самого файла, если Вы открываете файл больше чем один раз, или если Вы дублируете описатель с dup. Дескрипторы, которые исходят из отдельных обращений open, имеют независимые файловые позиции; использование lseek на одном дескрипторе не произодит никакого эффекта на другой. Например,

Напротив, описатели, сделанные дублированием совместно используют общую файловую позицию с первоначальным описателем, который был дублирован. Изменение файловой позиции одного из дубликатов, включая чтение или запись данных, воздействует на все из них. Таким образом, например,

Будет читать четыре символа, начиная с 1024-го символа ‘foo’, и еще четыре символа, начиная с 1028-го символа.

-Тип данных: оff_t

Это — арифметический тип данных, используемый, чтобы представить размеры файла. В системе GNU, он эквивалентен fpos_t или long int. Эти три синонима константы ‘SEEK_. ‘ существуют ради совместимости с более старыми BSD системами. Они определены в двух различных файлах: ‘fcntl.h’ и ‘sys/file.h’.

L_SET синоним для SEEK_SET.

L_INCR синоним SEEK_CUR.

L_XTND синоним SEEK_END.

Определяя дескриптор файла с помощью open, Вы можете создавать поток для него функцией fdopen. Вы можете получить основной описатель файла для существующего потока функцией fileno. Эти функции объявлены в заглавном файле ‘stdio.h’. -Функция: FILE * fdopen (int filedes, const char *opentype)

Функция fdopen возвращает новый поток для описателя файла filedes. Opentype аргумент интерпретируется таким же образом как в функции fopen (см. Раздел 7.3 [Открытие Потоков]), за исключением того, что опция ‘b’ не разрешается; это оттого, что GNU не делает никакого различия между текстом и двоичными файлами. Также, ‘w’ и ‘w+’ не вызывают усечение файла; они воздействуют только при открытии файла, а в этом случае, файл уже открыт. Вы должны удостовериться, что opentype аргумент соответствует фактическому режиму дескриптора файла.

Возвращаемое значение — новый поток. Если поток не может быть создан (например, если режимы для файла, обозначенного дескриптором файла не разрешают доступ, заданный opentype аргументом), взамен возвращается пустой указатель.

Для примера, показывающего использование функции fdopen, см. Раздел 10.1 [Создание Канала].

-Функция: int fileno (FILE *stream)

Эта функция возвращает описатель файла, связанный с указанным потоком. Если обнаружена ошибка (например, если поток не допустим) или если поток, не делает ввод — вывод в файл, fileno возвращает — 1.

Имеются также символические константы, определенные в ‘unistd.h’ для описателей файла, принадлежащих к стандартным потокам stdin, stdout, и stderr; см. Раздел 7.2 [Стандартные Потоки]. STDIN_FILENO

Эта макрокоманда имеет значение 0, которое является дескриптором файла для стандартного ввода. STDOUT_FILENO

Эта макрокоманда имеет значение 1, которое является дескриптором файла для стандартного вывода. STDERR_FILENO

Эта макрокоманда имеет значение 2, которое является дескриптором файла для стандартного вывода ошибки.

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

Самое лучшее использовать только один канал в вашей программе для фактической передачи данных в любой данный файл, за исключением того, когда весь доступ создан для ввода. Например, если Вы открываете канал (кое-что Вы можете делать только на уровне дескрипторов файла), или делать весь ввод — вывод через дескриптор, или создавать поток с fdopen, и тогда делать весь ввод — вывод через поток.

Связанные Каналы

Каналы, которые исходят из одного открытия, совместно используют ту же самую файловую позицию; мы называем их связанными каналами. Связанные каналы кончаются, когда Вы делаете поток из дескриптора, используя fdopen, и когда Вы получаете описатель из потока с fileno, и когда Вы копируете описатель с dup или dup2. Для файлов, которые не поддерживают, произвольный доступ, типа терминалов и канадов, все каналы действительно связаны. На файлах прямого доступа, все выходные потоки конкатенирующего типа действительно связаны друг с другом.

Если Вы использовали поток для ввода — вывода, и Вы хотите делать ввод — вывод, используя другой канал (или поток или дескриптор) который связан с этим, Вы должны сначала очистить поток, который Вы использовали. См. Раздел 8.5.3 [Очистка Потоков].

Завершение процесса, или выполнение новой программы в процессе, уничтожает все потоки в процессе. Если описатели, связанные с этими потоками сохраняются в других процессах, их файловые позиции станут неопределенными. Чтобы предотвратить это, Вы должны очистить потоки перед их разрушением.

Независимые Каналы

Когда Вы открываете каналы (потоки или описатели) отдельно, каждый канал имеет собственную файловую позицию. Они называются независимыми каналами.

Система обрабатывает каждый канал независимо. В большинстве случаев, это совершенно предсказуемостно и естественно (особенно для ввода): каждый канал может читать или писать последовательно в собственном месте в файле. Однако, если некоторые из каналов — потоки, Вы должны соблюдать предосторожности:

  • Вы должны oчистить выходной поток после использования, перед выполнением чего — нибудь еще, что могло бы читать или писать в ту же самую часть файла.
  • Вы должны oчистить входной поток перед чтением данных, которые могут изменяться использованием независимого канала. Иначе, Вы можете читать устаревшие данные, которые были в буфере потоков.

Если Вы выводите по одному каналу в конец файла, это конечно оставит другие независимые каналы, позиционированные где-нибудь перед новым концом. Если Вы хотите, чтобы они вывели в конец, Вы должны установить их файловые позиции в конец файла. (В этом нет нужды, если Вы используете описатель конкатенирующего типа или поток; они всегда выводят в текущий конец файла.)

Двум каналам невозможно иметь отдельные указатели файла для файла, который не поддерживает произвольный доступ. Таким образом, каналы для чтения или записи в такие файлы всегда связаны. Каналы конкатенирующего типа также всегда связаны. Для этих каналов, соблюдайте правила связанных каналов; см. Раздел 8.5.1 [Связанные Каналы].

Очистка Потоков

В системе GNU, Вы можете очистить любой поток функцией fclean: -Функция: int fclean (FILE *stream)

Очищает указанный поток так, чтобы буфер был пуст. На других системах, Вы можете использовать fflush, чтобы oчистить поток в большинстве случаев. Вы можете пропускать fclean или fflush, если Вы знаете, что поток уже пуст. Поток пуст всякий раз, когда буфер пуст. Например, небуферизованный поток всегда пуст. Входной поток, который находится в конце файла, пуст. Буферизированный строчно поток пуст, когда последний выходной символ был символ перевода строки.

Имеется один случай, в котором сброс потока является невозможным на большинстве систем. Тогда, когда поток делает ввод из файла, не являющегося файлом прямого доступа. Такие потоки обычно читают вперед, и когда файл ­ не произвольного доступа, то нет никакого способа сбросить обратно данные излишка уже прочтитанные. Когда входной поток читается из файла прямого доступа, fflush oчищает поток, но оставляет указатель файла в непредсказуемом месте; Вы должны установить указатель файла перед выполнением любого дальнейшего ввода — вывода. В системе GNU, используя fclean Вы избежите обе эти проблемы.

fflush также делает закрытие выходного потока, так что это ­ допустимый способ очистки выходного потока. В системе GNU, закрытие входного потока вызывает fclean.

Вы не нуждаетесь в чистке потока перед использованием дескриптора для операций управления, типа установки режимов терминала; эти операции не воздействуют на файловую позицию и не зависят от нее. Вы можете использовать любой дескриптор для этих операций, и на все каналы воздействовать одновременно. Однако, текст уже ‘выведенный’ в поток но все еще буферизируемый потоком будет подчинен новым режимам терминала. Чтобы удостовериться что ‘прошлый’ вывод подчинен установкам терминала, которые были в действительности во время, сбрасывайте выходные потоки для того терминала перед установкой режимов. См. Раздел 12.4 [Режимы Терминала].

Иногда программа должна принять ввод на нескольких входных каналах всякий раз, когда ввод прибывает. Например, некоторые рабочие станции могут иметь устройства типа планшета отцифровывания, поля функциональной клавиши, или поля набора кода, которые соединены через нормальные асинхронные последовательные интерфейсы; хороший стиль интерфейса пользователя требует, чтобы ответ немедленно воздействовал на любое устройство. Другой пример — программа, которая действует как сервер для нескольких других процессов через каналы или гнезда.

Вы не можете обычно использовать для этой цели read, потому что она блокирует программу до ввода на одном указанном описателе файла; ввод на других каналах не будет иметь действия. Вы могли бы установить режим не блокирования и опрашивать каждый описатель файла по очереди, но это очень неэффективно.

Лучшее решение состоит в том, чтобы использовать функцию выбора. Она блокирует программу до ввода или вывода по заданному набору описателей файла, или по таймеру. Это средство объявлено в заглавном файле ‘sys/types.h’.

Наборы описателей файла для функции выбора определены как объекты fd_set. Вот описание типа данных и некоторых макрокоманд для управления этих объектов.

fd_set (тип данных)

Этот тип данных представляет наборы описателей файла для функции выбора. Это — фактически битовый массив. int FD_SETSIZE (макрос)

Значение этой макрокоманды — максимальное число описателей файла, о которых объект fd_set может содержать информацию. На системах с фиксированным максимальным номером, FD_SETSIZE — по крайней мере это число. На некоторых системах, включая GNU, не имеется никакого абсолютного ограничения числа описателей, но эта макрокоманда все еще имеет постоянное значение, которые управляет числом битов в fd_set. void FD_ZERO (fd_set *set) (макрос)

Эта макрокоманда инициализирует набор наборов описателей файла, как пустое множество. void FD_SET (int filedes, fd_set *set) (макрос)

Эта макрокоманда добавляет filedes к набору описателей файлов. void FD_CLR (int filedes, fd_set *set) (макрос)

Эта макрокоманда удаляет filedes из набора дескрипторов файлов. int FD_ISSET (int filedes, fd_set *set) (макрос)

Эта макрокоманда возвращает значение отличное от нуля (истина), если filedes — элемент набора описателей файлов, и нуль (ложь) иначе.

Вот описание функции выбора непосредственно.

Описатели файла, заданные read_fds аргументом проверяются, являются ли они готовыми для чтения; write_fds описатели файла проверяются, являются ли они готовыми записи; и except_fds описатели файла задают исключительные условия. Вы можете передавать пустой указатель для любого из этих аргументов, если Вы не заинтересованы проверкой этого вида условия.

‘Исключительные условия’ не означают, что ошибки сообщаются немедленно, когда выполнен ошибочный системный вызов. Они включают условия, типа присутствия срочного сообщения на гнезде. (См. Главу 11 [Гнезда], для уточнения информации о срочных сообщениях.)

Функция выбора проверяет только первые nfds описателей файла. Обычно она передает FD_SETSIZE как значение этого аргумента.

Блокировка по времени определяет максимальное время ожидания. Если Вы передаете пустой указатель в качестве этого аргумента, это означает, что блокировать неопределенно, пока один из описателей файла не готов. Иначе, Вы должны обеспечить время в формате struct timeval; см. Раздел 17.2.2 [Календарь с высоким разрешением]. Определите нуль как время (struct timeval содержащий все нули) если Вы хотите выяснять, какие описатели готовы в данный момент, без ожидания.

Нормальное возвращаемое значение — общее число готовых описателей файла во всех наборах. Каждый из наборов аргументов записан поверх информацией относительно описателей, которые являются готовыми для соответствующей операции. Таким образом, чтобы видеть, готов ли данный описатель desc для ввода, используйте FD_ISSET (desc, read_fds) после возврата select.

Если select возвращается, потому что период блокировки по времени истекает, то возвращаемое значение — нуль.

Любой сигнал заставит select возвращаться немедленно. Так, если ваша программа использует сигналы, Вы не можете полагаться на select, чтобы ждать полное заданное время. Если Вы хотите убедиться в правильности ожидания, Вы должны проверить EINTR и повторять select с расчетной блокировкой по времени, основанной на текущем времени. См. пример ниже. См. также Раздел 21.5 [Прерванные Примитивы].

Если происходит ошибка, select возвращает -1 и не изменяет аргумент наборов описателей файла. Следующие errno условия ошибки определены для этой функции: EBADF

Один из набора описателей файла определил недопустимый дескриптор файла. EINTR

Операция была прервана сигналом. См. Раздел 21.5 [Прерванные Примитивы]. EINVAL

Аргумент блокировки по времени недопустим; один из компонентов отрицателен или слишком большой. Примечание о Переносимости: функция select — BSD возможность UNIX. Вот пример, показывающий, как Вы можете использовать select, чтобы установить период блокировки по времени для чтения из дескриптора файла. Input_timeout функция блокирует процесс вызова, пока ввод не доступен (на описателе файла), или пока период блокировки по времени не истек.

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

Второй аргумент fcntl функции — команда, которая определяет которую операцию выполнить. Функция и макрокоманды, которые обозначают различные флаги, объявлены в заглавном файле ‘fcntl.h’. (Многие из этих флагов также используются функцией open; см. Раздел 8.1 [Открытие и Закрытие Файлов].) -Функция: int fcntl (int filedes, int command, . )

Функция fcntl выполняет операцию, заданную командой, на описателе файла filedes. Некоторые команды требуют, чтобы были обеспечены дополнительные аргументы. Эти дополнительные аргументы и возвращаемое значение и условия ошибки даны в детализированных описаниях индивидуальных команд.

Вот краткий список различных команд. F_DUPFD

Дублирует дескриптор файла (возвращает другой дескриптор файла, указывающий на тот же самый открытый файл). См. Раздел 8.8 [Дублирование Дескрипторов]. F_GETFD

Получает флаги, связанные с описателем файла. См. Раздел 8.9 [Дескрипторные Флаги]. F_SETFD Устанавливает флаги, связанные с дескриптором файла. См. Раздел 8.9 [Дескрипторные Флаги]. F_GETFL

Получает флаги, связанные с открытым файлом. См. Раздел 8.10 [Флаги Состояния Файла]. F_SETFL

Устанавливает флаги, связанные с открытым файлом. См. Раздел 8.10 [Флаги Состояния Файла]. F_GETLK


Получает блокировку файла. См. Раздел 8.11 [Блокировки Файла].

Устанавливает или снимает блокировку файла. См. Раздел 8.11 [Блокировки Файла]. F_SETLKW

Подобен F_SETLK, но ждет завершения. См. Раздел 8.11 [Блокировки Файла]. F_GETOWN

Получает процесс или ID группы процессов, чтобы получить сигналы SIGIO. См. Раздел 8.12 [Ввод Прерывания]. F_SETOWN

Устанавливает процесс или ID группы процессов, чтобы получить сигналы SIGIO. См. Раздел 8.12 [Ввод Прерывания].

Вы можете дублировать дескриптор файла, или зарезервировать другой дескриптор файла, который относится к тому же самому открытому файлу, что и первый. Двойные дескрипторы совместно используют одну и ту же файловую позицию и один набор флагов состояния файла (см. Раздел 8.10 [Флаги Состояния Файла]), но каждый имеет собственный набор флагов дескриптора файла (см. Раздел 8.9 [Дескрипторные Флаги]).

Основное использование дублирования дескриптора файла это переадресация ввода или вывода: то есть замена файла или канала, которому указанный дескриптор файла соответствует.

Вы можете выполнять эту операцию, используя fcntl функцию с командой F_DUPFD, но имеются также удобные функции dup и dup2 для дублирования дескрипторов.

Fcntl функция и флаги объявлена в ‘fcntl.h’, в то время как прототипы для dup и dup2 находятся в файле ‘unistd.h’. -Функция: int dup (int old)

Эта функция копирует дескриптор old в первый доступный дескрипторный номер (первый номер не открытый сейчас). Это эквивалентно fcntl (old, F_DUPFD, 0).

-Функция: int dup2 (int old, int new)

Эта функция копирует дескриптор old в дескриптор new.

Если оld — недопустимый дескриптор, то dup2 не делает ничего. Иначе, новый дубликат old заменяет любое предыдущее значение дескриптора new, как будто он был закрыт сначала.

Если оld и new — различные числа, и old — допустимый дескрипторный номер, то dup2 эквивалентен:

Эта макрокоманда используется как аргумент команды fcntl, для копирования дескриптора файла, данного как первый аргумент.

Форма обращения в этом случае:

Возвращаемое значение из fcntl с этой командой — обычно значение нового дескриптора файла. Возвращаемое значение -1 указывает ошибку. Следующие errno условия ошибки определены для этой команды: EBADF

Аргумент old недопустим. EINVAL

Next_filedes аргумент недопустим. EMFILE

Дескрипторов файла, доступных вашей программе, больще нет. ENFILE

— не возможный код ошибки для dup2, потому что dup2 не создает новое открытие файла; двойные дескрипторные подпадают под ограничение, которое указывает ENFILE.

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

Вот пример, показывающий, как использовать dup2, чтобы делать переадресацию. Обычно, переадресация стандартных потоков (подобно stdin) выполняется командным интерпретатором или подобной программой перед вызовом одной из запускаемых функций (см. Раздел 23.5 [Выполнение Файла]) чтобы выполнить новую программу в дочернем процессе. Когда новая программа выполнена, она создает и инициализирует стандартные потоки, чтобы указать на соответствующие дескрипторы файла, прежде, чем функция main вызывается.

Так, чтобы переназначить стандартный ввод в файл, оболочка могла бы делать что — нибудь вроде:

Флаги дескриптора Файла — разнообразные атрибуты дескриптора файла. Эти флаги связаны со специфическими дескрипторами файла, так, что если Вы создаете двойные дескрипторы файла из одиночного открытия файла, то каждый дескриптор имеет собственный набор флагов.

В настоящее время имеется только один флаг дескриптора файла: FD_CLOEXEC, который заставляет дескриптор быть закрытым если Вы используете любую из запускаемых функций (см. Раздел 23.5 [Выполнение Файла]).

Символы в этом разделе определены в заглавном файле ‘fcntl.h’. int F_GETFD (макрос)

Эта макрокоманда используется как аргумент команды fcntl, чтобы определять, что она должна возвратить флаги дескриптора файла, связанные с filedes аргументом.

Нормальное возвращаемое значение из fcntl с этой командой ­ неотрицательное число, которое может интерпретироваться как поразрядное OR индивидуальных флагов (за исключением того, что в настоящее время имеется только один флаг).

В случае ошибки, fcntl возвращает -1. Следующие errno условия ошибки определены для этой команды: EBADF filedes аргумент недопустим.

int F_SETFD (макрос)

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна установить флаги дескриптора файла, связанные с filedes аргументом. Она требует третий int аргумент, чтобы определить новые флаги, так что форма обращения:

Следующая макрокоманда определена для использования как флаг дескриптора файла с fcntl функцией. Значение — константа integer пригодное для использования как значение битовой маски.

int FD_CLOEXEC (макрос)

Этот флаг определяет, что дескриптор файла должен быть закрыт, когда вызывается запускаемая функция; см. Раздел 23.5 [Выполнение Файл]. Когда дескриптор файла размещен (как с open или dup), этот бит первоначально очищен на новом описателе файла, означая, что дескриптор останется в живых в новой программе после запуска.

Если Вы хотите изменять флаги дескриптора файла, Вы должны получить текущие флаги с F_GETFD и изменять значение. Не думайте, что флаги, перечисленные здесь — единственные, которые реализованы, ваша программа может быть будет выполняться через годы, и тогда может существовать большее количество флагов. Например, вот функция, чтобы устанавливать или очистить флаг FD_CLOEXEC без того, чтобы изменять любые другие флаги:

Флаги состояния Файла используются, чтобы определить атрибуты открытия файла. В отличие от флагов дескриптора файла, обсужденных в Разделе 8.9 [Дескрипторные Флаги], флаги состояния файла разделяются дублированными дескрипторами файла, следующими из одиночного открытия файла.

Флаги состояния файла инициализируются функцией open из аргумента флагов функции open. Некоторые из флагов значимы только в open и не вспоминаются впоследствии; многие из оставшихся не могут впоследствии быть изменены, хотя Вы можете читать их значения, исследуя флаги состояния файла.

Несколько флагов состояния файла могут быть изменены, в любое время используя fcntl. Они включают O_APPEND и O_NONBLOCK.

Символы в этом разделе определены в заглавном файле ‘fcntl.h’. int F_GETFL (макрос)

Эта макрокоманда используется как аргумент команды fcntl, для чтения флагов состояния файла для файла открытого с описателем filedes.

Нормальное возвращаемое значение из fcntl с этой командой ­ неотрицательное число, которое может интерпретироваться как поразрядное ИЛИ индивидуальных флагов. Флаги закодированы так же, как аргумент флагов для open (см. Раздел 8.1 [Открытие и Закрытие Файлов]), но здесь значимы только режимы доступа файла и O_APPEND и O_NONBLOCK флаги. Так как режимы доступа файла — не одноразрядные значения, Вы можете маскировать другие биты в возвращенных флагах с O_ACCMODE, чтобы сравнить их.

В случае ошибки, fcntl возвращает -1. Следующие errno условия ошибки определены для этой команды: EBADF filedes аргумент недопустим.

-Макрос: int F_SETFL

Эта макрокоманда используется как аргумент команды fcntl, для установки флагов состояния файла для открытого файла, соответствующего filedes аргументу. Эта команда требует третьего int аргумента, чтобы определить новые флаги, так что обращение походит на это:

Нормальное возвращаемое значение из fcntl с этой командой ­ неопределенное значение отличное от -1, которое указывает ошибку. Условия ошибки — такие же как для команды F_GETFL.

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

  • O_APPEND — бит, который делает возможным конкатенирующий режим для файла. Если установлен, то все операции write пишут данные в конце файла, расширяя его, независимо от текущей файловой позиции.
  • O_NONBLOCK Бит, который делает возможным режим неблокирования для файла. Если этот бит установлен, запрос чтения в файле может возвращаться немедленно с состоянием отказа, если не имеется никакого немедленно доступного ввода кроме блокирования. Аналогично, запросы write могут также возвращаться немедленно с состоянием отказа, если вывод нельзя написать немедленно.
  • O_NDELAY Это — синоним для O_NONBLOCK, предусматривающий совместимость с BSD.

-Макрос: int O_ACCMODE

Эта макрокоманда замещает маску, которая может быть поразрядна AND со значением флага состояния файла, чтобы произвести значение, представляющее режим доступа файла. Режим будет O_RDONLY, O_WRONLY, или O_RDWR.

Остающиеся команды fcntl используются, чтобы поддерживать блокировку записей, который разрешает многим сотрудничающим программам предохранять друг друга от одновременного доступа к частям файла ошибочным способом.

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

Общедоступная блокировка или блокировка чтения запрещает любому другому процессу запрос блокировки записи в заданной части файла. Однако, другие процессы могут запрашивать блокировки чтения.

Функции read и write фактически не выясняет, имеются ли блокировки в данном месте.

Если Вы хотите выполнять протокол блокировки для файла, общедоступного многократными процессами, ваше приложение, должно делать явные обращения fcntl, чтобы запрашивать и очистить блокировки в соответствующих пунктах.

Блокировки связаны с процессами. Процесс может иметь только один вид набора блокировок для каждого байта данного файла. Когда любой дескриптор файла для того файла закрыт процессом, все блокировки, которые обрабатывают связи с тем файлом, опущены, даже если блокировки были сделаны, используя другие описатели, которые остаются открытыми. Аналогично, блокировки опущены, когда процесс выходит, и не наследуются дочерними созданными процессами (см. Раздел 23.4 [Создание Процесса]).

При создании блокировки, используйте struct flock, чтобы определить какую блокировку и где Вы хотите выполнить. Этот тип данных и связанные макрокоманды для fcntl функции объявлен в заглавном файле ‘fcntl.h’.

Эта структура используется с fcntl функцией, чтобы описать блокировку файла. Она имеет следующие элементы:

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна получить информацию относительно блокировки. Эта команда требует третьего аргумента типа struct flock *, чтобы быть переданной к fcntl, так, чтобы форма обращения была:

Вы должны определить тип блокировки F_WRLCK, если Вы хотите выяснять относительно блокировок чтения и записи, или F_RDLCK, если Вы хотите выяснять относительно блокировок только записи.

Может иметься больше чем одна блокировка, воздействующая на область, заданную lockp аргументом, но fcntl только возвращает информацию относительно одной из них. l_whence элемент структуры lockp установлен в SEEK_SET и l_start и l_len набор полей, чтобы идентифицировать блокированную область.

Если никакая блокировка не применяется, единственное изменение для структуры lockp, должно модифицировать l_type в значение F_UNLCK.

Нормальное возвращаемое значение из fcntl с этой командой ­ неопределенное значение отличное от -1, которое зарезервировано, чтобы указать ошибку. Следующие errno условия ошибки определены для этой команды: EBADF filedes аргумент недопустим.

Или lockp аргумент не определяет допустимую информацию блокировки, или файл, связанный с filedes не поддерживает блокировки. -Макрос int F_SETLK

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна установить или снять блокировку. Эта команда требует третьего аргумента типа struct flock *, так, чтобы форма обращения была:

Если блокировка не может быть установлена, fcntl возвращается немедленно со значением -1. Эта функция не блокирует ожидание пока другие процессы снимут блокировки. Если fcntl преуспевает, она возвращает значение отличное от -1.

Следующие errno условия ошибки определены для этой функции: EACCES

Блокировка не может быть установлена, потому что это блокировано существующей блокировкой на файле.

Некоторые системы используют EAGAIN в этом случае, и другие системы используют EACCES; ваша программа должна обработать их одинаково, после F_SETLK. EBADF


Также: filedes аргумент недопустим; Вы запросили блокировку чтения, но filedes — не открыт для чтения; или, Вы запросили блокировку записи, но filedes — не открыт для записи. EINVAL

Или lockp аргумент определяет недопустимую информацию блокировки, или файл, связанный с filedes не поддерживает блокировки. ENOLCK

Система работает без ресурсов блокировки файла; имеются уже слишком много блокировок файла.

Хорошо разработанные файловые системы никогда не сообщают эту ошибку, потому что они не имеют никакого ограничения на число блокировок. Однако, Вы должны все еще принимать во внимание возможности этой ошибки, поскольку это может следовать из доступа к сети к файловой системе на другой машине. int F_SETLKW (макрос)

Эта макрокоманда используется как аргумент команды fcntl, для определения, что она должна установить или снять блокировку. Это ­ тоже что команда F_SETLK, но заставляет процесс блокировать (или ждать) пока запрос не будет определен.

Эта команда требует третьего аргумента типа struct flock *, как для команды F_SETLK.

Fcntl возвращаемые значения и ошибки — те же что для команды F_SETLK, но эти дополнительные errno условия ошибки определены для этой команды:

Функция была прервана сигналом, во время ожидания. См. Раздел 21.5 [Прерванные Примитивы]. EDEADLK

Было обнаружено условие тупика. Это может случаться, если два процесса каждый из которых управляет уже заблокированной областью запрашивают блокировку на области, блокированной другим процессом.

Следующие макрокоманды определены для использования как значения для l_type элемента структуры flock. Значения — константы integer. F_RDLCK

Эта макрокоманда используется, чтобы установить блокировку чтения. F_WRLCK

Эта макрокоманда используется, чтобы установить блокировку записи. F_UNLCK

Эта макрокоманда используется, чтобы установить, что область разблокируется.

Наличие многократных копий программы, одновременно пишущих в файл может сделать содержимое файла бессмысленным. Но Вы можете предотвратить эту проблему, устанавливая блокировку записи на файле перед фактической записью в файл.

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

Не забудьте, что блокировки файла — только произвольный протокол для управления доступом к файлу. Все еще имеется возможность для доступа к файлу программами, которые не используют протокол блокировки.

Если Вы устанавливаете FASYNC флаг состояния на дескрипторе файла (см. Раздел 8.10 [Флаги Состояния Файла]), сигнал SIGIO посланы всякий раз, когда производится ввод или вывод, становится возможным на этом описателе файла. Процесс или группа процессов, может быть выбрана чтобы получить сигнал, используя команду F_SETOWN для fcntl функции. Если дескриптор файла является гнездом, он также, выбирает получателя сигналов SIGURG, которые возникают, когда внепоточные данные прибывает в это гнездо; см. Раздел 11.8.8 [Внепоточные Данные].

Если дескриптор файла соответствует терминалу, то SIGIO сигналы посылаются группе приоритетного процесса терминала. См. Главу 24 [Управление заданиями].

Символы в этом разделе определены в заглавном файле ‘fcntl.h’. -Макрос int F_GETOWN

Эта макрокоманда используется как аргумент команды fcntl, для определения того, что она должна получить информацию относительно процесса или группы процессов, которой посланы сигналы SIGIO. (Для терминала, это — фактически приоритетный ID группы процессов, которую Вы можете получать использованием tcgetpgrp; см. Раздел 24.7.3 [Функции Доступа Терминала].)

Возвращаемое значение интерпретируется как ID процесса; если оно отрицательно, то абсолютное значение — ID группы процессов.

Следующее errno условие ошибки определено для этой команды: EBADF

Filedes аргумент недопустим. int F_SETOWN (макрос)

Возвращаемое значение из fcntl с этой командой — -1 в случае ошибки и некоторое другого значения если обращение успешно. Следующие errno условия ошибки определены для этой команды: EBADF

Filedes аргумент недопустим. ESRCH

Не имеется никакого процесса или группы процессов, соответствующей pid.

Установка произвольной позиции в файле

Читайте также:

  1. АНТИОНТОЛОГИЧЕСКАЯ УСТАНОВКА ПОЗИТИВИСТОВ И НЕОПОЗИТИВИСТОВ
  2. В насосных установках
  3. Восковые композиции (смеси) и их характеристика
  4. Газобалонна установка для роботи на зрідженому газі
  5. Для управления технологическими установками, линиями и участками ГАП с­пользуются микро и мини-ЭВМ, реализующие третий уровень управления.
  6. Зависимость характера композиции от заполнения оптического центра
  7. Зрительные трубы и их установка
  8. Игры двух лиц с произвольной суммой.
  9. Какие принципы необходимо использовать при создании грамотной композиции кадра?
  10. Колективні та індивідуальні засоби захисту в електроустановках.
  11. Лекция 14. Установка, аттитюд, доминанта
  12. Листинг 3. Исходный текст программы OUT2.CPP в файле List7-3.CPP

Следующей по значимости для работы с файлами после уже рассмотренных системных функций является функция позиционирования в файле. Эта функция обозначается в MS Windows именем SetFilePointer и именем lseek в операционной системе Unix. Функция позиционирования позволяет организовать прямой доступ к данным в файле путем указания места в файле, с которого будет выполняться следующее чтение или запись.

Функция позиционирования SetFilePointer для MS Windows на языке Си имеет прототип

DWORD WINAPI SetFilePointer(HANDLE hFile, LONG ib,

LONG* pDistanceToMoveHigh, DWORD metod),

где hFile – хэндл позиционируемого файла, ib – число байтов, на которое будет перемещена текущая позиция в файле относительно точки отсчета (младшие 32 бита), metod – метод отсчета смещения в файле, pDistanceToMoveHigh – адрес старшего слова размещения количества байт, на которые требуется переместить текущую позицию в файле (при использовании файлов меньше 4 Гбайт этот параметр не используется – задается как NULL). Результирующая позиция после перемещения выдается функцией в качестве значения типа DWORD. При ошибке функция возвращает значение -1. Метод отсчета задается символическими константами FILE_BEGIN, FILE_CURRENT, FILE_END, которые определены в заголовочном файле.

Следующий пример, представленный листингом 2.5.1, демонстрирует использование позиционирования файла в MS Windows.

HANDLE hstdout, fhandle;

char fname[ ]=»myresult.txt»;

if (hstdout = = INVALID_HANDLE_VALUE) return;

fhandle=CreateFile(fname, GENERIC_READ, FILE_SHARE_READ, 0,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

if (fhandle= = INVALID_HANDLE_VALUE) return;

rc=SetFilePointer(fhandle, 10, 0, FILE_BEGIN);

if (rc= =NULL) return;

rc=ReadFile(fhandle, buffer+len, 80, &cb, NULL);

WriteFile(hstdout, buffer, cb, &cbw1, NULL);

Листинг 2.5.1. Программа для Windows

В начале программа получает от ОС хэндл стандартного вывода вызовом функции GetStdHandle. Затем выполняется открытие файла с доступом по чтению и нормальными атрибутами, причем в режиме доступности по чтению другим процессам. Действия по открытию задаются флагом OPEN_EXISTING, т.е. файл, указанный именем myresult.txt, должен уже существовать, иначе возникает ошибка, обнаруживаемая следующими командами. Предполагается использовать эту программу после выполнения программы, описанной листингом 2.3.1, которая и должна была создать файл с именем myresult.txt. Затем указатель текущей записи в файле устанавливается на 10 байт от начала файла, что выполняется вызовом функции SetFilePointer. Далее выполняется чтение из этого файла – с той позиции, которая была только что установлена, и прочитанный текст выводится на стандартный вывод функцией WriteFile. Файл закрывается, а программа завершается.

В операционной системе Unix для установки текущей позиции в файле служит функция с прототипом

off_t lseek (int handle, off_t pos, int whence),

где тип данных off_t совпадает в простейшем применении Unix с типом int (в общем случае тип данных off_t определяется в заголовочном файле sys/types.h). В качестве значения параметра whence следует использовать символические константы SEEK_CUR, SEEK_SET, SEEK_END, определяющие соответственно отсчет изменения позиции от текущего положения, от начала файла и от его конца. Второй параметр – pos определяет относительное смещение позиции в файле от указанной параметром whence точки отсчета. Результирующая позиция файла выдается как значение функции lseek. В случае неудачи функция возвращает значение -1.

Для использования больших файлов в современных версиях операционных разновидностей Unix предназначена модификация базовой функции позиционирования, имеющая прототип

__off64_t lseek64 (int handle, __off64_t pos, int whence).

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

Следующий пример, представленный листингом 2.5.2, демонстрирует использование позиционирования файла в Unix и по своим действиям аналогичен программе из листинга 2.5.1.

_lseek, _lseeki64 _lseek, _lseeki64

Перемещает указатель файла в заданное местоположение. Moves a file pointer to the specified location.

Синтаксис Syntax

Параметры Parameters

fd fd
Дескриптор файла, ссылающийся на открытый файл. File descriptor referring to an open file.

offset offset
Количество байт, начиная с origin. Number of bytes from origin.

origin origin
Первоначальная позиция. Initial position.

Возвращаемое значение Return Value

_lseek возвращает смещение (в байтах) новой позиции от начала файла. _lseek returns the offset, in bytes, of the new position from the beginning of the file. _lseeki64 возвращает смещение в 64-разрядном целом число. _lseeki64 returns the offset in a 64-bit integer. Функция возвращает-1L для указания ошибки. The function returns -1L to indicate an error. Если передан недопустимый параметр, например неверный дескриптор файла, параметр origin имеет недопустимое значение или позиция, заданная параметром offset, располагается до начала файла, вызывается обработчик недопустимого параметра, как описывается в разделе Проверка параметров. If passed an invalid parameter, such as a bad file descriptor, or the value for origin is invalid or the position specified by offset is before the beginning of the file, the invalid parameter handler is invoked, as described in Parameter Validation. Если выполнение может быть продолжено, эти функции устанавливают значение значение EBADF и возвращают-1L. If execution is allowed to continue, these functions set errno to EBADF and return -1L. Если устройство неспособно выполнять поиск (например, терминалы и принтеры), возвращаемое значение не определено. On devices incapable of seeking (such as terminals and printers), the return value is undefined.

Дополнительные сведения об этих и других кодах ошибок см. в разделе _doserrno, errno, _sys_errlist, and _sys_nerr. For more information about these and other error codes, see _doserrno, errno, _sys_errlist, and _sys_nerr.

Примечания Remarks

Функция _lseek перемещает указатель на файл, связанный с демоном , в новое расположение, которое является смещением байтов от источника. The _lseek function moves the file pointer associated with fd to a new location that is offset bytes from origin. Следующая операция в файле выполняется в новом местоположении. The next operation on the file occurs at the new location. Аргумент origin должен быть одной из следующих констант, определенных в Stdio.h. The origin argument must be one of the following constants, which are defined in Stdio.h.

значение происхождения origin value
SEEK_SET SEEK_SET Начало файла. Beginning of the file.
SEEK_CUR SEEK_CUR Текущая позиция указателя файла. Current position of the file pointer.
SEEK_END SEEK_END Конец файла. End of file.

_Lseek можно использовать для перемещения указателя в любом месте файла или после конца файла. You can use _lseek to reposition the pointer anywhere in a file or beyond the end of the file.


Требования Requirements

Подпрограмма Routine Обязательный заголовок Required header
_lseek _lseek
_lseeki64 _lseeki64

Дополнительные сведения о совместимости см. в разделе Совместимость. For more compatibility information, see Compatibility.

Способ перезаписать несколько позиций в файле

Я правильно читаю значения с некоторых позиций в моем файле, но у меня возникает проблема, когда мне нужно переписать эти значения. Все это портится в файле, после чего мой файл-ридер не находит то, что ему нужно, в нужных местах. Есть ли способ перезаписать всю строку и избежать этих ситуаций?
Я использую функцию lseek () для перемещения файлового указателя в нужную мне позицию.

Решение

Поскольку вы говорите «строка», я предполагаю, что вы говорите о текстовых файлах.

В этом случае: нет, нет способа перезаписать одну строку, Кроме если новая строка, которую вы хотите написать именно так одинаковой длины

Это потому, что абстракция файла на диске не работает, как редактирование файла, например, в. текстовый редактор, вы не можете сделать вставку / удаление в середине, не переписав все завершающие данные после точки изменения.

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

Другие решения

  1. прочитайте хвост после строки, чтобы изменить, и запишите это вместе с изменением, или
  2. прибегайте к файлам, отображенным в память: вы делаете изменения так же, как и в случае с C-строками, а остальная часть заботится об ОС.

Я бы предпочел файл с отображенной памятью. Когда переносимость является проблемой, у boost есть переносимая абстракция для них. Я хотел бы рассмотреть это, прежде чем прибегнуть к варианту 1.

Lseek изменить позицию в файле

[Nikolay N. Ivanov (nn at lindevel dot ru)]

Глава 5. НИЗКОУРОВНЕВЫЙ ВВОД-ВЫВОД

5.1. Обзор механизмов ввода-вывода в Linux

В языке C для осуществления файлового ввода-вывода используются механизмы стандартной библиотеки языка, объявленные в заголовочном файле stdio.h. Как вы вскоре узнаете консольный ввод-вывод — это не более чем частный случай файлового ввода-вывода. В C++ для ввода-вывода чаще всего используются потоковые типы данных. Однако все эти механизмы являются всего лишь надстройками над низкоуровневыми механизмами ввода-вывода ядра операционной системы.

С точки зрения модели КИС (Клиент-Интерфейс-Сервер), сервером стандартных механизмов ввода вывода языка C (printf, scanf, FILE*, fprintf, fputc и т. д.) является библиотека языка. А сервером низкоуровневого ввода-вывода в Linux, которому посвящена эта глава книги, является само ядро операционной системы.

Пользовательские программы взаимодействуют с ядром операционной системы посредством специальных механизмов, называемых системными вызовами (system calls, syscalls). Внешне системные вызовы реализованы в виде обычных функций языка C, однако каждый раз вызывая такую функцию, мы обращаемся непосредственно к ядру операционной системы. Список всех системных вызовов Linux можно найти в файле /usr/include/asm/unistd.h. В этой главе мы рассмотрим основные системные вызовы, осуществляющие ввод-вывод: open(), close(), read(), write(), lseek() и некоторые другие.

5.2. Файловые дескрипторы

В языке C при осуществлении ввода-вывода мы используем указатель FILE*. Даже функция printf() в итоге сводится к вызову vfprintf(stdout. ), разновидности функции fprintf(); константа stdout имеет тип struct _IO_FILE*, синонимом которого является тип FILE*. Это я к тому, что консольный ввод-вывод — это файловый ввод-вывод. Стандартный поток ввода, стандартный поток вывода и поток ошибок (как в C, так и в C++) — это файлы. В Linux все, куда можно что-то записать или откуда можно что-то прочитать представлено (или может быть представлено) в виде файла. Экран, клавиатура, аппаратные и виртуальные устройства, каналы, сокеты — все это файлы. Это очень удобно, поскольку ко всему можно применять одни и те же механизмы ввода-вывода, с которыми мы и познакомимся в этой главе. Владение механизмами низкоуровневого ввода-вывода дает свободу перемещения данных в Linux. Работа с локальными файловыми системами, межсетевое взаимодействие, работа с аппаратными устройствами, — все это осуществляется в Linux посредством низкоуровневого ввода-вывода.

Вы уже знаете из предыдущей главы, что при запуске программы в системе создается новый процесс (здесь есть свои особенности, о которых пока говорить не будем). У каждого процесса (кроме init) есть свой родительский процесс (parent process или просто parent), для которого новоиспеченный процесс является дочерним (child process, child). Каждый процесс получает копию окружения (environment) родительского процесса. Оказывается, кроме окружения дочерний процесс получает в качестве багажа еще и копию таблицы файловых дескрипторов.

Файловый дескриптор (file descriptor) — это целое число (int), соответствующее открытому файлу. Дескриптор, соответствующий реально открытому файлу всегда больше или равен нулю. Копия таблицы дескрипторов (читай: таблицы открытых файлов внутри процесса) скрыта в ядре. Мы не можем получить прямой доступ к этой таблице, как при работе с окружением через environ. Можно, конечно, кое-что «вытянуть» через дерево /proc, но нам это не надо. Программист должен лишь понимать, что каждый процесс имеет свою копию таблицы дескрипторов. В пределах одного процесса все дескрипторы уникальны (даже если они соответствуют одному и тому же файлу или устройству). В разных процессах дескрипторы могут совпадать или не совпадать — это не имеет никакого значения, поскольку у каждого процесса свой собственный набор открытых файлов.

Возникает вопрос: сколько файлов может открыть процесс? В каждой системе есть свой лимит, зависящий от конфигурации. Если вы используете bash или ksh (Korn Shell), то можете воспользоваться внутренней командой оболочки ulimit, чтобы узнать это значение. Если вы работаете с оболочкой C-shell (csh, tcsh), то в вашем распоряжении команда limit:

В командной оболочке, в которой вы работаете (bash, например), открыты три файла: стандартный ввод (дескриптор 0), стандартный вывод (дескриптор 1) и стандартный поток ошибок (дескриптор 2). Когда под оболочкой запускается программа, в системе создается новый процесс, который является для этой оболочки дочерним процессом, следовательно, получает копию таблицы дескрипторов своего родителя (то есть все открытые файлы родительского процесса). Таким образом программа может осуществлять консольный ввод-вывод через эти дескрипторы. На протяжении всей книги мы будем часто играть с этими дескрипторами.

Таблица дескрипторов, помимо всего прочего, содержит информацию о текущей позиции чтения-записи для каждого дескриптора. При открытии файла, позиция чтения-записи устанавливается в ноль. Каждый прочитанный или записанный байт увеличивает на единицу указатель текущей позиции. Мы вернемся к этой теме в разделе 5.7.

5.3. Открытие файла: системный вызов open()

Чтобы получить возможность прочитать что-то из файла или записать что-то в файл, его нужно открыть. Это делает системный вызов open(). Этот системный вызов не имеет постоянного списка аргументов (за счет использования механизма va_arg); в связи с этим существуют две «разновидности» open(). Не только в С++ есть перегрузка функций ;-) Если интересно, то о механизме va_arg можно прочитать на man-странице stdarg (man 3 stdarg) или в книге Б. Кернигана и Д. Ритчи «Язык программирования Си». Ниже приведены адаптированные прототипы системного вызова open().

Системный вызов open() объявлен в заголовочном файле fcntl.h. Ниже приведен общий адаптированный прототип open().

Начнем по порядку. Первый аргумент — имя файла в файловой системе в обычной форме: полный путь к файлу (если файл не находится в текущем каталоге) или сокращенное имя (если файл в текущем каталоге).

Второй аргумент — это режим открытия файла, представляющий собой один или несколько флагов открытия, объединенных оператором побитового ИЛИ. Список доступных флагов приведен в Таблице 4 Приложения 2.. Наиболее часто используют только первые семь флагов. Если вы хотите, например, открыть файл в режиме чтения и записи, и при этом автоматически создать файл, если такового не существует, то второй аргумент open() будет выглядеть примерно так: O_RDWR|O_CREAT. Константы-флаги открытия объявлены в заголовочном файле bits/fcntl.h, однако не стоит включать этот файл в свои программы, поскольку он уже включен в файл fcntl.h.

Третий аргумент используется в том случае, если open() создает новый файл. В этом случае файлу нужно задать права доступа (режим), с которыми он появится в файловой системе. Права доступа задаются перечислением флагов, объединенных побитовым ИЛИ. Вместо флагов можно использовать число (как правило восьмиричное), однако первый способ нагляднее и предпочтительнее. Список флагов приведен в Таблице 1 Приложения 2. Чтобы, например, созданный файл был доступен в режиме «чтение-запись» пользователем и группой и «только чтение» остальными пользователями, — в третьем аргументе open() надо указать примерно следующее: S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH или 0664. Флаги режима доступа реально объявлены в заголовочном файле bits/stat.h, но он не предназначен для включения в пользовательские программы, и вместо него мы должны включать файл sys/stat.h. Тип mode_t объявлен в заголовочном файле sys/types.h.

Если файл был успешно открыт, open() возвращает файловый дескриптор, по которому мы будем обращаться к файлу. Если произошла ошибка, то open() возвращает -1. Позже, в последующих главах книги мы научимся распознавать ошибки системных вызовов.

5.4. Закрытие файла: системный вызов close()

Системный вызов close() закрывает файл. Вообще говоря, по завершении процесса все открытые файлы (кроме файлов с дескрипторами 0, 1 и 2) автоматически закрываются. Тем не менее, это не освобождает нас от самостоятельного вызова close(), когда файл нужно закрыть. К тому же, если файлы не закрывать самостоятельно, то соответствующие дескрипторы не освобождаются, что может привести к превышению лимита открытых файлов. Простой пример: приложение может быть настроено так, чтобы каждую минуту открывать и перечитывать свой файл конфигурации для проверки обновлений. Если каждый раз файл не будет закрываться, то в моей системе, например, приложение может «накрыться медным тазом» примерно через 17 часов. Автоматически! Кроме того, файловая система Linux поддерживает механизм буферизации. Это означает, что данные, которые якобы записываются, реально записываются на носитель (синхронизируются) только через какое-то время, когда система сочтет это правильным и оптимальным. Это повышает производительность системы и даже продлевает ресурс жестких дисков. Системный вызов close() не форсирует запись данных на диск, однако дает больше гарантий того, что данные останутся в целости и сохранности.

Системный вызов close() объявлен в файле unistd.h. Ниже приведен его адаптированный прототип.

Очевидно, что единственный аргумент — это файловый дескриптор. Возвращаемое значение — ноль в случае успеха, и -1 — в случае ошибки. Довольно часто close() вызывают без проверки возвращаемого значения. Это не очень грубая ошибка, но, тем не менее, иногда закрытие файла бывает неудачным (в случае неправильного дескриптора, в случае прерывания функции по сигналу или в случае ошибки ввода-вывода, например). В любом случае, если программа сообщит пользователю, что файл невозможно закрыть, это хорошо.

Теперь можно написать простенкую программу, использующую системные вызовы open() и close(). Мы еще не умеем читать из файлов и писать в файлы, поэтому напишем программу, которая создает файл с именем, переданным в качестве аргумента (argv[1]) и с правами доступа 0600 (чтение и запись для пользователя). Ниже приведен исходный код программы.

Обратите внимание, если запустить программу дважды с одним и тем же аргументом, то на второй раз open() выдаст ошибку. В этом виноват флаг O_EXCL (см. Таблицу 4 Приложения 2), который «дает добро» только на создание еще не существующих файлов. Наглядности ради, флаги открытия и флаги режима мы занесли в отдельные переменные, однако можно было бы сделать так: Или так:

5.5. Чтение файла: системный вызов read()

Системный вызов read(), объявленный в файле unistd.h, позволяет читать данные из файла. В отличие от библиотечных функций файлового ввода-вывода, которые предоставляют возможность интерпретации считываемых данных. Можно, например, записать в файл следующее содержимое:

Теперь, используя библиотечные механизмы, можно читать файл по-разному:

Системный вызов read() читает данные в «сыром» виде, то есть как последовательность байт, без какой-либо интерпретации. Ниже представлен адаптированный прототип read().

Первый аргумент — это файловый дескриптор. Здесь больше сказать нечего. Второй аргумент — это указатель на область памяти, куда будут помещаться данные. Третий аргумент — количество байт, которые функция read() будет пытаться прочитать из файла. Возвращаемое значение — количество прочитанных байт, если чтение состоялось и -1, если произошла ошибка. Хочу заметить, что если read() возвращает значение меньше count, то это не символизирует об ошибке.

Хочу сказать несколько слов о типах. Тип size_t в Linux используется для хранения размеров блоков памяти. Какой тип реально скрывается за size_t, зависит от архитектуры; как правило это unsigned long int или unsigned int. Тип ssize_t (Signed SIZE Type) — это тот же size_t, только знаковый. Используется, например, в тех случаях, когда нужно сообщить об ошибке, вернув отрицательный размер блока памяти. Системный вызов read() именно так и поступает.

Теперь напишем программу, которая просто читает файл и выводит его содержимое на экран. Имя файла будет передаваться в качестве аргумента (argv[1]). Ниже приведен исходный код этой программы.

В этом примере используется укороченная версия open(), так как файл открывается только для чтения. В качестве буфера (второй аргумент read()) мы передаем адрес переменной типа char. По этому адресу будут считываться данные из файла (по одному байту за раз) и передаваться на стандартный вывод. Цикл чтения файла заканчивается, когда read() возвращает нуль (нечего больше читать) или -1 (ошибка). Системный вызов close() закрывает файл.

Как можно заметить, в нашем примере системный вызов read() вызывается ровно столько раз, сколько байт содержится в файле. Иногда это действительно нужно; но не здесь. Чтение-запись посимвольным методом (как в нашем примере) значительно замедляет процесс ввода-вывода за счет многократных обращений к системным вызовам. По этой же причине возрастает вероятность возникновения ошибки. Если нет действительной необходимости, файлы нужно читать блоками. О том, какой размер блока предпочтительнее, будет рассказано в последующих главах книги. Ниже приведен исходный код программы, которая делает то же самое, что и предыдущий пример, но с использованием блочного чтения файла. Размер блока установлен в 64 байта.

Теперь можно примерно оценить и сравнить скорость работы двух примеров. Для этого надо выбрать в системе достаточно большой файл (бинарник ядра или видеофильм, например) и посмотреть на то, как быстро читаются эти файлы:

5.6. Запись в файл: системный вызов write()

Для записи данных в файл используется системный вызов write(). Ниже представлен его прототип.

Как видите, прототип write() отличается от read() только спецификатором const во втором аргументе. В принципе write() выполняет процедуру, обратную read(): записывает count байтов из буфера buffer в файл с дескриптором fd, возвращая количество записанных байтов или -1 в случае ошибки. Так просто, что можно сразу переходить к примеру. За основу возьмем программу myread1 из предыдущего раздела.

В этом примере нам уже не надо изощеряться в попытках вставить нуль-терминатор в строку для записи, поскольку системный вызов write() не запишет большее количество байт, чем мы ему указали. В данном случае для демонстрации write() мы просто записывали данные в файл с дескриптором 1, то есть в стандартный вывод. Но прежде, чем переходить к чтению следующего раздела, попробуйте самостоятельно записать что-нибудь (при помощи write(), естественно) в обычный файл. Когда будете открывать файл для записи, обратите пожалуйста внимание на флаги O_TRUNC, O_CREAT и O_APPEND. Подумайте, все ли флаги сочетаются между собой по смыслу.

5.7. Произвольный доступ: системный вызов lseek()

Как уже говорилось, с каждым открытым файлом связано число, указывающее на текущую позицию чтения-записи. При открытии файла позиция равна нулю. Каждый вызов read() или write() увеличивает текущую позицию на значение, равное числу прочитанных или записанных байт. Благодаря этому механизму, каждый повторный вызов read() читает следующие данные, и каждый повторный write() записывает данные в продолжение предыдущих, а не затирает старые. Такой механизм последовательного доступа очень удобен, однако иногда требуется получить произвольный доступ к содержимому файла, чтобы, например, прочитать или записать файл заново.

Для изменения текущей позиции чтения-записи используется системный вызов lseek(). Ниже представлен его прототип.

Первый аргумент, как всегда, — файловый дескриптор. Второй аргумент — смещение, как положительное (вперед), так и отрицательное (назад). Третий аргумент обычно передается в виде одной из трех констант SEEK_SET, SEEK_CUR и SEEK_END, которые показывают, от какого места отсчитывается смещение. SEEK_SET — означает начало файла, SEEK_CUR — текущая позиция, SEEK_END — конец файла. Рассмотрим следующие вызовы:

Первый вызов устанавливает текущую позицию в начало файла. Второй вызов смещает позицию вперед на 20 байт. В третьем случае текущая позиция перемещается на 10 байт назад относительно конца файла.

В случае удачного завершения, lseek() возвращает значение установленной «новой» позиции относительно начала файла. В случае ошибки возвращается -1.

Я долго думал, какой бы пример придумать, чтобы продемонстрировать работу lseek() наглядным образом. Наиболее подходящим примером мне показалась идея создания программы рисования символами. Программа оказалась не слишком простой, однако если вы сможете разобраться в ней, то можете считать, что успешно овладели азами низкоуровневого ввода-вывода Linux. Ниже представлен исходный код этой программы.

Теперь разберемся, как работает эта программа. Изначально «полотно» заполняется пробелами. Функция init_draw() построчно записывает в файл пробелы, чтобы получился «холст», размером N_ROWS на N_COLS. Массив строк icode в функции main() — это набор команд рисования. Команда начинается с одной из трех литер: ‘v’ — нарисовать вертикальную линию, ‘h’ — нарисовать горизонтальную линию, ‘p’ — нарисовать точку. После каждой такой литеры следуют три числа. В случае вертикальной линии первое число — фиксированная координата X, а два других числа — это начальная и конечная координаты Y. В случае горизонтальной линии фиксируется координата Y (первое число). Два остальных числа — начальная координата X и конечная координата X. При рисовании точки используются только два первых числа: координата X и координата Y. Итак, функция draw_vline() рисует вертикальную линию, функция draw_hline() рисует горизонтальную линию, а draw_point() рисует точку.

Функция init_draw() пишет в файл N_ROWS строк, каждая из которых содержит N_COLS пробелов, заканчивающихся переводом строки. Это процедура подготовки «холста».

Функция draw_point() вычисляет позицию (исходя из значений координат), перемещает туда текущую позицию ввода-вывода файла, и записывает в эту позицию символ (FG_CHAR), которым мы рисуем «картину».

Функция draw_hline() заполняет часть строки символами FG_CHAR. Так получается горизонтальная линия. Функция draw_vline() работает иначе. Чтобы записать вертикальную линию, нужно записывать по одному символу и каждый раз «перескакивать» на следующую строку. Эта функция работает медленнее, чем draw_hline(), но иначе мы не можем.

Полученное изображение записывается в файл image. Будьте внимательны: чтобы разгрузить исходный код, из программы исключены многие проверки (read(), write(), close(), диапазон координат и проч.). Попробуйте включить эти проверки самостоятельно.

Nikolay N. Ivanov (nn at lindevel dot ru) — Программирование в Linux с нуля — Глава 5. НИЗКОУРОВНЕВЫЙ ВВОД-ВЫВОД Версия для печати

Илон Маск рекомендует:  border-top-color в CSS
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL