Чтение онлайн

на главную

Жанры

Разработка ядра Linux
Шрифт:

В общем такое повышение масштабируемости — это очень хорошая вещь, которая позволяет повысить производительность операционной системы Linux на больших и более мощных системах. Чрезмерное увлечение "ростом" масштабируемости может привести к снижению производительности на небольших многопроцессорных и однопроцессорных машинах, потому что для небольших машин не требуются такие мелкоструктурные блокировки, а им приходится иметь дело с большей сложностью и с большими накладными расходами. Рассмотрим связанный список. Первоначальная схема блокировки обеспечивает одну блокировку на весь список. Со временем эта одна блокировка может стать узким местом на очень большой многопроцессорной машине, на которой очень часто обращаются к связанному списку. Для решения проблемы одна блокировка может быть разбита на большое количество

блокировок — одна блокировка на один элемент списка. Для каждого элемента списка, который необходимо прочитать или записать, необходимо захватывать уникальную блокировку этого элемента. Теперь конфликт при захвате блокировки будет только в случае, когда несколько процессоров обращаются к одному элементу списка. Что делать, если все равно есть высокий уровень конфликтов? Может быть, необходимо использовать блокировку для каждого поля элемента списка? (Ответ: НЕТ.) Если серьезно, даже когда очень мелко структурированные блокировки хорошо работают на очень больших SMP-машинах, то как они будут работать на двухпроцессорных машинах? Накладные расходы, связанные с дополнительными блокировками, будут напрасными, если на двухпроцессорной машине нет существенных конфликтов при работе с блокировками.

Тем не менее масштабируемость — это важный фактор. Важно с самого начала разрабатывать схему блокировок для обеспечения хорошей масштабируемости. Блокировки на уровне крупных структурных единиц могут стать узким местом даже на машинах с небольшим количеством процессоров. Между крупноструктурными и мелкоструктурными блокировками очень тонкая грань. Слишком крупноструктурные блокировки приводят к большому уровню конфликтов, а слишком мелкоструктурные — к напрасным накладным расходам, если уровень конфликтов при захвате блокировок не очень высокий. Оба варианта эквивалентны плохой производительности.

Необходимо начинать с простого и переходить к сложному только при необходимости. Простота — это ключевой момент.

Блокировки в вашем коде

Обеспечение безопасности кода при SMP-обработке — это не то, что можно откладывать на потом. Правильная синхронизация, блокировки без тупиковых ситуаций, масштабируемость и ясность кода- все это следует учитывать при разработке с самого начала и до самого конца. При написании кода ядра, будь то новый системный вызов или переписывание драйвера устройства, необходимо, прежде всего, позаботиться об обеспечении защиты данных от конкурентного доступа.

Обеспечение достаточной защиты для любого случая — SMP, вытеснение кода ядра и так далее — в результате приведет к гарантии того, что все данные будут защищены на любой машине и в любой конфигурации. В следующей главе будет рассказано о том, как это осуществить.

Теперь, когда мы хорошо подкованы в теории параллелизма, синхронизации и блокировок, давайте углубимся в то, какие существуют конкретные инструменты, предоставляемые ядром Linux, для того чтобы гарантировать отсутствие состояний конкуренции и тупиковых ситуаций в коде.

Глава 9

Средства синхронизации в ядре

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

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

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

вначале равно 7).

Поток 1 Поток 2

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

– инкремент 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.

Рис. 9.1. Структура 32-битового типа

atomic_t
для аппаратной платформы SPARC в старой реализации

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

atomic_t
, который позволяет хранить полноценное 32-разрядное целое число, и указанного ограничения больше не существует. Тем не менее старая 24-битовая реализация все еще используется в старом коде для аппаратной платформы SPARC, и этот код все еще имеется в файле
<asm/atomic.h>
для этой аппаратной платформы.

Поделиться:
Популярные книги

Курсант: Назад в СССР 10

Дамиров Рафаэль
10. Курсант
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Курсант: Назад в СССР 10

Вернуть невесту. Ловушка для попаданки 2

Ардова Алиса
2. Вернуть невесту
Любовные романы:
любовно-фантастические романы
7.88
рейтинг книги
Вернуть невесту. Ловушка для попаданки 2

Законы Рода. Том 3

Flow Ascold
3. Граф Берестьев
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 3

Любимая учительница

Зайцева Мария
1. совершенная любовь
Любовные романы:
современные любовные романы
эро литература
8.73
рейтинг книги
Любимая учительница

Девятый

Каменистый Артем
1. Девятый
Фантастика:
боевая фантастика
попаданцы
9.15
рейтинг книги
Девятый

Вперед в прошлое 2

Ратманов Денис
2. Вперед в прошлое
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Вперед в прошлое 2

Калибр Личности 1

Голд Джон
1. Калибр Личности
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Калибр Личности 1

Совпадений нет

Безрукова Елена
Любовные романы:
любовно-фантастические романы
5.50
рейтинг книги
Совпадений нет

Кодекс Крови. Книга Х

Борзых М.
10. РОС: Кодекс Крови
Фантастика:
фэнтези
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга Х

Авиатор: назад в СССР 14

Дорин Михаил
14. Покоряя небо
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Авиатор: назад в СССР 14

Последний Паладин. Том 4

Саваровский Роман
4. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин. Том 4

Не грози Дубровскому!

Панарин Антон
1. РОС: Не грози Дубровскому!
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Не грози Дубровскому!

Кодекс Охотника. Книга XXIV

Винокуров Юрий
24. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XXIV

Средневековая история. Тетралогия

Гончарова Галина Дмитриевна
Средневековая история
Фантастика:
фэнтези
попаданцы
9.16
рейтинг книги
Средневековая история. Тетралогия