Основные операции с потоками
1. Создание нового потока: pthread_cteate в UNIX, CreateThread в Windows. Основным параметром этих функций является имя процедуры, которую необходимо запустить в новом потоке. В Windows имеется также функция CreateRemoteThread, позволяющая создать поток в другом процессе.
2. Прекращение работы потока. Завершить поток можно изнутри самого потока функцией pthread_exit в UNIX, ExitThread в Windows. После этого поток исчезает и уже не рассматривается планировщиком. Завершить поток можно также из другого потока при помощи функции pthread_cancel в UNIX, TerminateThread в Windows.
3. Ожидание потоком завершения другого определенного потока выполняется при помощи функции pthread_join в UNIX, WaitForSingleObject и WaitForMultipleObject в Windows.
4. Поток может добровольно уступить свою очередь другому потоку при помощи функций pthread_yield в UNIX, SwitchToThread и SuspendThread в Windows.
Способы реализации потоков
В пространстве пользователя
Пакет поддержки потоков целиком размещается в пространстве пользователя (рис. 6.1, а).
Ядро ОС ничего не знает о потоках и управляет обычными, однопоточными процессами. Каждый процесс имеет собственную таблицу потоков. Когда поток собирается выполнить действие, которое может привести к локальной блокировке, он вызывает процедуру из пакета поддержки потоков. Процедура сохраняет состояние потока в таблице потоков, ищет в таблице поток, готовый к запуску, и загружает в регистры его состояние.
Реализация потоков в пространстве пользователя обеспечивает следующие преимущества.
1. Можно реализовать в ОС, не поддерживающей потоки.
2. Переключение потоков происходит на порядок быстрее, чем в режиме ядра.
3. Каждый процесс может иметь свой собственный алгоритм планирования.
4. Приложения лучше масштабируются.
Проблемы, возникающие при реализации потоков в пространстве пользователя: блокирующий системный вызов в одном из потоков остановит все потоки процесса; при запуске одного потока ни один другой поток не будет запущен, пока первый поток добровольно не отдаст процессор.
В пространстве ядра
Таблица потоков располагается в ядре (рис. 6.1, б). Ядро также содержит обычную таблицу процессов. Все запросы, которые могут блокировать поток, реализуются как системные вызовы. Когда поток блокируется, ядро ОС запускает другой поток из этого же процесса либо поток из другого процесса. Недостатком такого подхода является увеличение накладных расходов при операциях с потоками из-за большой цены системных запросов.
Смешанная реализация
Ядро ОС знает только о потоках своего уровня и управляет ими (рис. 6.1, в). Некоторые из потоков ядра могут содержать по несколько потоков пользовательского уровня, которые управляются так же, как потоки в системе, не поддерживающей многопоточность. В данном подходе совмещаются достоинства первых двух методов.
Вопросы для самоконтроля
1. Назовите две различные и потенциально незавимые характеристики, содержащиеся в понятии процесса.
2. Дайте определение понятию "поток".
3. Перечислите отличия процессов от потоков.
4. Какие ресурсы обычно совместно используются всеми потоками процесса?
5. Перечислите основные операции с потоками.
6. Перечислите причины, по которым переключение потоков обходится дешевле, чем переключение процессов.
7. Перечислите способы реализации потоков по отношению к коду ОС.
8. Перечислите преимущества потоков на пользовательском уровне по сравнению с потоками на уровне ядра.
9. Может ли поток быть прерван прерыванием по таймеру? Если да, то при каких обстоятельствах? Если нет, то почему?
Тема 7. Взаимоблокировки
Основные понятия
Взаимная блокировка – блокировка группы процессов, ожидающих события, которое никогда не может наступить, т.к. событие может вызвать только другой процесс из той же группы. Взаимоблокировки возникают при доступе процессов к ресурсам. Ресурс – любой объект, который может запрашиваться и ожидаться процессом. Может состоять из любого количества идентичных единиц; процесс может запрашивать любое количество единиц ресурса. Существует две основные категории ресурсов:
Повторно используемые ресурсы не истощаются при использовании, могут использоваться вновь. Они имеют фиксированное количество единиц, которые не могут создаваться/уничтожаться. Процесс, запрашивающий такой ресурс, удерживает его при использовании, освобождает, затем ресурс может быть выделен другим процессам. Примеры повторно используемых ресурсов: процессор, УВВ, основная и вторичная память, структуры данных (файлы).
Расходуемые ресурсы могут быть созданы (произведены) и уничтожены (потреблены). Когда процесс запрашивает такой ресурс, ресурс прекращает существование. Обычно ограничений на количество расходуемых ресурсов определенного типа нет. Незаблокированный процесс-производитель может выпустить любое количество таких ресурсов. Примеры расходуемых ресурсов: прерывания, сигналы, сообщения, информация в буферах ввода-вывода.
Типы доступа к ресурсу
При монопольном (exclusive) режиме доступ к ресурсу в каждый момент времени может иметь только один процесс. При разделяемом (shared) режиме доступ к ресурсу одновременно может иметь любое число процессов. Эти режимы несовместимы, т.е. к ресурсу не может быть доступа одновременно в монопольном и разделяемом режиме. Два режима доступа имеют смысл только для повторно используемых ресурсов, т.к. потребляемые ресурсы уничтожаются после использования. Когда существует много единиц (копий) ресурса, доступ к одним из них может осуществляться в разделяемом режиме несколькими процессами, в то время как доступ к другим единицам – в монопольном режиме.
Пример 1. Взаимоблокировка с повторно используемыми ресурсами.
Процесс P1 | Процесс P2 |
Запрос ресурса A | Запрос ресурса B |
Запрос ресурса B | Запрос ресурса A |
Если оба процесса дойдут до своего второго запроса, возникнет взаимоблокировка. Один из способов избежать такой взаимоблокировки – наложить системные ограничения на порядок запроса ресурсов.
Пример 2. Взаимоблокировка с повторно используемыми ресурсами. Пусть доступно 200 КБ ОП, и выполняется следующая последовательность запросов:
Процесс P1 | Процесс P2 |
Запрос 80 КБ | Запрос 70 КБ |
Запрос 60 КБ | Запрос 80 КБ |
Если оба процесса дойдут до своего второго запроса, возникнет взаимоблокировка, несмотря на то, что в системе достаточно памяти для выполнения каждого из процессов по отдельности.
Пример 3. Взаимоблокировка с расходуемыми ресурсами. Два процесса пытаются получить сообщение от другого процесса, а затем отправить ему сообщение.
Процесс P1 | Процесс P2 |
m1 = Receive (P2); | m1 = Receive (P1) |
Send (P2, m2); | Send (P1, m2); |
Взаимоблокировка возникнет, если операция Receive является блокирующей.