Читать интересную книгу UNIX: взаимодействие процессов - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 50 51 52 53 54 55 56 57 58 ... 128

Первый из них пытается установить блокировку на запись (и блокируется, поскольку родительский процесс установил блокировку на чтение для всего файла), а второй процесс секунду спустя пытается получить блокировку на чтение. Временная диаграмма этих запросов изображена на рис. 9.2, а в листинге 9.6 приведен текст нашей программы. 

Рис. 9.2. Определение возможности установки блокировки на чтение при наличиивочереди блокировки на запись

Листинг 9.6. Определение возможности установки блокировки на чтение при наличии в очереди блокировки на запись

//lock/test2.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   Read_lock(fd, 0, SEEK_SET, 0); /* родительский процесс блокирует весь файл на чтение */

8   printf("%s: parent has read lockn", Gf_time());

9   if (Fork() == 0) {

10   /* первый дочерний процесс */

11   sleep(1);

12   printf("%s: first child tries to obtain write lockn", Gf_time());

13   Writew_lock(fd, 0, SEEK_SET, 0); /* здесь он будет заблокирован */

14   printf("%s: first child obtains write lockn", Gf_time());

15   sleep(2);

16   Un_lock(fd, 0, SEEK_SET, 0);

17   printf("ls: first child releases write lockn", Gf_time());

18   exit(0);

19  }

20  if (Fork() == 0) {

21   /* второй дочерний процесс */

22   sleep(3);

23   printf("%s: second child tries to obtain read lockn", Gf_time());

24   Readw_lock(fd, 0, SEEK_SET, 0);

25   printf("%s: second child obtains read lockn", Gf_time());

26   sleep(4);

27   Un_lock(fd, 0, SEEK_SET, 0);

28   printf("%s: second child releases read lockn", Gf_time());

29   exit(0);

30  }

31  /* родительский процесс */

32  sleep(5);

33  Un_lock(fd, 0, SEEK_SET, 0);

34  printf("%s: parent releases read lockn", 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 мы можем узнать, был ли ресурс предоставлен немедленно или второму процессу пришлось ждать первого. Блокировка снимается через четыре секунды.

Родительский процесс блокирует ресурс 5 секунд

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 — временная диаграмма ее выполнения.

Листинг 9.7. Есть ли у писателей приоритет перед читателями

//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 lockn", Gf_time());

9   if (Fork() == 0) {

10   /* первый дочерний процесс */

11   sleep(1);

12   printf("ls: first child tries to obtain write lockn", Gf_time());

13   Writew_lock(fd, 0, SEEK_SET, 0); /* здесь процесс будет заблокирован */

14   printf("%s: first child obtains write lockn", Gf_time());

15   sleep(2);

16   Un_lock(fd, 0, SEEK_LET, 0);

17   printf("ls: first child releases write lockn", Gf_time());

18   exit(0);

19  }

20  if (Fork() == 0) {

21   /* второй дочерний процесс */

22   sleep(3);

23   printf("ls: second child tries to obtain read lockn", Gf_time());

24   Readw_lock(fd, 0, SEEK_SET, 0);

25   printf(%s: second child obtains read lockn", Gf_time());

26   sleep(4);

27   Un_lock(fd, 0, SEEK_SET, 0);

28   printf("ls: second child releases read lockn", Gf_time());

29   exit(0);

30  }

31  /* родительский процесс */

32  sleep(5);

33  Un_lock(fd, 0, SEEK_SET, 0);

34  printf("ls: parent releases write lockn", Gf_time());

35  exit(0);

36 }

Родительский процесс создает файл и устанавливает блокировку на запись

6-8 Родительский процесс создает файл и блокирует его целиком на запись.

Первый дочерний процесс

9-19 Порождается первый процесс, который ждет одну секунду, а затем запрашивает блокировку на запись для всего файла. Мы знаем, что при этом процесс будет заблокирован, поскольку родительский процесс установил блокировку и снимет ее только через пять секунд, и мы хотим, чтобы этот запрос был помещен в очередь.

Второй дочерний процесс

20-30 Порождается второй процесс, который ждет три секунды, а затем запрашивает блокировку на чтение на весь файл. Этот запрос будет также помещен в очередь.

И в Solaris 2.6, и в Digital Unix 4.0B мы видим, что блокировка на запись предоставляется первому процессу, как изображено на рис. 9.3. Но это еще не означает, что у запросов на запись есть приоритет перед запросами на чтение, поскольку, возможно, ядро предоставляет блокировку в порядке очереди вне зависимости от того, на чтение она или на запись. Чтобы проверить это, мы создаем еще одну тестовую программу, практически идентичную приведенной в листинге 9.7, но в ней блокировка на чтение запрашивается через одну секунду, а блокировка на запись — через три секунды. Эти две программы иллюстрируют, что Solaris и Digital Unix обрабатывают запросы в порядке очереди вне зависимости от типа запроса. Однако в BSD/OS 3.1 приоритет имеют запросы на чтение. 

Рис. 9.3. Есть ли у писателей приоритет перед читателями

Вот вывод программы из листинга 9.7, на основании которого была составлена временная диaгрaммa на рис. 9.3:

alpha % test3

16:34:02.810285: parent has write lock

16:34:03.848166: first child tries to obtain write lock

16:34:05.861082: second child tries to obtain read lock

16:34:07.858393: parent releases write lock

16:34:07.865222: first child obtains write lock

16:34:09.865987: first child releases write lock

16:34:09.872823: second child obtains read lock

16:34:13.873822: second child releases read lock

9.7. Запуск единственного экземпляра демона

Часто блокировки записей используются для обеспечения работы какой-либо пpoгрaммы (например, демона) в единственном экземпляре. Фрагмент кода, приведенный в листинге 9.8, должен выполняться при запуске демона.

Листинг 9.8. Гарантия выполнения единственного экземпляра программы

//lock/onedaemon.c

1  #include "unpipc.h"

2  #define PATH_PIDFILE "pidfile"

3  int

4  main(int argc, char **argv)

5  {

6   int pidfd;

7   char line[MAXLINE];

1 ... 50 51 52 53 54 55 56 57 58 ... 128
На этом сайте Вы можете читать книги онлайн бесплатно русская версия UNIX: взаимодействие процессов - Уильям Стивенс.

Оставить комментарий