21 /* второй дочерний процесс */
22 sleep(3);
23 printf("%s: second child tries to obtain read lock\n", Gf_time());
24 Readw_lock(fd, 0, SEEK_SET, 0);
25 printf("%s: second child obtains read lock\n", Gf_time());
26 sleep(4);
27 Un_lock(fd, 0, SEEK_SET, 0);
28 printf("%s: second child releases read lock\n", Gf_time());
29 exit(0);
30 }
31 /* родительский процесс */
32 sleep(5);
33 Un_lock(fd, 0, SEEK_SET, 0);
34 printf("%s: parent releases read lock\n", Gf_time());
35 exit(0);
36 }
6-8 Родительский процесс открывает файл и устанавливает блокировку на чтение для всего файла целиком. Обратите внимание, что мы вызываем read_lock (которая возвращает ошибку в случае недоступности ресурса), а не readw_lock (которая ждет его освобождения), потому что мы ожидаем, что эта блокировка будет установлена немедленно. Мы также выводим значение текущего времени функцией gf_time [24, с. 404], когда получаем блокировку.
9-19 Порождается первый процесс, который ждет 1 секунду и блокируется в ожидании получения блокировки на запись для всего файла. Затем он устанавливает эту блокировку, ждет 2 секунды, снимает ее и завершает работу.
20-30 Порождается второй процесс, который ждет 3 секунды, давая возможность первому попытаться установить блокировку на запись, а затем пытается получить блокировку на чтение для всего файла. По моменту возвращения из функции readw_lock мы можем узнать, был ли ресурс предоставлен немедленно или второму процессу пришлось ждать первого. Блокировка снимается через четыре секунды.
31-35 Родительский процесс ждет пять секунд, снимает блокировку и завершает работу.
На рис. 9.2 приведена временная диаграмма выполнения программы в Solaris 2.6, Digital Unix 4.0B и BSD/OS 3.1. Как видно, блокировка чтения предоставляется второму дочернему процессу немедленно, несмотря на наличие в очереди запроса на блокировку записи. Существует вероятность, что запрос на запись так и не будет выполнен, если будут постоянно поступать новые запросы на чтение. Ниже приведен результат выполнения программы, в который были добавлены пустые строки для улучшения читаемости:
alpha % test2
16:32:29.674453: parent has read lock
16:32:30.709197: first child tries to obtain write lock
16:32:32.725810: second child tries to obtain read lock
16:32:32.728739: second child obtains read lock
16:32:34.722282: parent releases read lock
16:32:36.729738: second child releases read lock
16:32:36.735597: first child obtains write lock
16:32:38.736938: first child releases write lock
Пример: имеют ли приоритет запросы на запись перед запросами на чтение?
Следующий вопрос, на который мы попытаемся дать ответ, таков: есть ли приоритет у запросов на блокировку записи перед запросами на блокировку чтения, если все они находятся в очереди? Некоторые решения задачи читателей и писателей предусматривают это.
В листинге 9.7 приведен текст нашей тестовой программы, а на рис. 9.3 — временная диаграмма ее выполнения.
//lock/test3.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int fd;
6 fd = Open("test1.data", O_RDWR | O_CREAT, FILE_MODE);
7 Write_lock(fd, 0, SEEK_SET, 0); /* родительский процесс блокирует весь файл на запись */
8 printf("ls: parent has write lock\n", Gf_time());
9 if (Fork() == 0) {
10 /* первый дочерний процесс */
11 sleep(1);
12 printf("ls: first child tries to obtain write lock\n", Gf_time());