5.4 Взаимоисключение
Рассмотрим систему, обслуживающую в режиме разделения времени много терминалов.
Предположим, что пользователи заканчивают каждую строку, которую они вводят в машину, символом <Return>, что необходимо вести постоянный учет общего количества строк, введенных пользователями с начала рабочего дня, и что контроль каждого терминала пользователя осуществляется при помощи отдельного процесса.
Каждый раз, когда один из этих процессов принимает строку с соответствующего терминала пользователя, он прибавляет единицу к глобальной разделяемой переменной системы под названием СТРОКВВЕДЕННЫХ.
Рассмотрим, что произойдет, когда два процесса попытаются одновременно произвести прибавление 1 к этой переменной. Предположим, что у каждого процесса имеется собственная копия кода
LOAD СТРОКВВЕДЕННЫХ
ADD 1
STORE СТРОКВВЕДЕННЫХ
Шаг-1. Пусть в данный момент переменная СТРОКВВЕДЕННЫХ имеет значение 21687.
Шаг-2. Предположим теперь, что первый процесс выполняет команды загрузки LOAD и сложения ADD, после чего в аккумуляторе оказывается значение 21688.
Шаг-3. Затем этот процесс (ввиду истечения выделенного ему кванта времени) уступает процессор второму процессу.
Шаг-4. Второй процесс выполняет теперь все три команды, устанавливая значение СТРОКВВЕДЕННЫХ равным 21688.
Шаг-5. Затем второй процесс возвращает управление процессором первому процессу, который продолжает свое выполнение с команды записи STORE и также помещает значение 21688 в поле СТРОКВВЕДЕННЫХ.
Таким образом, из-за не координированного доступа к разделяемой переменной СТРОКВВЕДЕННЫХ система в сущности теряет одну строку — правильная общая сумма сейчас должна была бы составить 21689.
Эту задачу можно решить, если каждому процессу предоставлять монопольное, исключительное право доступа к переменной СТРОКВВЕДЕННЫХ.
Когда один процесс увеличивает эту разделяемую переменную, всем остальным процессам, которым нужно было бы также произвести приращение в то же самое время, придется ждать, а когда данный процесс закончит свое обращение к переменной, будет разрешено продолжить работу одному из процессов, находящихся в состоянии ожидания. Таким образом, каждый процесс, обращающийся к разделяемым данным, исключает для всех других процессов возможность одновременного с ним обращения к этим данным. Это называется взаимоисключением.
5.5 Критические участки
Взаимоисключение необходимо только в том случае, когда процессы обращаются к разделяемым, общим данным — если же они выполняют операции, которые не приводят к конфликтным ситуациям, они должны иметь возможность работать параллельно.
Когда процесс производит обращение к разделяемым данным, то говорят, он находится в своем критическом участке (или в своей критической области).
Когда какой-либо процесс находится в своем критическом участке, другие процессы могут, конечно, продолжать выполнение, но без входа в их критические участки. Когда процесс выходит из твоего критического участка, то одному из остальных процессов, ожидающих входа в свои критические участки, должно быть разрешено продолжить работу (если в этот момент действительно есть процесс в состоянии ожидания). Обеспечение взаимоисключения является одной из ключевых проблем параллельного программирования.
Было предложено много способов решения этой проблемы — программные и аппаратные; частные, низкоуровневые и глобальные, высокоуровневые; одни из предложенных способов предусматривают свободное взаимодействие между процессами, а другие требуют строгого соблюдения жестких протоколов.
Когда процесс находится в своем критическом участке, это его ко многому обязывает.
В этом особом режиме процесс имеет исключительное право доступа к разделяемым данным, а всем остальным процессам, которым в то же самое время требуется доступ к этим данным, приходится ждать.
Поэтому процессы должны как можно быстрее проходить свои критические участки и не должны в этот период блокироваться, так что критические участки необходимо кодировать очень тщательно (чтобы, например, не допустить возможности зацикливания внутри критического участка).
Если процесс, находящийся в своем критическом участке, завершается, либо естественным, либо аварийным путем, то операционная система при выполнении служебных функций по завершению процессов должна отменить режим взаимоисключения, с тем чтобы другие процессы получили возможность входить в свои критические участки.
