Реферат - Взаимоисключение параллельных процессов и потоков в Linux. Синхронизация в ядре - файл n1.doc

Реферат - Взаимоисключение параллельных процессов и потоков в Linux. Синхронизация в ядре
скачать (182.1 kb.)
Доступные файлы (5):
n1.doc252kb.09.07.2011 13:25скачать
n2.rtf373kb.09.07.2011 13:26скачать
n3.doc330kb.06.06.2011 15:45скачать
n4.doc283kb.06.06.2011 16:01скачать
n5.rtf228kb.09.07.2011 12:50скачать

n1.doc

  1   2   3   4   5   6   7

Взаимоисключение параллельных процессов и потоков



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

Атомарные операции


Атомарные операции (atomic operations) предоставляют инструкции, которые выполняются атомарно, — т.е. не прерываясь. Так же как и атом вначале считался неделимой частицей, атомарные операции являются неделимыми инструкциями. Например, как было показано в предыдущей главе, операция атомарного инкремен­та позволяет считывать из памяти и увеличивать на единицу значение переменной за один неделимый и непрерывный шаг. В отличие от состояния конкуренции за ре­сурс, которая обсуждалась в предыдущей главе, результат выполнения такой опера­ции всегда один и тот же, например, как показано в следующем примере (допустим, что значение переменной i вначале равно 7).


Поток 1

инкремент i (7->8)



Поток 2



инкремент i (8->9)


Результирующее значение 9 — правильное. Параллельное выполнение двух атомар­ных операций с одной и той же переменной невозможно никогда. Таким образом, для такой операции инкремента состояние конкуренции за ресурс возникнуть не может.

Ядро предоставляет два набора интерфейсов для выполнения атомарных опе­раций: один — для работы с целыми числами, а другой — для работы с отдельными битами. Эти интерфейсы реализованы для всех аппаратных платформ, которые под­держиваются операционной системой Linux. Большинство аппаратных платформ поддерживают атомарные операции или непосредственно, или путем блокировки шины доступа к памяти при выполнении одной операции (что в свою очередь га­рантирует, что другая операция не может выполниться параллельно). Это как-то по­зволяет справиться с проблемой в случае аппаратных платформ, таких как SPARC, которые не поддерживают базовых машинных инструкций для выполнения атомар­ных операций.

Целочисленные атомарные операции



Средства выполнения атомарных операций с целыми числами работают с типом данных atomic_t. Вместо того, чтобы использовать функции, которые работают непосредственно с типом данных int языка С, по ряду причин используется спе­циальный тип данных. Во-первых, функции, которые выполняют атомарные опера­ции, принимают только аргументы типа atomic_t, это гарантирует, что атомарные операции выполняются только с данными этого специального типа. В то же время это также гарантирует, что данные этого типа не смогут передаваться в другие функ­ции, которые не выполняют атомарных операций. Действительно, ничего хорошего не будет от таких атомарных операций, которые иногда атомарные, а иногда — нет. Следующий момент— использование типа atomic_t позволяет гарантировать, что компилятор (по ошибке, но для повышения эффективности) не будет оптимизи­ровать операции обращения к атомарным переменным. Важно, чтобы атомарные операции получали правильное значение адреса переменной в памяти, а не адреса временных копий. И наконец, за типом atomic_t скрываются различия между реа­лизациями для различных аппаратных платформ.

Кроме того, что тип atomic_t — это 32-разрядное целое число на всех машинах, которые поддерживаются операционной системой Linux, при разработке кода не­обходимо учитывать, что максимальный диапазон значений переменной этого типа не может быть больше 24 бит. Это связано с аппаратной платформой SPARC, для которой используется несколько странная реализация атомарных операций: в млад­шие 8 бит 32-разрядного целого числа типа int встроена блокировка, как показано на рис. 9.1.
32-разрядный тип atomic_t


24-битовое знаковое целое

Блокировка




(Бит) 31

7

0


Рис. 9.1. Структура 32-битового типа atomic_t для,
аппаратной платформы SPARC в старой реализации
Блокировка используется для предотвращения параллельного доступа к перемен­ной атомарного типа, так как для аппаратной платформы SPARC отсутствует соот­ветствующая поддержка на уровне машинных инструкций. Следовательно, на маши­нах SPARC могут быть использованы только 24 бит. Хотя код, который рассчитан на использование полного 32-битового диапазона значений, будет работать и на маши­нах других типов, он может приводить к странным и коварным ошибкам на машинах типа SPARC, и так делать не нужно. В последнее время умные хакеры додумались, как для аппаратной платформы SPARC обеспечить тип atomic_t, который позволяет хранить полноценное 32-разрядное целое число, и указанного ограничения боль­ше не существует. Тем не менее старая 24-битовая реализация все еще используется в старом коде для аппаратной платформы SPARC, и этот код все еще имеется в файле для этой аппаратной платформы.

