Файл: А. В. Гордеев А. Ю. Молчанов системное программное обеспечение электронный вариант книги издательства Питер СанктПетербург Челябинск юургу каф. Автоматика и управление 2002 2 Предисловие Настоящий учебник.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 12.01.2024
Просмотров: 1062
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
29
кие длительные операции, оформлять в виде самостоятельных «подпроцессов»
(легковесных или облегченных процессов – потоков, можно также воспользоваться термином задача), которые будут выполняться параллельно с другими «подпро- цессами» (потоками, задачами), то у пользователя появляется возможность парал- лельно выполнять несколько операций в рамках одного приложения (процесса).
Легковесными эти задачи называют потому, что операционная система не должна для них организовывать полноценную виртуальную машину. Эти задачи не имеют своих собственных ресурсов, они развиваются в том же виртуальном адресном пространстве, могут пользоваться теми же файлами, виртуальными устройствами и иными ресурсами, что и данный процесс. Единственное, что им необходимо иметь,
– это процессорный ресурс. В однопроцессорной системе треды (задачи) разделяют между собой процессорное время так же, как это делают обычные процессы, а в мультипроцессорной системе могут выполняться одновременно, если не встречают конкуренции из-за обращения к иным ресурсам.
Главное, что обеспечивает многопоточность, – это возможность параллельно выполнять несколько видов операций в одной прикладной программе. Параллель- ные вычисления (а, следовательно, и более эффективное использование ресурсов центрального процессора, и меньшее суммарное время выполнения задач) теперь уже часто реализуется на уровне тредов, и программа, оформленная в виде не- скольких тредов в рамках одного процесса, может быть выполнена быстрее за счёт параллельного выполнения её отдельных частей. Например, если электронная таб- лица или текстовый процессор были разработаны с учётом возможностей многопо- точной обработки, то пользователь может запросить пересчёт своего рабочего лис- та или слияние нескольких документов и одновременно продолжать заполнять таб- лицу или открывать для редактирования следующий документ.
Особенно эффективно можно использовать многопоточность для выполнения распределённых приложений; например, многопоточный сервер может параллель- но выполнять запросы сразу нескольких клиентов. Как известно, операционная система OS/2 одной из первых среди ОС, используемых на ПК, ввела многопоточ- ность. В середине девяностых годов для этой ОС было создано очень большое ко-
30
личество приложений, в которых использование механизмов многопоточной обра- ботки реально приводило к существенно большей скорости выполнения вычисле- ний.
Итак, сущность «поток» была введена для того, чтобы именно с помощью этих единиц распределять процессорное время между возможными работами. Сущ- ность «процесс» предполагает, что при диспетчеризации нужно учитывать все ре- сурсы, закрепленные за ним. А при манипулировании тредами можно менять толь- ко контекст задачи, если мы переключаемся с одной задачи на другую в рамках од- ного процесса. Все остальные вычислительные ресурсы при этом не затрагиваются.
Каждый процесс всегда состоит, по крайней мере, из одного потока, и только если имеется внутренний параллелизм, программист может «расщепить» один тред на несколько параллельных.
Потребность в потоках (threads) возникла ещё на однопроцессорных вычисли- тельных системах, поскольку они позволяют организовать вычисления более эф- фективно. Для использования достоинств многопроцессорных систем с общей па- мятью треды уже просто необходимы, так как позволяют не только реально уско- рить выполнение тех задач, которые допускают их естественное распаралле- ливание, но и загрузить процессорные элементы работой, чтобы они не простаи- вали. Заметим, однако, что желательно, чтобы можно было уменьшить взаимо- действие тредов между собой, ибо ускорение от одновременного выполнения па- раллельных потоков может быть сведено к минимуму из-за задержек синхро- низации и обмена данными.
Каждый тред выполняется строго последовательно и имеет свой собственный программный счётчик и стек. Треды, как и процессы, могут порождать треды-по- томки, поскольку любой процесс состоит, по крайней мере, из одного треда. По- добно традиционным процессам (то есть процессам, состоящим из одного треда),
каждый тред может находится в одном из активных состояний. Пока один тред за- блокирован (или просто находится в очерёди готовых к исполнению задач), другой тред того же процесса может выполняться. Треды разделяют процессорное время
31
так же, как это делают обычные процессы, в соответствии с различными варианта- ми диспетчеризации.
Как мы уже знаем, все треды имеют одно и то же виртуальное адресное про- странство своего процесса. Это означает, что они разделяют одни и те же гло- бальные переменные. Поскольку каждый тред может иметь доступ к каждому вир- туальному адресу, один тред может использовать стек другого треда. Между пото- ками нет полной защиты, так как это, во-первых, невозможно, а во-вторых, не нужно. Все потоки одного процесса всегда решают общую задачу одного поль-
зователя, и механизм потоков используется здесь для более быстрого решения за-
дачи путем её распараллеливания. При этом программисту очень важно получить в свое распоряжение удобные средства организации взаимодействия частей одной программы. Повторим, что кроме разделения адресного пространства, все треды разделяют также набор открытых файлов, используют общие устройства, выделен- ные процессу, имеют одни и те же наборы сигналов, семафоры и т. п. А что у тре- дов будет их собственным? Собственными являются программный счетчик, стек,
рабочие регистры процессора, потоки-потомки, состояние.
Вследствие того, что треды, относящиеся к одному процессу, выполняются в одном и том же виртуальном адресном пространстве, между ними легко организо- вать тесное взаимодействие, в отличие от процессов, для которых нужны специ- альные механизмы обмена сообщениями и данными. Более того, программист, соз- дающий многопоточное приложение, может заранее продумать работу множества тредов процесса таким образом, чтобы они могли взаимодействовать наиболее вы- годным способом, а не участвовать в конкуренции за предоставление ресурсов то- гда, когда этого можно избежать.
Для того чтобы можно было эффективно организовать параллельное выполне- ние рассмотренных сущностей (процессов и тредов), в архитектуру современных процессоров включена возможность работать со специальной информационной структурой, описывающей ту или иную сущность. Для этого уже на уровне архи- тектуры микропроцессора используется понятие «задача» (task). Оно как бы объе- диняет в себе обычный и «легковесный» процессы. Это понятие и поддерживаемая
32
для него на уровне аппаратуры информационная структура позволяют в дальней- шем при разработке операционной системы построить соответствующие дескрип- торы, как для процесса, так и для треда. Отличаться эти дескрипторы будут, преж- де всего, тем, что дескриптор треда может хранить только контекст приостанов- ленного вычислительного процесса, тогда как дескриптор процесса (process) дол- жен уже содержать поля, описывающие тем или иным способом ресурсы, выде- ленные этому процессу. Другими словами, тот же task state segment (сегмент со- стояния задачи), подробно рассмотренный в разделе «Адресация в 32-разрядных микропроцессорах i80x86 при работе в защищённом режиме» главы 3, использует- ся как основа для дескриптора процесса. Каждый тред (в случае использования так называемой «плоской» модели памяти – см. раздел «Поддержка страничного спо- соба организации виртуальной памяти», глава 3 – может быть оформлен в виде са- мостоятельного сегмента, что приводит к тому, что простая (не многопоточная)
программа будет иметь всего один сегмент кода в виртуальном адресном про- странстве.
В завершение можно привести несколько советов по использованию потоков при создании приложений, заимствованных из работы [55].
1 В случае использования однопроцессорной системы множество параллель- ных потоков часто не ускоряет работу приложения, поскольку в каждый отдельно взятый промежуток времени возможно выполнение только одного потока. Кроме того, чем больше у вас потоков, тем больше нагрузка на систему, потраченная на переключение между ними. Если ваш проект имеет более двух постоянно рабо- тающих потоков, то такая мультизадачность не сделает программу быстрее, если каждый из потоков не будет требовать частого ввода/ вывода.
2 Вначале нужно понять, для чего необходим поток. Поток, осуществляющий обработку, может помешать системе быстро реагировать на запросы ввода/ вывода.
Потоки позволяют программе отзываться на просьбы пользователя и устройств, но при этом сильно загружать процессор. Потоки позволяют компьютеру одновре- менно обслуживать множество устройств, и созданный вами поток, отвечающий за обработку специфического устройства, в качестве минимума может потребовать
33
столько времени, сколько системе необходимо для обработки запросов всех уст- ройств.
3 Потокам можно назначить определенный приоритет для того, чтобы наиме- нее значимые процессы выполнялись в фоновом режиме. Это путь честного разде- ления ресурсов CPU
1
. Однако необходимо осознать тот факт, что процессор один на всех, а потоков много. Если в вашей программе главная процедура передаёт не- что для обработки в низкоприоритетный поток, то сама программа становится про- сто неуправляемой.
4 Потоки хорошо работают, когда они независимы. Но они начинают работать непродуктивно, если вынуждены часто синхронизироваться для доступа к общим ресурсам. Блокировка и критические секции отнюдь не увеличивают скорость ра- боты системы, хотя без использования этих механизмов взаимодействующие вы- числения организовывать нельзя.
5 Помните, что память виртуальна. Механизм виртуальной памяти (см. раздел
«Память и отображения, виртуальное адресное пространство», глава 2) следит за тем, какая часть виртуального адресного пространства должна находиться в опера- тивной памяти, а какая должна быть сброшена в файл подкачки. Потоки усложня- ют ситуацию, если они обращаются в одно и то же время к разным адресам вирту- ального адресного пространства приложения. Это значительно увеличивает на- грузку на систему, особенно при небольшом объёме кэш-памяти. Помните, что ре- ально память не всегда «свободна», как это пишут в информационных «окошках»
«О системе». Всегда отождествляйте доступ к памяти с доступом к файлу на диске и создавайте приложение с учётом вышесказанного.
6 Всякий раз, когда какой-либо из ваших потоков пытается воспользоваться общим ресурсом вычислительного процесса, которому он принадлежит, вы обяза- ны тем или иным образом легализовать и защитить свою деятельность. Хорошим средством для этого являются критические секции, семафоры и очерёди сообще- ний. Если вы протестировали свое приложение и не обнаружили ошибок синхро- низации, то это ещё не значит, что их там нет. Пользователь может создать самые
1
CPU (central processing unit) – центральное обрабатывающее устройство или просто процессор.
34
непредсказуемые ситуации. Это очень ответственный момент в разработке много- поточных приложений.
7 Не возлагайте на поток несколько функций. Сложные функциональные от- ношения затрудняют понимание общей структуры приложения, его алгоритм. Чем проще и менее многозначна каждая из рассматриваемых ситуаций, тем больше ве- роятность, что ошибок удастся избежать.
1 2 3 4 5 6 7 8 9 ... 37
Прерывания
Прерывания представляют собой механизм, позволяющий координировать па- раллельное функционирование отдельных устройств вычислительной системы и реагировать на особые состояния, возникающие при работе процессора. Таким об- разом, прерывание – это принудительная передача управления от выполняемой
программы к системе (а через неё – к соответствующей программе обработки
прерывания), происходящая при возникновении определенного события.
Идея прерываний была предложена в середине 50-х годов и можно без пре- увеличения сказать, что она внесла наиболее весомый вклад в развитие вычисли- тельной техники. Основная цель введения прерываний – реализация асинхронного режима работы и распараллеливание работы отдельных устройств вычислитель- ного комплекса.
Механизм прерываний реализуется аппаратно-программными средствами.
Структуры систем прерывания (в зависимости от аппаратной архитектуры) могут быть самыми разными, но все они имеют одну общую особенность – прерывание непременно влечет за собой изменение порядка выполнения команд процессором.
Механизм обработки прерываний независимо от архитектуры вычислительной системы включает следующие элементы:
1 Установление факта прерывания (прием сигнала на прерывание) и иденти- фикация прерывания (в операционных системах иногда осуществляется повторно,
на шаге 4).
2 Запоминание состояния прерванного процесса. Состояние процесса опреде- ляется, прежде всего, значением счетчика команд (адресом следующей команды,
который, например, в i80x86 определяется регистрами CS и IP – указателем коман-
35
ды [2, 22, 84]), содержимым регистров процессора и может включать также специ- фикацию режима (например, режим пользовательский или привилегированный) и другую информацию.
3 Управление аппаратно передаётся подпрограмме обработки прерывания. В
простейшем случае в счётчик команд заносится начальный адрес подпрограммы обработки прерываний, а в соответствующие регистры – информация из слова со- стояния. В более развитых процессорах, например в том же i80286 и последующих
32-битовых микропроцессорах, начиная с i80386, осуществляется достаточно сложная процедура определения начального адреса соответствующей подпрограм- мы обработки прерывания и не менее сложная процедура инициализации рабочих регистров процессора (см. раздел «Система прерываний 32-разрядных микропро- цессоров i80x86», глава 3).
4 Сохранение информации о прерванной программе, которую не удалось спа- сти на шаге 2 с помощью действий аппаратуры. В некоторых вычислительных сис- темах предусматривается запоминание довольно большого объёма информации о состоянии прерванного процесса.
5 Обработка прерывания. Эта работа может быть выполнена той же подпро- граммой, которой было передано управление на шаге 3, но в ОС чаще всего она реализуется путем последующего вызова соответствующей подпрограммы.
6 Восстановление информации, относящейся к прерванному процессу (этап,
обратный шагу 4).
7. Возврат в прерванную программу.
Шаги 1-3 реализуются аппаратно, а шаги 4-7 – программно.
На рис. 1.4 показано, что при возникновении запроса на прерывание естест- венный ход вычислений нарушается и управление передаётся программе обработ- ки возникшего прерывания. При этом средствами аппаратуры сохраняется (как правило, с помощью механизмов стековой памяти) адрес той команды, с которой следует продолжить выполнение прерванной программы. После выполнения про- граммы обработки прерывания управление возвращается прерванной ранее про- грамме посредством занесения в указатель команд сохранённого адреса команды.
36
Однако такая схема используется только в самых простых программных средах. В
мультипрограммных операционных системах обработка прерываний происходит по более сложным схемам, о чём будет более подробно написано ниже.
Рис. 1.4. Обработка прерывания
Итак, главные функции механизма прерываний:
♦ распознавание или классификация прерываний;
♦ передача управления соответственно обработчику прерываний;
♦ корректное возвращение к прерванной программе.
Переход от прерываемой программы к обработчику и обратно должен выпол- няться как можно быстрей. Одним из быстрых методов является использование таблицы, содержащей перечень всех допустимых для компьютера прерываний и адреса соответствующих обработчиков. Для корректного возвращения к пре- рванной программе перед передачей управления обработчику прерываний со- держимое регистров процессора запоминается либо в памяти с прямым доступом,
либо в системном стеке – system stack.
Прерывание
Исполняемая программа
Отключение прерываний,
сохранение контекста прерванной программы,
установка режима работы системы прерываний
Подпрограмма обработки прерывания
Собственно тело программы обработки прерывания
Восстановление контекста прерванной ранее программы,
установка прежнего режима работы системы прерываний
37
Прерывания, возникающие при работе вычислительной системы, можно раз- делить на два основных класса: внешние (их иногда называют асинхронными) и внутренние (синхронные).
Внешние прерывания вызываются асинхронными событиями, которые проис- ходят вне прерываемого процесса, например:
♦ прерывания от таймера;
♦ прерывания от внешних устройств (прерывания по вводу/выводу);
♦ прерывания по нарушению питания;
♦ прерывания с пульта оператора вычислительной системы;
♦ прерывания от другого процессора или другой вычислительной системы.
Внутренние прерывания вызываются событиями, которые связаны с работой процессора и являются синхронными с его операциями. Примерами являются сле- дующие запросы на прерывания:
♦ при нарушении адресации (в адресной части выполняемой команды указан запрещённый или несуществующий адрес, обращение к отсутствующему сегменту или странице при организации механизмов виртуальной памяти);
♦ при наличии в поле кода операции незадействованной двоичной комбина- ции;
♦ при делении на нуль;
♦ при переполнении или исчезновении порядка;
♦ при обнаружении ошибок чётности, ошибок в работе различных устройств аппаратуры средствами контроля.
Могут ещё существовать прерывания при обращении к супервизору ОС – в не- которых компьютерах часть команд может использовать только ОС, а не пользо- ватели. Соответственно в аппаратуре предусмотрены различные режимы работы, и пользовательские программы выполняются в режиме, в котором эти привиле- гированные команды не исполняются. При попытке использовать команду, запре- щённую в данном режиме, происходит внутреннее прерывание и управление пере- даётся супервизору ОС. К привилегированным командам относятся и команды пе- реключения режима работа центрального процессора.