- •10.1. Виды параллельной обработки
- •10.1.1. Классификация систем параллельной обработки
- •10.2. Матричная обработка данных
- •Массив процессорных элементов
- •10.3. Архитектура мультипроцессорных систем общего назначения
- •10.4. Коммуникационные сети
- •10.5. Организация памяти в мультипроцессорных системах
- •10.6. Программный параллелизм и общие переменные
- •10.6.1. Доступ к общим переменным
- •10.6.2. Согласованность кэша
- •10.6.3. Блокировка и согласованность кэш-памяти
- •10.7. Мультикомпьютерные системы
- •10.8. Общая память и передача сообщений
- •10.8.1. Система с общей памятью
- •10.8.2. Система с передачей сообщений
- •10.9. Производительность мультипроцессорных систем
- •10.9.1. Закон Амдала
- •10.9.2. Показатели производительности
10.6.1. Доступ к общим переменным
Предположим, что мы определили две задачи, которые могут параллельно выполняться мультипроцессорной системой. Эти задачи почти полностью независимы, но время от времени они считывают и модифицируют некоторую общую переменную, хранящуюся в глобальной памяти. Пусть, например, общая переменная SUM представляет баланс некоторого счета. Этот счет должен обновляться несколькими задачами, выполняющимися разными процессорами. Каждая задача производит с переменной SUM следующие действия: считывает ее текущее значение, выполняет некоторую операцию с его использованием, а результат сохраняет в переменной SUM. Нетрудно представить себе, какие ошибки могут возникнуть, если такие операции с переменой SUM будут осуществляться задачами Т1 и Т2, выполняющимися в параллельном режиме процессорами Р1 и Р2. Предположим, что обе задачи, Т1 и Т2, считывают текущее значение переменной SUM, скажем 17, и каждая по отдельности его модифицирует. Задача Т1 прибавляет к нему 5, получая 22, а задача Т2 вычитает из него 7, результатом чего является число 10. Затем каждая из задач заносит в переменную SUM свой результат — сначала это действие выполняет задача Т2, затем задача Т1. Теперь в переменной SUM хранится число 22, что является ошибкой, так как на самом деле в этой переменной должно находиться значение l5 (17 +5-7), которое получится при строго последовательном проведении изменений.
Для того чтобы обеспечить правильную работу с переменной SUM, каждая задача должна получать к ней монопольный доступ на все время выполнения последовательности операций чтения, модификации и записи. С этой целью может быть задействована глобальная переменная блокировки LOCK и машинная команда Test and Set. Переменная LOCK может принимать значение 0 или 1. Мы используем ее для того, чтобы гарантировать, что никакие две задачи не смогут одновременно получить доступ к переменной SUM. Последовательность команд, для выполнения которой требуется монопольный доступ к некоторой переменной, называется критической секцией программы. Переменная LOCK используется следующим образом. Если ни одна из задач не находится в состоянии выполнения критической секции, оперирующей переменной SUM, переменная LOCK равна 0. Когда какая-либо из задач собирается модифицировать переменную SUM, она сначала проверяет значение переменной LOCK, а затем устанавливает таковое в 1 независимо от ее исходного значения переменной. Если исходным значением является 0, задача приступает к работе с переменной SUM, поскольку это означает, что никакая другая задача с этой переменной в данный момент не работает. Если же исходное значение переменной LOCK равняется 1, значит, с переменной SUM работает другая задача. В таком случае первая задача должна находиться в состоянии ожидания до тех пор, пока вторая не установит значение переменной LOCK в 0, и лишь затем она может приступать к собственной операции с этой переменной. Все перечисленные операции с переменной LOCK выполняются командой Test and Set. Эта команда в соответствии со своим названием незаметно для программы выполняет проверку и установку переменной LOCK. В процессе ее работы соответствующий модуль памяти не должен отвечать на запросы доступа от других процессоров.
TaskTl
……
LOCK1 TAS.B LOCKBYTE
BMI LOCK1
«Модификация SUM» Критическая секция
CRL.B LOCKBYTE
…..
TaskT2
…..
LOCK2 TAS.B LOCKBYTE
BMI LOCK2
«Модификация SUM» Критическая секция
CRL.B LOCKBYTE
…..
Рис. 10.14. Совместный доступ к критической секции программы
Рассмотрим конкретный пример — действие команды Test and Set (или TAS) микропроцессора 68000 компании Motorola. Один из операндов этой команды имеет размер 1 байт. Предположим, что данный операнд хранится в памяти по адресу LOCKBYTE. Бит b7 старший бит этого операнда — выполняет роль описанной выше переменной LOCK. При осуществлении командой TAS операций проверки и установки бита b7 прерываний не происходит. Флагу кода условия N (отрицательное значение) присваивается исходное значение бита Бит b7. Если флаг N равен 0, по завершении выполнения команды TAS программа может войти в критическую секцию, а если он равен 1, программа должна подождать, пока он не примет значение 0. На рис. 10.14 показано, как две задачи, Т1 и Т2, манипулируют переменной LOCKBYTE для входа в критическую секцию кода, где они обновляют общую переменную SUM. За командой TAS следует команда условного перехода. При N=1 опять производится переход к команде TAS, в результате чего получается цикл ожидания, в котором эта команда выполняется над операндом по адресу LOCKBYTE до тех пор, пока бит b7 не примет значение 0. Если бит b7 = 0, переход к команде TAS не осуществляется и программа входит в критическую секцию. По завершении выполнения критической секции переменная LOCKBYTE очищается. В результате бит b7 устанавливается в 0 и ожидающая этого программа может войти в свою критическую секцию.
Команда TAS является примером простой машинной команды, предназначенной для реализации блокировки. Подобные команды имеются в большинстве компьютеров. Они могут выполнять и дополнительные функции, такие как условные переходы на основе результатов проверки.