Объявления всего, что необходимо для использования целочисленных атомарных операций, находятся в заголовочном файле . Для некоторых аппа­ратных платформ существуют дополнительные средства, которые уникальны только для этой платформы, но для всех аппаратных платформ существует минимальный набор операций, которые используются в ядре повсюду. При написании кода ядра необходимо гарантировать, что соответствующие операции доступны и правильно реализованы для всех аппаратных платформ.

Объявление переменных типа atomic_t производится обычным образом. При необходимости можно установить заданное значение этой переменной.
atomic_t u; /* определение переменной и */

atomic_t v = ATOMIC_INIT (0); /* определение переменной v и

инициализация ее в значение нуль */
Выполнять операции так же просто.
atomic_set(&v, 4); /* v = 4 (атомарно) */

atoraic_add(2, &v); /* v = v + 2 = 6 (атомарно) */

atomic_inc(&v); /*v = v + l = 7 (атомарно) */
Если необходимо конвертировать тип atomic_t в тип int. то нужно использо­вать функцию atomic_read().
printk("%d\n", atomic_read (sv) ) ; /* будет напечатано "7" */
Наиболее частое использование атомарных целочисленных операций — это инкремент счетчиков. Защищать один счетчик с помощью сложной системы бло­кировок — это глупо, поэтому разработчики используют вызовы atcmic_int () и atomic_dec (), которые значительно быстрее. Еще одно использование атомарных целочисленных операций — это атомарное выполнение операции с проверкой ре­зультата. Наиболее распространенный пример — это атомарные декремент и про­верка результата, с помощью функции
int atomic_dec_and_test (atoraic_t *v) .
Эта функция уменьшает на единицу значение заданной переменной атомарного типа. Если результат выполнения операции равен нулю, то возвращается значение true, иначе возвращается false. Полный список всех атомарных операций с целы­ми числами (т.е. тех, которые доступны для всех аппаратных платформ) приведен в табл. 9.1. Все операции, которые реализованы для определенной аппаратной плат­формы, приведены в файле .

Обычно атомарные операции реализованы как функции с подстановкой тела и встраиваемыми инструкциями на языке ассемблера (разработчики ядра любят inline). В случае если какая-либо из функций обладает внутренней атомарнос­тью, то обычно она выполняется в виде макроса. Например, для большинства нор­мальных аппаратных платформ считывание одного машинного слова данных — это атомарная операция. Операция считывания всегда возвращает машинное слово в непротиворечивом состоянии или перед операцией записи, или после нее, но во время операции записи чтение не может быть выполнено никогда. Следовательно, функция atomic_read () обычно реализуется как макрос, который возвращает цело­численное значение переменной типа atomic_t.
Таблица 9.1, Полный список всех атомарных операций с целыми числами


Атомарная целочисленная операция

Описание

ATOMIC_INIT(int i)
int atomic read(atomic_t *v)
void atomic set (atomic t *v, int i)
void atomic add(int i, atomic_t *v)
void atomic_sub(int i, atomic_t *v)
void atomic_inc(atomic_t *v)
void atomic_dec(atomic_t *v)
int atomic_sub_and_test (int i, atomic_t *v)

int atomic_add_negative(int i, atoraic_t *v)

int atomic dec and test(atomic t *v)

int atomic inc and test(atomic t *v)


Объявление и инициализация в значение i переменной типа atomic_t

Атомарное считывание значения целочислен­ной переменной v

Атомарно установить переменную v в значение i

Атомарно прибавить значение i к переменной v

Атомарно вычесть значение i из переменной v

Атомарно прибавить единицу к переменной v

Атомарно вычесть единицу из переменной v

Атомарно вычесть значение i из переменной v и возвратить true, если результат равен нулю, и false в противном случае

Атомарно прибавить значение i к переменной v и возвратить true, если результат операции меньше нуля, иначе возвратить false

Атомарно вычесть единицу из переменной v и возвратить true, если результат операции равен нулю, иначе возвратить false

Атомарно прибавить единицу к переменной v и возвратить true, если результат операции равен нулю, иначе возвратить false



  1   2   3   4   5   6   7


Взаимоисключение параллельных процессов и потоков
Учебный материал
© nashaucheba.ru
При копировании укажите ссылку.
обратиться к администрации