Основы операционных систем. Практикум

       

Необходимость синхронизации процессов и нитей исполнения, использующих общую память


Все рассмотренные на этом семинаре примеры являются не совсем корректными. В большинстве случаев они работают правильно, однако возможны ситуации, когда совместная деятельность этих процессов или нитей исполнения приводит к неверным и неожиданным результатам. Это связано с тем, что любые неатомарные операции, связанные с изменением содержимого разделяемой памяти, представляют собой критическую секцию процесса или нити исполнения. Вспомните рассмотрение критических секций в лекции 5.

Вернемся к рассмотрению программ из раздела "Прогон программ с использованием разделяемой памяти". При одновременном существовании двух процессов в операционной системе может возникнуть следующая последовательность выполнения операций во времени:

... Процесс 1: array[0] += 1; Процесс 2: array[1] += 1; Процесс 1: array[2] += 1; Процесс 1: printf( "Program 1 was spawn %d times, program 2 - %d times, total - %d times\n", array[0], array[1], array[2]); ...

Тогда печать будет давать неправильные результаты. Естественно, что воспроизвести подобную последовательность действий практически нереально. Мы не сможем подобрать необходимое время старта процессов и степень загруженности вычислительной системы. Но мы можем смоделировать эту ситуацию, добавив в обе программы достаточно длительные пустые циклы перед оператором array[2] += 1; Это проделано в следующих программах.

Листинг 6.3a. Программа 1 (06-3а.с) для иллюстрации некорректной работы с разделяемой памятью. (html, txt)

Листинг 6.3b. Программа 2 (06-3b.c) для иллюстрации некорректной работы с разделяемой памятью. (html, txt)

Наберите программы, сохраните под именами 06-3а.с и 06-3b.c cоответственно, откомпилируйте их и запустите любую из них один раз для создания и инициализации разделяемой памяти. Затем запустите другую и, пока она находится в цикле, запустите, например, с другого виртуального терминала, снова первую программу. Вы получите неожиданный результат: количество запусков по отдельности не будет соответствовать количеству запусков вместе.

Как мы видим, для написания корректно работающих программ необходимо обеспечивать взаимоисключение при работе с разделяемой памятью и, может быть, взаимную очередность доступа к ней. Это можно сделать с помощью рассмотренных в лекции 6 алгоритмов синхронизации, например, алгоритма Петерсона или алгоритма булочной.

Задача повышенной сложности: модифицируйте программы из этого раздела для корректной работы с помощью алгоритма Петерсона.

На следующем семинаре мы рассмотрим семафоры, которые являются средством System V IPC, предназначенным для синхронизации процессов.



Содержание раздела