Void status(struct flock *lock)
{
printf("Status:");
switch(lock->l_type)
{
case F_UNLCK:
printf("F_UNLCK (Блокування знято)\n");
break;
case F_RDLCK:
printf("F_RDLCK (pid: %d)(Блокування читання)\n", lock->l_pid);
break;
case F_WRLCK:
printf("F_WRLCK(pid:%d)(Блокування запису)\n", lock->l_pid);
break;
default: break;
}
}
Int main(int argc, char **argv)
{
struct flock lock;
int fd;
char buffer[100];
fd=open(FNAME, O_WRONLY|O_CREAT|O_APPEND, S_IRWXU);
memset(&lock, 0, sizeof(struct flock));
/*Перевірка, чи встановлено блокування*/
fcntl(fd, F_GETLK, &lock);
if(lock.l_type==F_WRLCK || lock.l_type==F_RDLCK)
{
status(&lock);
memset(&lock,0,sizeof(struct flock));
lock.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &lock) < 0)
printf("Помилка при:
fcntl(fd, F_SETLK, F_UNLCK)(%s)\n", strerror(errno));
else
printf("Успішно зняте блокування: fcntl(fd, F_SETLK, F_UNLCK)\n");
}
status(&lock);
write(STDOUT_FILENO,"\n Введені дані:", sizeof("\n Введені дані: "));
while((read(1,puffer,100))>1)
write(fd,buffer,100);
memset(&lock, 0, sizeof(struct flock));
lock.l_type = F_WRLCK;
lock.l_start=0;
lock.l_whence = SEEK_SET;
lock.l_len=0;
lock.l_pid = getpid();
if (fcntl(fd, F_SETLK, &lock) < 0)
printf("Помилка при:
fcntl(fd, F_SETLK, F_WRLCK)(%s)\n", strerror(errno));
else
printf("Успішно: fcntl(fd, F_SETLK, F_WRLCK)\n");
status(&lock);
switch(fork())
{
case-1: exit(0);
case 0: if(lock.l_type == F_WRLCK)
{
printf("Нащадок:Неможлива запис у файл(F_WRLCK)\n");
exit(0);
}
else
printf("Нащадок готовий до запису у файл\n");
exit(0);
default: wait(NULL);break;
}
close(fd);
return 0;
}
Ця програма ілюструє можливість сериализации процесів. Проте тут все одно можливі читання і запис у процесі -нащадку. Такий варіант блокування називається advisory locking(необов'язкове блокування). При його установці не проводиться ніяких додаткових перевірок того, чи повинні системні функції open, read та write через блокування заборонятися при виклику. Для advisory locking приймається, що користувач сам є відповідальним за перевірку того, чи існують певні блокування або ні. Неможливо заборонити всім процесам доступ до файлу. Тільки процеси, опитують наявність блокування за допомогою fcntl, блокуються за її наявності (та й то не завжди).
Для інших випадків є так звані строгі блокування(mandatory locking). Ці блокування забороняють процесу отримувати доступ за допомогою функцій read або write до даних , які раніше були блоковані іншим процесом через fcntl.
Строгі блокування можна дозволити, встановивши біт Set- Group - ID і знявши біт виконання для групи, наприклад:
Int mandatory_lock(int fd)
{
struct stat statbuffer;
if(fstat(fd, &statbuffer) < 0)
{
fprintf(stderr, "Помилка при виклику fstat\n");
return 0;
}
if(fchmod(fd (statbuffer.st_mode & ~S_IXGRP)|S_ISGID) < 0)
{
fprintf(stderr, "Неможливо встановити строге блокування\n");
return 0;
}
return 1;
}
Суворе блокування є залежним від системи. Однак, воно не може запобігти видалення файлу через unlink().
Якщо за допомогою open() відкривається файл з прапорами O_TRUNC і O_CREAT і для цього файлу встановлена суворе блокування, то повертається помилка зі значенням errno = EAGAIN.
Блокування частин файлу і тупики
Взаємне блокування процесів може виникнути через блокування файлів. Нехай , наприклад, процес 1 намагається встановити блокування в деякому файлі dead.txt в позиції 10.
Інший процес з 2 організовує блокування того ж самого файлу в позиції 20. Поки ситуація ще керована. Далі, процес 1 хоче організувати наступне блокування в позиції 20, де вже стоїть блокування процесу 2. При цьому використовується команда F_SETLKW. При цьому процес 1 зупиняється до тих пір, поки процес 2 знову не звільнить зі свого боку блокування в позиції 20. Тепер процес 2 намагається організувати в позиції 10, де процес 1 вже поставив своє блокування, таке ж блокування командою F_SETLKW, і також призупиняється і чекає, поки процес 1 зніме блокування. Тепер обидва процеси, 1 та 2, припинені і обидва чекають один одного ( F_SETLKW ) , утворюючи безвихідь. Ніякої з процесів не може відновити своє виконання .
Причини виникнення цієї ситуації багато в чому викликані невдалим проектуванням алгоритмів. У Linux не передбачені механізми визначення та запобігання глухого кута, оскільки присутність цих механізмів суттєво впливає на продуктивність системи. Відповідальність за запобігання глухого кута цілком лягає на програміста.
Приклад програми , що викликає глухий кут:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<errno.h>
extern int errno;
