6.9. Блокировка доступа к файлам.
процессов.
- Допустим, что процесс A изменяет некоторую область файла, в то время как процесс
B пытается прочесть ту же область. Итогом такого соревнования может быть то,
что процесс B прочтет неверные данные.
- Допустим, что процесс A изменяет некоторую область файла, в то время как процесс
C также изменяет ту же самую область. В итоге эта область может содержать
неверные данные (часть - от процесса A, часть - от C).
Ясно, что требуется механизм синхронизации процессов, позволяющий не пускать
другой процесс (процессы) читать и/или записывать данные в указанной области. Меха-
низмов синхронизации в UNIX существует множество: от семафоров до блокировок областей
файла. О последних мы и будем тут говорить.
Прежде всего отметим, что блокировки файла носят в UNIX необязательный характер.
То есть, программа не использующая вызовов синхронизации, будет иметь доступ к данным
без каких либо ограничений. Увы. Таким образом, программы, собирающиеся корректно
пользоваться общими данными, должны все использовать - и при том один и тот же -
механизм синхронизации: заключить между собой "джентльменское соглашение".
6.9.1. Блокировка устанавливается при помощи вызова
flock_t lock;
fcntl(fd, operation, &lock);
Здесь operation может быть одним из трех:
F_SETLK
Устанавливает или снимает замок, описываемый структурой lock. Структура flock_t
имеет такие поля:
short l_type;
short l_whence;
off_t l_start;
size_t l_len;
long l_sysid;
pid_t l_pid;
l_type
тип блокировки:
F_RDLCK - на чтение;
F_WRLCK - на запись;
F_UNLCK - снять все замки.
l_whence, l_start, l_len
описывают сегмент файла, на который ставится замок: от точки
lseek(fd,l_start,l_whence); длиной l_len байт. Здесь l_whence может быть:
SEEK_SET, SEEK_CUR, SEEK_END. l_len равное нулю означает "до конца файла". Так
если все три параметра равны 0, то будет заблокирован весь файл.
F_SETLKW
Устанавливает или снимает замок, описываемый структурой lock. При этом, если
замок на область, пересекающуюся с указанной уже кем-то установлен, то сперва
дождаться снятия этого замка.
читать ждать;запереть;читать
WRITE | записать ждать;запереть;записать ждать;запереть;записать
UNLOCK | отпереть отпереть отпереть
и разблокировки.
- Если кто-то читает сегмент, а другой процесс собрался изменить (записать) этот
сегмент, то этот другой процесс обязан дождаться окончания чтения первым.
- В момент, обозначенный как отпереть - будятся процессы, ждущие разблокировки, и
ровно один из них получает доступ (может установить свою блокировку). Порядок -
кто из них будет первым - вообще говоря не определен.
F_GETLK
Запрашиваем возможность установить замок, описанный в lock.
- Если мы можем установить такой замок (не заперто никем), то в структуре lock
поле l_type становится равным F_UNLCK и поле l_whence равным SEEK_SET.
- Если замок уже кем-то установлен (и вызов F_SETLKW заблокировал бы наш процесс,
привел бы к ожиданию), мы получаем информацию о чужом замке в структуру lock.
При этом в поле l_pid заносится идентификатор процесса, создавшего этот замок, а
в поле l_sysid - идентификатор машины (поскольку блокировка файлов поддержива-
ется через сетевые файловые системы).
Замки автоматически снимаются при закрытии дескриптора файла. Замки не наследу-
ются порожденным процессом при вызове fork.
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
char DataFile [] = "data.xxx";
char info [] = "abcdefghijklmnopqrstuvwxyz";
#define OFFSET 5
#define SIZE 12
#define PAUSE 2
int trial = 1;
int fd, pid;
char buffer[120], myname[20];
void writeAccess(), readAccess();
void fcleanup(int nsig){
unlink(DataFile);
printf("cleanup:%s\n", myname);
if(nsig) exit(0);
}
int main(){
int i;
fd = creat(DataFile, 0644);
write(fd, info, strlen(info));
close(fd);
signal(SIGINT, fcleanup);
sprintf(myname, fork() ? "B-%06d" : "A-%06d", pid = getpid());
srand(time(NULL)+pid);
printf("%s:started\n", myname);
fd = open(DataFile, O_RDWR|O_EXCL);
printf("%s:opened %s\n", myname, DataFile);
for(i=0; i < 30; i++){
if(rand()%2) readAccess();
else writeAccess();
}
close(fd);
printf("%s:finished\n", myname);
wait(NULL);
fcleanup(0);
return 0;
}
void writeAccess(){
flock_t lock;
printf("Write:%s #%d\n", myname, trial);
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = (off_t) OFFSET;
lock.l_len = (size_t) SIZE;
if(fcntl(fd, F_SETLKW, &lock) <0)
perror("F_SETLKW");
printf("\twrite:%s locked\n", myname);
sprintf(buffer, "%s #%02d", myname, trial);
printf ("\twrite:%s \"%s\"\n", myname, buffer);
lseek (fd, (off_t) OFFSET, SEEK_SET);
write (fd, buffer, SIZE);
sleep (PAUSE);
lock.l_type = F_UNLCK;
if(fcntl(fd, F_SETLKW, &lock) <0)
perror("F_SETLKW");
printf("\twrite:%s unlocked\n", myname);
trial++;
}
void readAccess(){
flock_t lock;
printf("Read:%s #%d\n", myname, trial);
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = (off_t) OFFSET;
lock.l_len = (size_t) SIZE;
if(fcntl(fd, F_SETLKW, &lock) <0)
perror("F_SETLKW");
printf("\tread:%s locked\n", myname);
lseek(fd, (off_t) OFFSET, SEEK_SET);
read (fd, buffer, SIZE);
printf("\tcontents:%s \"%*.*s\"\n", myname, SIZE, SIZE, buffer);
sleep (PAUSE);
lock.l_type = F_UNLCK;
if(fcntl(fd, F_SETLKW, &lock) <0)
perror("F_SETLKW");
printf("\tread:%s unlocked\n", myname);
процессом A - то запись процессом B дождется разблокировки A (чтение
- не будет дожидаться). Если идет запись процессом A - то и чтение процессом B и
запись процессом B дождутся разблокировки A.
6.9.2.
UNIX SVR4 имеет еще один интерфейс для блокировки файлов: функцию lockf.
#include <unistd.h>
int lockf(int fd, int operation, size_t size);
Операция operation:
F_ULOCK
Разблокировать указанный сегмент файла (это может снимать один или несколько
замков).
F_LOCK
F_TLOCK
Установить замок. При этом, если уже имеется чужой замок на запрашиваемую
область, F_LOCK блокирует процесс, F_TLOCK - просто выдает ошибку (функция возв-
ращает -1, errno устанавливается в EAGAIN).
- Ожидание отпирания/запирания замка может быть прервано сигналом.
- Замок устанавливается следующим образом: от текущей позиции указателя чтения-
записи в файле fd (что не похоже на fcntl, где позиция задается явно как пара-
метр в структуре); длиной size. Отрицательное значение size файла. Если файл изменит размер, запертая область все равно
будет простираться до конца файла (уже нового).
- Замки, установленные процессом, автоматически отпираются при завершении про-
цесса.
F_TEST
было: ___________#######____######__________
запрошено:______________##########______________
стало: ___________#################__________
Если снимается замок с области, покрывающей только часть заблокированной прежде,
остаток области остается как отдельный замок.
было: ___________#################__________
запрошено:______________XXXXXXXXXX______________
стало: ___________###__________####__________
[Назад] [Содержание] [Вперед]
| Главная |