Взаимная блокировка процессов может возникнуть из-за блокировки файлов. Пусть, например, процесс номер 1 пытается установить блокировку в некотором файле dead.txt в позиции 10.
Другой процесс с номером 2 организует блокировку того же самого файла в позиции 20. Эта ситуация еще управляема. Далее процесс 1 хочет организовать следующую блокировку в позиции 20, где уже стоит блокировка процесса 2. При этом используется команда F_SETLKW. При этом процесс 1 приостанавливается до тех пор, пока процесс 2 снова не освободит со своей стороны блокировку в позиции 20. Теперь процесс 2 пытается организовать в позиции 10, где процесс 1 уже поставил блокировку, такую же блокировку командой F_SETLKW, и также приостанавливается и ждет, пока процесс 1 снимет блокировку. Теперь оба процесса, номер 1 и номер 2, приостановлены и оба ждут друг друга (F_SETLKW), образуя дедлок. Никакой из процессов не возобновит свое выполнение.
Причины, по которым эта ситуация возникает, во многом вызваны неудачным проектированием алгоритмов. В UNIX не предусмотрены механизмы определения и предотвращения тупика. Наличие этих механизмов существенно влияет на производительность системы, поэтому ответственность за предотвращение тупика ложится на программиста.
Пример программы, вызывающей тупик, приведен ниже (рис. 16):
#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) failed (%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 (Ресурс временно недоступен).