Взаимная блокировка процессов может возникнуть из-за блокировки файлов. Пусть, например, процесс 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/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
extern int errno;
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;
}
}
void writelock(char *proсess, int fd, off_t from,
off_t to) {
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_start=from;
lock.l_whence = SEEK_SET;
lock.l_len=to;
lock.l_pid = getpid();
if (fcntl(fd, F_SETLKW, &lock) < 0)
{
printf("%s : Ошибка
fcntl(fd, F_SETLKW, F_WRLCK) (%s)\n",
proсess,strerror(errno));
printf("\nВозник DEADLOCK (%s - proсess)!\n\n",
proсess);
exit(0);
}
else
printf("%s : fcntl(fd, F_SETLKW, F_WRLCK)
успешно\n", proсess);
status(&lock);
}
int main()
{
int fd, i;
pid_t pid;
if(( fd=creat("dead.txt", S_IRUSR |
S_IWUSR | S_IRGRP | S_IROTH))<0)
{
fprintf(stderr, "Ошибка при создании......\n");
exit(0);
}
/*Заполняем dead.txt 50 байтами символа X*/
for(i=0; i<50; i++)
write(fd, "X", 1);
if((pid = fork()) < 0)
{
fprintf(stderr, "Ошибка fork()......\n");
exit(0);
}
else if(pid == 0) //Потомок
{
writelock("Потомок", fd, 20, 0);
sleep(3);
writelock("Потомок" , fd, 0, 20);
}
else //Родитель
{
writelock("Родитель", fd, 0, 20);
sleep(1);
writelock ("Родитель", fd, 20, 0);
}
exit(0);
}
Вначале создается файл данных dead.txt, в который записывается 50 символов X. Затем родительский процесс организует блокировку от байта 0 до байта 19, а потомок - блокировку от байта 20 до конца файла (EOF). Потомок "засыпает" на 3 сек., а родитель теперь устанавливает блокировку от байта 20 до байта EOF и приостанавливается, так как байты от 20 до EOF блокированы в данный момент потомком, а родитель использует команду F_SETLKW. Наконец, потомок пытается установить блокировку на запись от байта 0 до байта 19, причем он также приостанавливается, так как в этой области уже установлена блокировка родителя и используется команда F_SETLKW. Здесь возникает тупик, что подтверждается выдачей кода ошибки для errno = EDEADLK (возникновение тупика по ресурсам). Тупик может возникнуть только при использовании команды F_SETLKW. Если применять команду F_SETLK, выдается код ошибки для errno = EAGAIN (ресурс временно недоступен).