C++ CSS HTML Java JavaScript MySQL Oracle PERL PHP SQL Unix VBScript XHTML XML Сети
6.9. Блокировка доступа к файлам.
 
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______________

	стало:	___________###__________####__________

[Назад] [Содержание] [Вперед]

Главная