|
В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13 |
Файловый ввод/вывод
Блокировка сегмента описывается структурой flock, которая, согласно стандарту, должна содержать по крайней мере следующие поля:
short l_type /* Тип блокировки: */ /* F_RDLCK (на чтение) */ /* F_WRLCK (на запись), */ /* F_UNLCK (снятие блокировки) */ short l_whence/* Точка отсчета для смещения */ /* l_start: SEEK_SET, SEEK_CUR */ /* или SEEK_END */ off_t l_start /* Смещение в байтах начала */ /* блокируемого сегмента */ /* относительно точки отсчета */ off_t l_len /* Размер блокируемого сегмента. */ /* 0 означает блокировку до конца файла. */ pid_t l_pid /* Идентификатор процесса,*/ /* установившего блокировку; */ /* (возвращается по команде F_GETLK, */ /* см. далее) */
Отметим, что блокируемый сегмент может выходить за конец, но не за начало файла. Длину сегмента разрешается задавать отрицательным числом, если тип off_t допускает такую возможность; в таком случае смещение начала блокируемого сегмента относительно точки отсчета равно l_start+l_len, а смещение конца – l_start-1.
Приведем несколько примеров заполнения структуры flock (см. листинг 5.25).
#include <fcntl.h>
struct flock lck;
. . .
lck.l_type = F_RDLCK;
/* Блокировка на чтение */
/* всего файла */
lck.l_whence = SEEK_SET;
lck.l_start = 0;
lck.l_len = 0;
Листинг
5.25.
Примеры заполнения структуры flock.
Установка блокировки осуществляется управляющими командами F_SETLK и F_SETLKW функции fcntl() (см. листинг 5.26).
if (fcntl (fd, F_SETLK, &lck) != -1) ... if (fcntl (fd, F_SETLKW, &lck) != -1) ...Листинг 5.26. Примеры вызова функции 2 для установки блокировок.
Если блокировка не может быть установлена, выполнение команды F_SETLK немедленно завершается, и возвращается -1. Команда F_SETLKW отличается только тем, что в аналогичной ситуации процесс переходит в состояние ожидания до тех пор, пока нужный сегмент файла не будет разблокирован.
Для снятия блокировки можно воспользоваться командами F_SETLK или F_SETLKW. Для этого значение поля l_type должно быть установлено равным F_UNLCK.
Получить характеристики блокировки, мешающей установить новую, позволяет управляющая команда F_GETLK (новая блокировка задается структурой, на которую при обращении к fcntl() указывает третий аргумент arg ). Если запрашиваемую блокировку установить нельзя, информация о первой помещается в ту же структуру; в частности, будет задано значение поля l_pid – оно идентифицирует процесс, установивший блокировку. (Естественно, значение поля l_whence будет установлено равным SEEK_SET.) Если нет помех для создания нужной блокировки, полю l_type присваивается значение F_UNLCK, а остальные поля в структуре не изменяются.
При закрытии файлового дескриптора все блокировки, установленные в файле текущим процессом, снимаются.
Приведем пример двух программ, первая из которых (назовем ее set_locks ) устанавливает блокировки нескольких сегментов файла и засыпает на некоторое время (см. листинг 5.27), а вторая ( test_locks, см. листинг 5.28), выполняющаяся, разумеется, параллельно, в рамках другого процесса, получает и выводит информацию о заблокированных сегментах того же файла (см. листинг 5.29).
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <assert.h>
#define LOCKFILE "my_lockfile"
/* Программа устанавливает несколько блокировок */
/* на файл LOCKFILE */
int main (void) {
int fd;
struct flock lck;
assert ((fd = open (LOCKFILE, O_RDWR | O_CREAT |
O_TRUNC, S_IRWXU)) != -1);
/* Установим блокировку на запись на весь файл */
lck.l_type = F_WRLCK;
lck.l_whence = SEEK_SET;
lck.l_start = (off_t) 0;
lck.l_len = (off_t) 0;
if (fcntl (fd, F_SETLK, &lck) == -1) {
perror ("FCNTL-F_SETLK-1");
close (fd);
return (-1);
}
/* Сделаем размер файла ненулевым */
if (lseek (fd, (off_t) 1024, SEEK_SET) == -1) {
perror ("LSEEK");
close (fd);
return (-1);
}
if (write (fd, &lck, sizeof (lck)) != sizeof (lck)) {
perror ("WRITE");
close (fd);
return (-1);
}
/* Снимем блокировку в середине файла */
lck.l_type = F_UNLCK;
lck.l_whence = SEEK_SET;
lck.l_start = (off_t) 512;
lck.l_len = (off_t) sizeof (lck);
if (fcntl (fd, F_SETLK, &lck) == -1) {
perror ("FCNTL-F_SETLK-2");
close (fd);
return (-1);
}
/* Установим блокировку на чтение в конце файла */
lck.l_type = F_RDLCK;
lck.l_whence = SEEK_END;
lck.l_start = (off_t) -sizeof (lck);
lck.l_len = (off_t) sizeof (lck);
if (fcntl (fd, F_SETLK, &lck) == -1) {
perror ("FCNTL-F_SETLK-2");
close (fd);
return (-1);
}
sleep (10);
return (close (fd));
}
Листинг
5.27.
Пример программы set_locks, устанавливающей блокировки файла.
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#define LOCKFILE "my_lockfile"
/* Программа выявляет блокировки, установленные */
/* на файл LOCKFILE */
int main (void) {
int fd;
struct flock lck;
assert ((fd = open (LOCKFILE, O_WRONLY)) != -1);
(void) printf ("ид-р проц. тип начало длина\n");
/* Начнем с попытки установить блокировку на */
/* весь файл */
lck.l_whence = SEEK_SET;
lck.l_start = 0;
lck.l_len = 0;
do {
lck.l_type = F_WRLCK;
(void) fcntl (fd, F_GETLK, &lck);
if (lck.l_type != F_UNLCK) {
(void) printf ("%9d %3c %7ld %5ld\n",
lck.l_pid,
(lck.l_type == F_WRLCK) ? 'W' : 'R',
lck.l_start, lck.l_len);
/* Если эта блокировка покрывает остаток файла, */
/* нет нужды выявлять другие блокировки */
if (lck.l_len == 0) break;
/* Иначе поищем новую блокировку после найденной */
lck.l_start += lck.l_len;
}
while (lck.l_type != F_UNLCK);
return (close (fd));
}
Листинг
5.28.
Пример программы test_locks, выявляющей блокировки файла.
ид-р проц. тип начало длина 31174 W 0 512 31174 W 528 496 31174 R 1024 16 31174 W 1040 0Листинг 5.29. Возможный результат выполнения командной строки set_locks & test_locks.
Отметим, что блокировка на чтение расщепила блокировку на запись, первоначально покрывавшую весь файл.
Читателю предлагается выполнить командную строку set_locks & set_locks и объяснить полученный результат.
Функции setbuf(), setvbuf( ) и fflush() выполняют управляющие операции с буферами потоков (см. листинг 5.30).
#include <stdio.h> void setbuf (FILE *restrict stream, char *restrict buf); #include <stdio.h> int setvbuf (FILE *restrict stream, char *restrict buf, int type, size_t size); #include <stdio.h> int fflush (FILE *stream);Листинг 5.30. Описание функций setbuf(), setvbuf() и fflush().
Функция setvbuf(), которую можно использовать после открытия файла, но до первой операции ввода/вывода, устанавливает режим буферизации в соответствии со значением своего третьего аргумента, type:
_IOLBF – построчная буферизация;
_IONBF – отсутствие буферизации.
Функция setvbuf() сама резервирует буфера заданного размера, но если аргумент buf не является пустым указателем, может использоваться и пользовательский буфер. В случае нормального завершения функция возвращает 0.
Функцию setbuf() можно считать частным случаем setvbuf(). С точностью до возвращаемого значения вызов setbuf (stream, NULL) эквивалентен setvbuf (stream, NULL, _IONBF, BUFSIZ) (отмена буферизации); если же значение buf не равно NULL, вызов setbuf (stream, buf) сводится к setvbuf (stream, buf, _IOFBF, BUFSIZ) (полная буферизация).
Функцию setbuf() чаще всего применяют для отмены буферизации стандартного вывода и/или стандартного протокола, выполняя вызовы setbuf (stdout, NULL) и/или setbuf (stderr, NULL).
Использование функций setbuf() и setvbuf() требует известной аккуратности. Типичная ошибка – указание в качестве аргумента buf автоматического массива, определенного внутри блока, и продолжение работы с потоком после выхода из этого блока.
Функция fflush() выталкивает буфера, помещая в файл предназначенные для записи, но еще не записанные данные. Если в качестве аргумента stream задан пустой указатель, выталкиваются все буфера всех потоков.
Вызов fflush() – необходимый элемент завершения транзакций, но он полезен и применительно к стандартному выводу (протоколу), если нужно выдать приглашение для пользовательского ввода на экран, а не в буфер (см. листинг 5.31).
char name [LINE_MAX];
(void) printf ("Введите Ваше имя: ");
(void) fflush (stdout); (void) fgets (name,
sizeof (name), stdin);
Листинг
5.31.
Пример использования функции fflush().