Linux Kernel (Ядро линукса) (часть 1)


Макрос spin Jock при отсутствии вытеснения в ядре

Если при компиляции ядра возможность вытеснения в ядре не была предусмотрена, макрос spin iock будет существенно отличаться от того, что описан в предыдущем разделе. В этом случае макрос возвращает фрагмент ассемблерного кода, фактически эквивалентный следующему "плотному” циклу ожидания без прекращения выполнения3:
1: lock; decb slp->slock jns 3f
2: pause cmpb $0,slp->slock jle 2b jmp lb
3:Ассемблерная инструкция decb уменьшает значение спин-блокировки. Она атомарна, потому что имеет префикс в виде байта lock. Затем проверяет флаг знака. Если он сброшен, значит, спин-блокировка была установлен в 1 (разблокировано), и нормальное выполнение продолжается с метки 3 (суффикс f отмечает тот факт, что инструкция перехода направлена вперед, т. е. метка появляется в одной из последующих строчек программы). В противном случае плотный” цикл после метки 2 (суффикс ь означает метку, расположенную сзади”) выполняется до тех пор, пока спин-блокировка не примет положительное значение. Затем выполнение продолжается с метки 1, потому что небезопасно идти дальше, не убедившись, что блокировка не захвачена другим процессом.

Макрос spin__unlock

Макрос spin uniock освобождает спин-блокировку, полученную процессом. Он выполняет следующую ассемблерную инструкцию:
movb $1, slp->slock
а затем вызывает функцию preempt enabie (если вытеснение в ядре не поддерживается, функция preempt enabie ничего не предпринимает). Заметьте, что байт lock не используется, потому что в микропроцессорах 80x86 обращения к памяти только для записи выполняются атомарно.

Спин-блокировки чтения/записи

Спин-блокировки чтения/записи были созданы для повышения уровня параллельности в ядре. Они позволяют нескольким управляющим трактам ядра одновременно читать одну структуру, если никакой другой тракт не модифицирует ее. Если какой-то тракт пожелает записать данные в структуру, он должен будет захватить спин-блокировку на запись и получить исключительный доступ к ресурсу. Естественно, параллельное чтение данных повышает производительность системы.две критические области (С1 и С2), защищенные спин-блокировками чтения/записи. Тракты ядра R0 и R1 одновременно читают данные в области С1, а тракт W0 ждет спин-блокировку на запись. Тракт W1 пишет данные в область С2, а тракты R2 и W2 ждут спин- блокировку на чтение и запись соответственно.

Каждая спин-блокировка чтения/записи представлена структурой rwiock t. Ее поле lock имеет длину 32 бита и кодирует два разных информационных элемента:

- 24-битовый счетчик, показывающий количество трактов ядра, одновременно читающих защищаемую структуру. Значения счетчика в дополнительном коде хранится в битах 0—23 этого поля;
- флаг "разблокировано", устанавливаемый, когда никакой тракт не читает и не записывает данные, и сбрасываемые в противном случае. Этот флаг хранится в бите 24.
Обратите внимание, что в поле lock хранится число охоюооооо, если спин- блокировка свободна (флаг "разблокировано" установлен, и нет читающих трактов), число охоооооооо, если спин-блокировка получена для записи (флаг "разблокировано” сброшен, и нет читающих трактов), и любое число из последовательности OxOOffffff, OxOOfffffe и т. д., если спин-блокировка была получена для чтения одним, двумя и т. д. процессами (флаг "разблокировано” сброшен, а в двадцати четырех битах хранится число читающих трактов в дополнительном коде). Как и структура spiniock t, структура rwiock t имеет поле break_lock. Макрос rwiock init инициализирует поле lock спин-блокировки чтения/записи значением охоюооооо (разблокировано), а поле break iock— нулем.
Получение и освобождение блокировки на чтение
Макрос read iock, принимающий в качестве параметра адрес rwip спин- блокировки чтения/записи, аналогичен макросу spin iock, описанному ранее. Если компиляции ядра была выбрана опция вытеснения в ядре, макрос выполняет точно те же действия, что макрос spin_iock(), но с единственным исключением: чтобы получить спин-блокировку чтения/записи на шаге , макрос вызывает функцию rawreadtryiock о:
int rawreadtryiock(rwiockt lock){atomic_t count = (atomic_t )lock->lock; atomic_dec(count); if (atomic_read(count) >= 0) return 1; atomic_inc(count); return 0;}
Обращение к полю lock, счетчику спин-блокировки чтения/записи, происходит с помощью атомарных операций. Однако следует обратить внимание, что функция в целом работает со счетчиком не атомарно. Например, счетчик может измениться между проверкой его значения в операторе if и возвратом единицы. Тем не менее функция работает корректно. В действительности, она возвращает , только если счетчик не содержал ноль или отрицательное число перед уменьшением, поскольку счетчик равен охоюооооо, когда спин- блокировка не захвачена, OxOOffffff, когда есть один читающий тракт, и охоооооооо, когда имеется один пишущий тракт.
Если при компиляции ядра опция вытеснения в ядре не была выбрана, макрос read iock возвращает следующий ассемблерный код:
movl $rwlp->lock,еах lock; subl $1,(еах)
jns If
call read_lock_failed
1:Здесь read iock faiiedO представляет собой следующую функцию на ассемблере:
read_lock_failed:
lock; incl (eax)
1: pause cmpl $1,(eax) js lb
lock; decl (eax)
js read_lock_failed ret
Макрос read iock атомарным образом уменьшает значение спин-блокировки, тем самым увеличивая количество читающих потоков. Тракт получает спин-блокировку, если операция уменьшения дает неотрицательное значение. В противном случае вызывается функция read_lock_failed. Она атомарно увеличивает значение поля lock, чтобы отменить уменьшение, выполнение макросом read iock, а затем входит в цикл, пока поле не станет положительным (больше ИЛИ равным 1). После ЭТОГО функция read_lock_failed
снова пытается получить спин-блокировку (другой управляющий тракт ядра мог получить спин-блокировку на запись сразу после инструкции cmp).
Освобождение спин-блокировки, полученной для чтения, происходит очень просто, поскольку макрос read uniock должен всего лишь увеличить счетчик в поле lock следующей ассемблерной инструкцией:
lock; incl rwlp->lock
(уменьшая тем самым количество читающих потоков), а затем вызвать функцию preempt enabie , чтобы вновь включить вытеснение в ядре.

Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | Следующая страница




Возможно, Вас также заинтересует:

ОС Knoppix - это Linux без про...

ВведениеЕсли вы цените свое время, умеете считать деньги и знаете стоимость информации, то эта книга...

Linux Kernel (Ядро линукса) (ч...

Спин-блокировкаСпин-блокировка необходима в многопроцессорной системе, потому что могут возникнуть...

Linux Kernel (Ядро линукса) (ч...

Копирование при записи В системах Unix первых поколений создание процесса было реализовано довольно...

Linux Kernel (Ядро линукса) (ч...

Буферы блоков и головы буферовУ каждого буфера есть дескриптор голова буфера, имеющий тип buffer...