Реализация понятия последовательного процесса в операционных системах
Для того чтобы операционная система могла управлять процессами, она должна располагать всей необходимой для этого информацией. С этой целью на каждый процесс заводится специальная информационная структура, называемая дескриптором процесса (описателем задачи, блоком управления задачей). В общем случае дескриптор процесса, как правило, содержит следующую информацию:
- идентификатор процесса (Process Identifier, PID);
- тип (или класс) процесса, который определяет для супервизора некоторые правила предоставления ресурсов;
- приоритет процесса, в соответствии с которым супервизор предоставляет ресурсы (в рамках одного класса процессов в первую очередь обслуживаются более приоритетные процессы);
- переменную состояния, которая определяет, в каком состоянии находится процесс (готов к работе, выполняется, ожидает устройства ввода-вывода и т. д.);
- контекст задачи, то есть защищенную область памяти (или адрес такой области), в которой хранятся текущие значения регистров процессора, когда процесс прерывается, не закончив работы;
- информацию о ресурсах, которыми процесс владеет и/или имеет право пользоваться (указатели на открытые файлы, информация о незавершенных операциях ввода-вывода и др.);
- место (или его адрес) для организации общения с другими процессами;
Понятия вычислительного процесса и ресурса_______________________________ 35
- параметры времени запуска (момент времени, когда процесс должен активизироваться, и периодичность этой процедуры);
- в случае отсутствия системы управления файлами адрес задачи на диске в ее исходном состоянии и адрес па диске, куда она выгружается из оперативной памяти, если ее вытесняет другая задача (последнее справедливо для диск-резидент -ных задач, которые постоянно находятся во внешней памяти на системном магнитном диске и загружаются в оперативную память только на время выполнения).
Описатели задач, как правило, постоянно располагаются в оперативной памяти с целью ускорить работу супервизора, который организует их в списки (очереди) и отображает изменение состояния процесса перемещением соответствующего описателя из одного списка в другой. Для каждого состояния (за исключением состояния выполнения для однопроцессорной системы) операционная система ведет соответствующий список задач, находящихся в этом состоянии. Однако для состояния ожидания обычно имеется не один список, а столько, сколько различных видов ресурсов могут вызывать состояние ожидания. Например, состояний ожидания завершения операции ввода-вывода может быть столько, сколько устройств ввода-вывода имеется в системе.
В некоторых операционных системах количество описателей определяется жестко и заранее (на этапе генерации варианта операционной системы или в конфигурационном файле, который используется при загрузке ОС), в других по мере необходимости система может выделять участки памяти под новые описатели. Например, в уже мало кому известной системе OS/2, которая несколько лет тому назад многими специалистами считалась одной из лучших ОС для персональных компьютеров, максимально возможное количество описателей задач указывается в конфигурационном файле CONFIG.SYS. Например, строка THREADS=1024 в файле CONFIG.SYS означает, что всего в системе может параллельно существовать и выполняться до 1024 задач, включая вычислительные процессы и их потоки.
В ныне широко распространенных системах Windows NT/2000/XP количество описателей нигде в явном виде не задается. Это переменная величина, и она определяется самой операционной системой. Однако посмотреть на текущее количество таких описателей можно. Если, работая в Windows NT/2000/XP, нажать одновременно комбинацию клавиш Ctrl+Shift+Esc, появится окно Диспетчера задач. На вкладке Быстродействие этой программы мы увидим поле с названием Всего дескрипторов и соответствующее число. Тут же указывается количество дескрипторов для управления потоками (задачами) и число полноценных вычислительных процессов. Более подробно о процессах и потоках см. далее.
В операционных системах реального времени чаще всего количество процессов фиксируется, и, следовательно, целесообразно заранее определять (на этапе генерации или конфигурирования ОС) количество дескрипторов. Для использования таких операционных систем в качестве систем общего назначения (что нынче уже нехарактерно)' обычно количество дескрипторов бралось с некоторым запасом,
1 В недалеком прошлом достаточно часто и качестве вычислительных систем общего назначения приобретались мини-ЭВМ и устанавливали на них ОС реального времени.
36_____________________________________________ Глава 1. Основные понятия
и появление новой задачи связывалось с заполнением этой информационной структуры. Поскольку дескрипторы процессов постоянно располагаются в оперативной памяти (с целью ускорить работу диспетчера), то их количество не должно быть очень большим.
Для более эффективной обработки данных в системах реального времени целесообразно иметь постоянные задачи, полностью или частично всегда существующие в системе независимо от того, поступило на них требование или нет. Каждая постоянная задача обладает некоторой собственной областью оперативной памяти (ОЗУ-резидентная задача, или просто резидентная задача) независимо от того, выполняется задача в данный момент или нет. Эта область, в частности, может использоваться для хранения данных, полученных задачей ранее. Данные могут храниться в ней и тогда, когда задача находится в состоянии ожидания или даже в состоянии бездействия.
Для аппаратной поддержки работы операционных систем с этими информационными структурами (дескрипторами задач) в процессорах могут быть реализованы соответствующие механизмы. Так, например, в микропроцессорах Intel 80x86 (см. главу 4) имеется специальный регистр TR (Task Register), указывающий местонахождение специальной информационной структуры — сегмента состояния задачи (Task State Segment, TSS), в котором при переключении с задачи на задачу автоматически сохраняется содержимое регистров процессора [1,8, 48].
Поскольку между терминами «процесс» и «задача» со временем появилось существенное различие, мы сейчас подробно рассмотрим этот вопрос.
Процессы и задачи
Хотя понятия мультипрограммного и мультизадачного режимов работы достаточно близки, это все-таки не одно и то же. К сожалению, здесь до сих пор имеется некоторая путаница. Основные причины тому — не только то, что терминология еще не устоялась и что многие фирмы-разработчики по-разному предпочитали называть одни и те же сущности, но и сложность, неоднозначность ситуации.
Мультипрограммный режим предполагает, что операционная система организует параллельное выполнение нескольких вычислительных процессов на одном компьютере. И каждый вычислительный процесс может, в принципе, никак не зависеть от другого вычислительного процесса. Разве что они могут задержать выполнение друг друга из-за необходимости поочередно разделять ресурсы или сильно задерживать выполнение друг друга при владении неразделяемым ресурсом. У них может не быть ни общих файлов, ни общих переменных. Они вообще могут принадлежать разным пользователям. Просто эти процессы, с позиций внешнего наблюдателя, выполняются на одном и том же компьютере в одно и то же время. Хотя могут выполняться и в разное время, и на разных компьютерах. Главное — это то, что мультипрограммный режим обеспечивает для этих процессов их независимость. Каждому процессу операционная система выделяет затребованные ресурсы, он выполняется как бы на отдельной виртуальной машине. Средства защиты системы должны обеспечить невмешательство одного вычислительного процесса в другой вычислительный процесс. И если такую защиту обеспечить невозможно,
Понятия вычислительного процесса и ресурса_______________________________ 37
то система не может считаться надежной. Немало методов и конкретных способов было придумано разработчиками для обеспечения надежных вычислений и предотвращения возможности намеренно или по ошибке повлиять на результаты вычислений в другом процессе.
Однако существует и другая потребность: не разделить вычислительные процессы друг от друга, а наоборот совместить их, обеспечить возможность тесного взаимодействия между осуществляемыми вычислениями. Например, результаты вычислений одного вычислительного процесса могут требоваться для начала или продолжения работы другого. Существует огромное количество ситуаций, когда необходимо обеспечить активное взаимодействие между выполняющимися вычислительными процессами. Если нет возможности получить доступ к переменным другого процесса, ибо операционная система построена надежно и защищает адресные пространства одного вычислительного процесса от вмешательства другого вычислительного процесса, то возникают очень серьезные препятствия на пути передачи каких бы то ни было данных между процессами.
Термин мультизадачный режим работы стали применять как раз для тех случаев, когда необходимо обеспечить взаимодействие между вычислениями. Мультизадачный режим означает, что операционная система позволяет организовать параллельное выполнение вычислений, и имеются специальные механизмы для передачи данных, синхросигналов, каких-либо сообщений между этими взаимодействующими вычислениями. Это можно сделать за счет того, что такие вычисления не должны системой изолироваться друг от друга. Операционная система не должна для них в обязательном порядке задействовать все механизмы защиты вычислений от невмешательства друг в друга. При мультизадачном режиме разработчик программы должен позаботиться о разделении ресурсов между его задачами. Операционная система будет всего лишь разделять процессорное время между задачами.
Понятие процесса было введено для реализации идей мультипрограммирования. Термин задача тоже, к сожалению, в большинстве случаев применялся для того же. В свое время различали термины «мультизадачность» и «мультипрограммирование», но потом они стали заменять друг друга, и это вносило немалую путаницу. Таким образом, для реализации мультизадачности в ее исходном толковании необходимо было ввести соответствующую сущность. Такой сущностью стали легковесные (thin) процессы, или, как их теперь преимущественно называют, потоки выполнения, нити, или треды (threads).
Когда говорят о процессах (process), то тем самым хотят отметить, что операционная система поддерживает их обособленность: у каждого процесса имеется свое виртуальное адресное пространство, каждому процессу назначаются свои ресурсы — файлы, окна, семафоры и т. д. Такая обособленность нужна для того, чтобы защитить один процесс от другого, поскольку они, совместно используя все ресурсы вычислительной системы, конкурируют друг с другом за доступ к ресурсам. В общем случае процессы просто никак не связаны между собой и могут принадлежать даже разным пользователям, разделяющим одну вычислительную систему.
' Поток выполнения (thread) не следует путать с потоком данных (stream).
38_____________________________________________ Глава 1. Основные понятия
Другими словами, в случае процессов операционная система считает их совершенно несвязанными и независимыми. При этом именно операционная система берет на себя роль арбитра в спорах конкурирующих процессов за ресурсы. Она же и обеспечивает защиту выполняющихся вычислений.
Однако желательно иметь еще и возможность задействовать внутренний параллелизм, который может быть в самих процессах. Такой внутренний параллелизм встречается достаточно часто и позволяет ускорить вычисления. Например, некоторые операции, выполняемые приложением, могут требовать для своего исполнения достаточно длительное использование центрального процессора. В этом случае при интерактивной работе с приложением пользователь вынужден долго ожидать завершения заказанной операции и не может управлять приложением до тех пор, пока операция не выполнится до самого конца. Такие ситуации встречаются достаточно часто, например, при работе с графическими редакторами при обработке больших изображений с высокой степенью детализации. Если же программные модули, исполняющие такие длительные операции, оформлять в виде самостоятельных «подпроцессов» (легковесных процессов, потоков выполнения, или задач), которые могут выполняться параллельно с другими подпроцессами (потоками, задачами), то у пользователя появляется возможность параллельно выполнять несколько операций в рамках одного приложения (процесса). Легковесными эти процессы называют потому, что операционная система не должна для них организовывать полноценную виртуальную машину, то есть эти задачи, прежде всего, не имеют своих собственных ресурсов, а развиваются в том же виртуальном адресном пространстве, могут пользоваться теми же файлами, виртуальными устройствами и иными ресурсами, выделенными ОС данному процессу. Единственное, что они имеют свое — это процессорный ресурс. В однопроцессорной системе потоки выполнения (задачи) разделяют между собой процессорное время так же, как это делают обычные процессы, а в мультипроцессорной системе они могут выполняться одновременно, если не встречают конкуренции из-за обращения к иным ресурсам.
Главное, что обеспечивает многопоточность — это возможность параллельно выполнять несколько видов операций в одной прикладной программе. Параллельные вычисления (а следовательно, и более эффективное использование ресурсов центрального процессора, и меньшее суммарное время выполнения задач) гораздо удобнее реализовать не на уровне процессов, но на уровне задач (потоков, тредов). И программа, разработанная с использованием механизма потоков, представляемая как некоторое множество задач в рамках одного процесса, может быть выполнена быстрее за счет параллельного функционирования ее отдельных частей. Особенно это выгодно при наличии нескольких процессоров, ибо каждая задача может выполняться на отдельном процессоре. Например, если электронная таблица, текстовый процессор или графический редактор были разработаны с учетом возможностей многопоточной обработки, то пользователь может запросить пересчет своего рабочего листа, слияние нескольких документов или преобразование изображения и одновременно продолжать заполнять таблицу, открывать для редактирования следующий документ, изменять другое изображение.
Понятия вычислительного процесса и ресурса_______________________________ 39
Особенно эффективно можно использовать многопоточность для выполнения распределенных приложений. Например, многопоточный сервер может параллельно выполнять запросы сразу нескольких клиентов. Как известно, операционная система OS/2 была одной из первых систем, используемых в персональных компьютерах, которая поддерживала многопоточность. В середине 90-х годов для этой операционной системы было создано большое количество приложений, в которых наличие механизмов многопоточной обработки реально приводило к существенному повышению скорости вычислений. Для систем Windows, с которыми мы все имеем дело, ярко выраженной многопоточностью обладают такие продукты, как SQL Server, Oracle. И хотя те же Word, Excel, Internet Explorer также при своей работе образуют потоки, явного параллелизма в этих программах почти не поддерживается. Поэтому при увеличении числа процессоров в компьютере такие программы не начинают выполняться быстрее.
Итак, сущность «поток выполнения» была введена для того, чтобы именно с помощью этих единиц распределять процессорное время между возможными работами. Сущность «процесс» предполагает, что при диспетчеризации нужно учитывать все ресурсы, закрепленные за ним. При манипулировании задачами-потоками можно менять только контекст задачи, если мы переключаемся с одной задачи на другую в рамках одного процесса. Все остальные вычислительные ресурсы при этом не затрагиваются. Каждый процесс всегда состоит, по крайней мере, из одного потока выполнения, и только если имеется внутренний параллелизм, программист может «расщепить» один поток на несколько параллельных. Потребность в потоках возникла еще в однопроцессорных вычислительных системах, поскольку они позволяли организовать вычисления более эффективно. Для использования достоинств многопроцессорных систем с общей памятью потоки уже просто необходимы, так как позволяют не только реально ускорить выполнение тех задач, которые допускают их естественное распараллеливание, но и загрузить процессорные элементы работой, с тем чтобы они не простаивали. Заметим, однако, что желательно, чтобы можно было свести к минимуму взаимодействие потоков между собой, ибо ускорение от одновременного выполнения параллельных потоков может быть сведено к минимуму из-за задержек синхронизации и обмена данными.
Каждый поток выполняется строго последовательно и имеет свой собственный программный счетчик и стек. Потоки, как и процессы, могут порождать потоки-потомки, поскольку любой процесс состоит по крайней мере из одного потока. Подобно традиционным процессам (то есть процессам, состоящим из одного потока), каждый поток может находиться в одном из активных состояний. Пока один поток заблокирован (или просто находится в очереди готовых к исполнению задач), другой поток того же процесса может выполняться. Потоки разделяют процессорное время так же, как это делают обычные процессы, в соответствии с различными вариантами диспетчеризации.
Как уже упоминалось, иногда потоки выполнения называют легковесными процессами. Как мы уже знаем, все потоки имеют одно и то же виртуальное адресное пространство своего процесса. Это означает, что они разделяют одни и те же глобальные переменные. Поскольку каждый поток может иметь доступ к каждому
40_____________________________________________ Глава 1. Основные понятия
виртуальному адресу, один поток может использовать стек другого потока. Между потоками нет полной защиты, потому что, во-первых, это невозможно, а во-вторых, не нужно. Все потоки одного процесса всегда решают общую задачу одного пользователя, и механизм потоков используется здесь для более быстрого решения задачи путем ее распараллеливания. При этом программисту очень важно получить в свое распоряжение удобные средства организации взаимодействия частей одной программы. Повторим, что кроме разделения адресного пространства, все потоки разделяют также набор открытых файлов, устройства, выделенные процессу, имеют одни и те же наборы сигналов, семафоры и т. п. А что у потоков является их собственным? Собственными являются программный счетчик, стек, рабочие регистры процессора, потоки-потомки, состояние.
Вследствие того, что потоки, относящиеся к одному процессу, выполняются в одном и том же виртуальном адресном пространстве, между ними легко организовать тесное взаимодействие, в отличие от процессов, для которых нужны специальные механизмы обмена сообщениями и данными. Более того, программист, создающий многопоточное приложение, может заранее продумать работу множества потоков процесса таким образом, чтобы они могли взаимодействовать наиболее выгодным способом, а не конкурировать за ресурсы тогда, когда этого можно избежать.
Для того чтобы можно было эффективно организовать параллельное выполнение рассмотренных сущностей (процессов и потоков), в архитектуру современных процессоров включены средства для работы со специальной информационной структурой, описывающей ту или иную сущность. Для этого уже на уровне архитектуры микропроцессора используется понятие задача (task). Оно как бы объединяет в себе и обычный процесс, и поток выполнения (тред). Это понятие и поддерживаемая для него на уровне аппаратуры информационная структура позволяют в дальнейшем при разработке операционной системы строить соответствующие дескрипторы как для задач, так и для процессов. И отличаться эти дескрипторы будут прежде всего тем, что дескриптор задачи может хранить только контекст приостановленного вычислительного процесса, тогда как дескриптор процесса должен содержать поля, описывающие тем или иным способом ресурсы, выделенные этому процессу. Для хранения контекста задачи в микропроцессорах с архитектурой i32 имеется специальный сегмент состояния задачи (Task State Segment, TSS). А для отображения информации о процессе используется уже иная информационная структура, однако она включает в себя TSS. Другими словами, сегмент состояния задачи, подробно рассматриваемый в разделе «Адресация в 32-разрядных микропроцессорах i80x86 при работе в защищенном режиме» главы 4, используется как основа для дескриптора процесса. Таким образом, дескриптор процесса больше по размеру, чем TSS, и включает в себя такие традиционные поля, как идентификатор процесса, его имя, тип, приоритет и проч.
Каждый поток (в случае использования так называемой «плоской» модели памяти — см. раздел «Сегментная, страничная и сегментно-страничная организация памяти» в главе 3) может быть оформлен в виде самостоятельного сегмента, что приводит к тому, что простая (не многопоточная) программа будет иметь всего один сегмент кода в виртуальном адресном пространстве.
Понятия вычислительного процесса и ресурса_______________________________ 41
Теперь, если вернуться к уже упомянутому файлу CONFIG.SYS, в котором для операционной системы OS/2 указываются наиболее важные параметры, определяющие ее работу, стоит заметить, что в этом файле строка THREADS=1024 указывает на количество не процессов, а именно задач. И под задачей в данном случае понимается как процесс, так и поток этого процесса.
К большому сожалению, практически невозможно использовать термины «задача» и «процесс» с однозначным толкованием, чтобы под задачей обязательно понимать поток, в то время как термин «процесс» означал бы множество потоков. Значение этих терминов по-прежнему сильно зависит от контекста. И это характерно практически для каждой книги, в том числе и для учебной литературы. Грешен этим и автор. Остается надеяться, что со временем все же ситуация изменится, и толкование этих слов будет более четким и строгим.
В завершение можно привести несколько советов по использованию потоков выполнения при создании приложений, заимствованных из [28].
- В случае однопроцессорной системы множество параллельных потоков часто не ускоряет работу приложения, поскольку в каждый отдельно взятый промежуток времени возможно выполнение только одного потока. Кроме того, чем больше у вас потоков, тем больше нагрузки на систему, относящиеся к переключению между ними. Мультизадачность из более двух постоянно работающих потоков в вашем проекте не сделает программу быстрее, если каждый из потоков не будет требовать частого ввода-вывода.
- Вначале нужно понять, для чего необходим поток. Поток, осуществляющий обработку, может помешать системе быстро реагировать на запросы ввода-вывода. Потоки позволяют программе отзываться на просьбы пользователя и устройств, но при этом (в том числе) сильно загружают процессор. Потоки позволяют компьютеру одновременно обслуживать множество устройств, и созданный вами поток, отвечающий за обработку специфического устройства, как минимум может потребовать столько времени, сколько системе необходимо для обработки запросов от всех устройств.
- Потокам можно назначать разные приоритеты для того, чтобы наименее значимые процессы выполнялись в фоновом режиме. Это путь честного разделения ресурсов процессора. Однако необходимо осознать тот факт, что процессор один на всех, а потоков много. Если в вашей программе главная процедура передает нечто для обработки в низкоприоритетный поток, то сама программа становится просто неуправляемой.
- Потоки хорошо работают, когда они независимы. Но они начинают работать непродуктивно, когда вынуждены часто синхронизироваться для доступа к общим ресурсам. Взаимные блокировки и критические секции отнюдь не добавляют скорости работы системы, хотя без использования этих механизмов взаимодействующие вычисления организовывать нельзя.
- Помните, что память виртуальна. Механизм виртуальной памяти (см. раздел «Память и отображения, виртуальное адресное пространство» в главе 3) следит за тем, какая часть виртуального адресного пространства должна находиться в оперативной памяти, а какая должна быть сброшена в файл подкачки.
42_____________________________________________ Глава 1. Основные понятия
Потоки усложняют ситуацию, если они обращаются в одно и то же время к разным адресам виртуального адресного пространства приложения. Это значительно увеличивает нагрузку на систему, особенно при небольшом объеме кэшпамяти. Помните, что реально память не всегда «свободна», как это пишут в информационных окошках «О системе». Всегда отождествляйте доступ к памяти с доступом к файлу на диске и создавайте приложение с учетом вышесказанного.
- Всякий раз, когда любой из ваших потоков пытается воспользоваться общим ресурсом вычислительного процесса, которому он принадлежит, вы обязаны каким-то образом легализовать и защитить вашу деятельность. Хорошим средством для этого являются критические секции, семафоры и очереди сообщений (см. главу 7). Если вы протестировали ваше приложение и не обнаружили ошибок синхронизации, то это еще не значит, что их там нет. Пользователь может создавать самые непредсказуемые ситуации. Это очень ответственный момент в разработке многопоточных приложений.
- Не возлагайте на поток несколько функций. Сложные функциональные отношения затрудняют понимание общей структуры приложения, его алгоритм. Чем проще и однозначнее каждая из рассматриваемых ситуаций, тем больше вероятность, что ошибок удастся избежать.
Основные виды ресурсов
И возможности их разделения
Рассмотрим кратко основные виды ресурсов вычислительной системы и способы их разделения (см. рис. 1.5). Прежде всего, одним из важнейших ресурсов является сам процессор1, точнее — процессорное время. Процессорное время делится попеременно (параллельно). Имеется множество методов разделения этого ресурса (см. раздел «Планирование и диспетчеризация процессов и задач» в главе 2).
Вторым видом ресурсов вычислительной системы можно считать память. Оперативная память может делиться и одновременно (то есть в памяти одновременно может располагаться несколько задач или, по крайней мере, текущих фрагментов, участвующих в вычислениях), и попеременно (в разные моменты оперативная память может предоставляться для разных вычислительных процессов). Память — очень интересный вид ресурса. Дело в том, что в каждый конкретный момент времени процессор при выполнении вычислений обращается к очень ограниченному числу ячеек оперативной памяти. С этой точки зрения желательно память выделять для возможно большего числа параллельно исполняемых задач. С другой стороны, как правило, чем больше оперативной памяти может быть выделено для конкретного текущего вычислительного процесса, тем лучше будут условия его выполнения. Поэтому проблема эффективного разделения оперативной памяти между параллельно выполняемыми вычислительными процессами является од-
1 Разговор о процессоре как об одном из ресурсов более характерен для мультипроцессорных систем. В случае однопроцессорных систем чаще говорят о процессорном времени.
Основные виды ресурсов и возможности их разделения_______________________ 43
ной из самых актуальных. Достаточно подробно вопросы распределения памяти между параллельно выполняющимися процессами рассмотрены в главе 3.
Внешняя память тоже является ресурсом, который часто необходим для выполнения вычислений. Когда говорят о внешней памяти (например, памяти на магнитных дисках), то собственно память и доступ1 к ней считаются разными видами ресурса. Каждый из этих ресурсов может предоставляться независимо от другого. Но для полноценной работы с внешней памятью необходимо иметь оба этих ресурса. Собственно внешняя память может разделяться и одновременно, а вот доступ к ней всегда разделяется попеременно.
Если говорить о внешних устройствах, то они, как правило, могут разделяться параллельно, если используются механизмы прямого доступа. Если же устройство работает с последовательным доступом, то оно не может считаться разделяемым ресурсом. Простыми и наглядными примерами внешних устройств, которые не могут быть разделяемыми, являются принтер и накопитель на магнитной ленте. Действительно, если допустить, что принтер можно разделять между двумя процессами, которые смогут его использовать (управлять его работой) попеременно, то результаты печати, скорее всего, окажутся негодными — фрагменты выведенного текста могут перемешаться таким образом, что будет не понятно, что есть что. Аналогично и для накопителя на магнитной ленте. Если один процесс начнет что-то читать или писать, а второй при этом запросит перемотку ленты на ее начало, то оба вычислительных процесса не смогут выполнить свои вычисления. Здесь следует заметить, что при работе с устройствами печати мы, тем не менее, явно наблюдаем возможность печатать из разных программ, выполняющихся параллельно. Однако необходимо знать, что это реализуется за счет того, что каждый вычислительный процесс получает свой виртуальный принтер, который он ни с кем не разделяет. А операционная система, получив задания на печать от выполняющихся задач, сама упорядочивает эти задания и передает очередное задание на принтер только после полного завершения предыдущего задания.
Очень важным видом ресурсов являются программные модули. Прежде всего, мы будем рассматривать системные программные модули, поскольку именно они обычно считаются программными ресурсами и поэтому в принципе могут распределяться между выполняющимися процессами.
Как известно, программные модули могут быть однократно используемыми и многократно (или повторно) используемыми. Однократно используемыми называют такие программные модули, которые могут быть правильно выполнены только один раз, то есть в процессе своего выполнения они могут испортить себя: либо повреждается часть кода, либо исходные данные, от которых зависит ход вычислений. Очевидно, что однократно используемые программные модули являются неделимым ресурсом. Более того, их, как правило, вообще не распределяют как ресурс системы. Системные однократно используемые программные модули, как правило, задействуются только на этапе загрузки операционной системы. При этом следует иметь в виду тот очевидный факт, что собственно двоичные файлы, которые
' Процесс обращения к данным.
44_____________________________________________ Глава 1. Основные понятия
обычно хранятся на системном диске и в которых и записаны эти модули, не портятся, а потому могут быть повторно использованы при следующем запуске операционной системы.
Повторно используемые программные модули, в свою очередь, могут быть непривилегированными, привилегированными и реентерабельными. Все они допускают корректное повторное выполнение программного кода при обращении к нему из другой программы.
Привилегированные программные модули работают в так называемом привилегированном режиме, то есть при отключенной системе прерываний (часто говорят, что прерывания закрыты), когда никакие внешние события не могут нарушить естественный порядок вычислений. Как результат, программный модуль выполняется до своего конца, после чего он может быть вновь вызван на исполнение из другой задачи (другого вычислительного процесса). С позиций стороннего наблюдателя по отношению к вычислительным процессам, которые попеременно (причем, возможно, неоднократно) в течение срока своей «жизни» вызывают некоторый привилегированный программный модуль, такой модуль будет выступать как попеременно разделяемый ресурс. Структура привилегированных программных модулей изображена на рис. 1.8. Здесь в первой секции программного модуля выключается система прерываний. Следовательно, при выполнении вычислений в первой секции ничто не может их прервать, и беспокоиться о промежуточных переменных нет необходимости. В последней секции, напротив, система прерываний включается. Даже если тут же возникнет прерывание и другой процесс запросит этот же привилегированный модуль, все равно все вычисления уже выполнены и ничто не сможет их испортить.
Рис. 1.8. Структура привилегированного программного модуля
Непривилегированные программные модули — это обычные программные модули, которые могут быть прерваны во время своей работы. Следовательно, такие модули в общем случае нельзя считать разделяемыми, потому что если после прерывания выполнения такого модуля, исполняемого в рамках одного вычислительного процесса, запустить его еще раз по требованию другого вычислительного процесса, то промежуточные результаты для прерванных вычислений могут быть потеряны.
В противоположность этому, реентерабельные программные модули допускают повторное многократное прерывание своего исполнения и повторный их запуск по обращению из других задач (вычислительных процессов). Для этого реентерабельные программные модули должны быть созданы таким образом, чтобы было
' Реентерабельный — допускающий повторные прерывания (дословный перевод с английского слова «re-enterable»).
Основные виды ресурсов и возможности их разделения________________________ 45
обеспечено сохранение промежуточных результатов для прерываемых вычислений и возврат к ним, когда вычислительный процесс возобновляется с прерванной ранее точки. Это может быть реализовано двумя способами: с помощью статических и динамических методов выделения памяти под сохраняемые значения. Основным и наиболее часто используемым является динамический способ выделения памяти для сохранения всех промежуточных результатов вычисления, относящихся к реентерабельному программному модулю (рис. 1.9).
Рис. 1.9. Структура реентерабельного программного модуля
Основная идея построения и работы реентерабельного программного модуля заключается в том, что в первой (головной) своей части путем обращения из системной привилегированной секции осуществляется запрос на получение в системной области памяти блока ячеек, необходимого для размещения всех текущих (промежуточных) данных. При этом на вершину стека помещается указатель на начало области данных и ее объем. Все текущие переменные реентерабельного программного модуля в этом случае располагаются в системной области памяти. Адресация этих переменных осуществляется относительно вершины стека. Поскольку в конце привилегированной секции система прерываний включается, то во время работы центральной (основной) части реентерабельного модуля возможно ее прерывание. Если прерывания не возникает, то в третьей (заключительной) секции
46_____________________________________________ Глава 1. Основные понятия
осуществляется запрос на освобождение используемого блока системной области памяти. При освобождении этой области памяти модифицируется значение стека. Если же во время работы центральной секции возникает прерывание, и другой вычислительный процесс обращается к тому же самому реентерабельному программному модулю, то для этого нового процесса вновь заказывается новый блок памяти в системной области памяти, и на вершину стека записывается новый указатель. Очевидно, что возможно многократное повторное вхождение в реентерабельный программный модуль до тех пор, пока в области системной памяти, выделяемой спе