
- •Глава 1. Теоретические основы функционирования операционных систем 4
- •Глава 1.Теоретические основы функционирования операционных систем
- •1.1.Процессы и ресурсы
- •1.1.1.Определение процесса
- •1.1.2.Понятие ресурса
- •1.1.3.Развитие процесса в вс
- •1.1.4. Планирование процессов
- •1.1.5. Классификация процессов
- •1.1.6.Классификация ресурсов
- •1.1.7.Структуры данных для управления процессами и ресурсами
- •1.1.8.Ядро операционной системы и реализация базовых функций ос
- •1.2.Проблема синхронизации и взаимное исключение
- •1.2.1.Определение и свойства критической секции
- •1.2.2. Программные методы реализации взаимного исключения
- •1.2.3.Синхронизация процессов с помощью семафоров
- •1.2.4.Реализация примитивов взаимоисключения
- •1.2.5.Параллельное программирование и мониторы
- •1.2.6.Рандеву как модель организации взаимодействия процессов
- •1.2.7.Система прохождения сообщений
- •1.2.8.Многозадачность и языки программирования
- •1.2.9.Взаимодействие процессов и синхронизация задач в os/2
- •1.2.10.Организация взаимодействия процессов и потоков в Win32
- •1.3.Проблема тупика
- •1.3.1.Определение тупика
- •1.3.2.Необходимые условия возникновения тупика и решение задачи предотвращения тупика
- •1.3.3.Модель системы для исследования проблемы тупика
- •1.3.4.Методы распознавания тупика
- •1.3.5.Выход из тупика и восстановление работоспособности системы
- •1.3.6.Методы обхода тупиков
- •Задания для самопроверки Задания по теме «Взаимное исключение» Программные методы решения проблемы взаимного исключения
- •Задачи на работу с семафорами
- •Решение:
- •Решение:
- •Решение:
- •Решение:
- •Решение:
- •Решение:
- •Решение:
- •Контрольная по теме «Синхронизация процессов» Вариант 1
- •Вариант 2
- •Задания по теме «Тупики»
1.1.8.Ядро операционной системы и реализация базовых функций ос
Операции, выполняемые в системе над ее объектами, ведут к изменению информации в соответствующих дескрипторах. Основные операции над процессами и ресурсами в ОС выполняются компонентами ядра ОС.
Ядро ОС - это базовый набор примитивов и процессов, на основе которых строится остальная часть системы. В состав ядра включают обычно компоненты ОС, реализующие основные, наиболее часто используемые ее функции:
управление процессами;
управление ресурсами;
обработка прерываний и
ввод/вывод.
Примитив вызывается процессом в форме вызова подпрограммы. После вызова он становится частью вызывающего процесса. Его выполнение может представлять собой критическую секцию этого процесса.
Процесс, запрашивающий некоторое действие со стороны другого процесса, посылает этому процессу запрос в виде сообщения. При этом два процесса являются практически независимыми единицами работы, которые могут выполняться параллельно, однако процесс, обратившийся с запросом, может блокироваться до его выполнения обслуживающим процессом, реализующим соответствующие действия. Служебный процесс может быть написан так, что он остается в состоянии блокировки, пока не получит запроса на выполняемую им операцию. Другой вариант построения системных процессов предполагает, что они постоянно требуют у системы работы для себя.
Выбор способа реализации системных функций зависит от концепции построения ОС. Вызов процесса является более сложным, занимает больше времени и требует больших ресурсов. Но он позволяет сделать систему более гибкой и надежной.
Для реализации базовых функций компоненты ядра должны выполнять следующий набор операций над процессами:
создание (порождение) процесса;
уничтожение процесса (завершение);
блокирование процесса (перевод в состояние ожидания);
разблокирование процесса (перевод в состояние готовности);
активизация процесса (переключение процессора на выполнение соответствующей программы, запуск процесса);
прерывание процесса (приостановка выполнения программы в результате внешнего события, например, истечения кванта времени, выделенного процессору, - перевод процесса из состояния активности в состояние готовности).
Для создания процесса вызывается соответствующая функция ОС с параметрами, задающими характеристики порождаемого процесса (его имя, первоначально распределяемые ресурсы (например, память, выделенная для загрузки и инициализации программы), приоритет и т.п.). При вызове данной функции в памяти ВС строится системный блок управления, описывающий данный процесс. Этот дескриптор помещается в соответствующую очередь процессов, находящихся в состоянии готовности (если процесс не активизируется сразу после его создания).
Порождение процесса может инициироваться как при запуске программы пользователем, так и активным процессом-предком. Порожденный процесс может унаследовать ресурсы родительского процесса, его окружение. Созданный процесс сможет в свою очередь породить дочерние процессы и разделить свои ресурсы со своими потомками. Этот механизм используется и при создании нитей (потоков) в многопотоковых системах. Процессам разрешено управлять своими потомками (например, приостанавливать их). Для реализации “родственных связей” процессов информация о непосредственных предках и потомках процессов хранится в их дескрипторах (как ссылка на соответствующие управляющие блоки).
Нормальная последовательность действий при вводе нового процесса в систему состоит в выполнении операции создания процесса с последующей его активизацией (запуском). Выделение процессу, находящемуся в состоянии готовности, времени процессора осуществляется в соответствии с установленными в системе принципами планирования.
Запуск одного процесса (более приоритетного) может вызвать прерывание выполнения другого, активного процесса и перераспределение процессора. В этом случае прерванный процесс переходит в состояние готовности и помещается в очередь процессов, претендующих на выделение им времени центрального процессора. Состояние реального процессора на момент прерывания выполнения на нем процесса запоминается в дескрипторе прерванного процесса или в контекстной памяти этого процесса.
Блокирование процесса происходит в результате выполнения им запроса к системе на выполнение какого-либо действия (например, ввода или вывода) или на предоставление какого-либо ресурса. Процесс, сделавший запрос, переходит в состояние ожидания, его дескриптор попадает в соответствующую очередь процессов, ожидающих удовлетворения своих запросов. В системе может существовать множество таких очередей (для каждого имеющегося в системе ресурса).
Из состояния ожидания процесс может перейти в состояние готовности после завершения системой запрошенной процессом операции или после освобождения запрошенного данным процессом ресурса другим процессом, которому этот ресурс был ранее распределен. Очереди процессов, находящихся в состоянии ожидания, строятся в соответствии с установленными дисциплинами. Изменение состояния процессов в этих очередях - всегда следствие внешнего по отношению к ним события.
Операция уничтожения процесса является обратной операцией для создания процесса. При уничтожении процесса уничтожается его виртуальный процессор, все распределенные ему ресурсы возвращаются в систему. Так как обычно родительский процесс разделяет свои ресурсы со своими потомками, то уничтожение процесса-предка требует обычно уничтожения и всех его потомков. В противном случае иерархия процессов в системе была бы нарушена. Если по какой-либо причине родительский процесс завершается раньше, чем порожденные им потомки, то его завершение может быть отложено до завершения порожденных процессов или они могут быть завершены принудительно.
Окончание процесса ведет к уничтожению его дескриптора.
Кроме того, как уже было показано, операционная система может выполнять дополнительно операции по управлению процессами:
приостановки (задержки) процесса;
возобновления задержанного процесса;
изменения приоритета процесса;
изменение полномочий процесса.
Приостановка процесса выводит его из списка процессов, конкурирующих за получение ресурсов в системе. Возобновление процесса возвращает его в состав списка, который он занимал до приостановки (если процесс был прерван и задержан в состоянии активности, то его дескриптор попадает в список процессов, находящихся в состоянии готовности и ожидающих выделения им времени центрального процессора, если задерживался процесс, сделавший запрос на какой-либо ресурс, то он остается в очереди процессов, ожидающих распределения этого ресурса). Приостановка/возобновление процесса реализуется через изменение значения соответствующего поля (текущее состояние процесса) в дескрипторе этого процесса.
Изменение приоритета процесса может вызвать изменение положения его дескриптора в занимаемой им очереди. Операция изменения приоритета процесса выполняется в соответствии с установленными в данной системе правилами.
Существует два варианта определения полномочий процесса: процесс наследует полномочия пользователя, запустившего на выполнение программу, и процесс обладает собственными правами, не совпадающими с правами пользователя. В том случае, когда процессы имеют свои списки полномочий, порожденные процессы могут обладать большими правами, чем их предки.
Ресурсы в системе отслеживаются с помощью блоков управления ресурсами. Состояние ресурсов в системе изменяется при выполнении одной из следующих операций:
запрос на ресурс от процесса;
выделение ресурса процессу;
освобождение ресурса процессом, которому этот ресурс был распределен, или процессом-производителем данного ресурса (в зависимости от типа ресурса).
Запрос на ресурс выполняется процессом. В запросе указывается запрашиваемый ресурс и (для составных ресурсов) количество запрашиваемых единиц ресурса. Результатом запроса является перевод процесса в состояние ожидания и активизация программы распределения ресурса. В том случае, когда ресурс не может быть выделен процессу немедленно, процесс оказывается блокированным в соответствующей очереди, связанной с данным ресурсом. Если ресурс может быть выделен процессу, изменяется состояние ресурса или список доступных для распределения единиц этого ресурса, а процесс запоминается в списке процессов, получивших данный ресурс. Этот список используется системой в том случае, когда возникает необходимость перераспределения ресурса (ресурс может быть изъят у менее приоритетного процесса для удовлетворения запроса, поступившего от процесса с большим приоритетом). При выделении ресурса изменяется и блок управления процессом (описание виртуального процессора процесса).
Операция выделения ресурса процессу - ответ системы на соответствующий запрос от процесса. Она выполняется либо немедленно после получения системой запроса на ресурс (если ресурс доступен для распределения), либо после освобождения ресурса другим процессом (ресурс распределяется процессу, находящемуся в очереди первым).
Операция освобождения ресурса выполняется процессом, которому ранее был распределен повторно используемый ресурс. В этом случае процесс может освободить только те единицы ресурса, которые были ему выделены системой.
Для потребляемого ресурса операцию освобождения может выполнить процесс-производитель этого ресурса.
Выполнение операции освобождения ресурса ведет к изменению его состояния или к появлению доступных для распределения единиц этого ресурса в его описи.
Для повторно используемых ресурсов ссылка на освободивший ресурс процесс удаляется из списка процессов, которым этот ресурс распределен. Изменяется также и описание виртуального процессора процесса, освободившего ресурс, в его дескрипторе.
Таким образом, все операции, выполняемые в системе над ее объектами (процессами и ресурсами) отражаются в соответствующих управляющих блоках системы - дескрипторах этих объектов. Структура дескрипторов различна в разных операционных системах и зависит от свойств процессов в конкретной системе и от типов ресурсов.
Кроме того, различные операционные системы отличаются друг от друга распределением функций управления между компонентами системы (ее ядром и слоями различных уровней для иерархических ОС, микроядром и серверами, выполняемыми в режиме пользователя для ОС микроядерной архитектурой “клиент-сервер”).
Основные проблемы при управлении процессами и ресурсами возникают в системах, в которых возможны конфликтные ситуации при разделении ресурсов несколькими процессами.
Ядро ОС Windows NT (Kernel) является основным компонентом системы, ее “сердцем”, и работает напрямую с уровнем аппаратных абстракций. Этот модуль, в первую очередь, занимается планированием действий процессора. В случае, когда компьютер содержит несколько процессоров, ядро синхронизирует их работу для достижения максимальной производительности системы.
Ядро осуществляет диспетчеризацию нитей управления (подзадач или потоков), которые являются основными объектами в системе планирования.
Подкомпоненты исполняющей системы Windows NT, такие как диспетчер ввода/вывода и диспетчер процессов, используют ядро для синхронизации действий. Они также взаимодействуют с ядром для управления объектами более высоких уровней абстракции, называемых объектами ядра (некоторые из этих объектов экспортируются внутри пользовательских вызовов интерфейса прикладных программ (API).
Ядро управляет двумя типами объектов:
объекты диспетчеризации (dispatcher objects) характеризуются сигнальным состоянием (signaled/nonsignaled) и управляют диспетчеризацией и синхронизацией системных операций; эти объекты включают события (events), мутанты (mutants), мутексы (mutexes), семафоры (semaphores), нити управления (threads) и таймеры (timers);
управляющие объекты (control objects) - используются для реализации операций управления ядра, но не воздействуют на диспетчеризацию или синхронизацию; они включают асинхронные вызовы процедур (asynchronous procedure calls), прерывания (interrupts), уведомления (power notifies) и состояния (power statuses) источника питания, процессы (processes) и профили (profilers).
Объекты типа события (event) используются для записи местонахождения события и синхронизации его с некоторым выполняемым действием.
Объекты типа mutant используются ядром для контроля над общим монопольным доступом к ресурсу (применяются для обеспечения в пользовательском режиме механизма взаимного исключения, могут использоваться и в привилегированном режиме).
Объекты типа mutex также используются для контроля общего монопольного доступа к ресурсу, но могут быть использованы только в режиме выполнения ядра и предназначены для обеспечения беступикового механизма взаимного исключения.
Семафоры используются для управления доступом к ресурсу, но не обязательно в режиме взаимного исключения. Объекты этого типа действуют как “клапаны”: через каждый клапан может пройти одновременно некоторое количество нитей управления (до установленного ограничения, определяемого состоянием ресурса); клапан открыт (находится в состоянии signaled) до тех пор, пока имеются доступные ресурса, связанные с этим семафором, когда число используемых ресурсов достигает ограничивающей отметки, клапан закрывается (переходит в состояние nonsignaled).
Объекты ядра thread связаны с потоками; каждая нить связана с объектом ядра “процесс”, представляющим процесс, владеющий данной нитью. Процесс определяет распределение виртуального адресного пространства для нити и собирает результаты выполнения своих нитей. Их выполнение может распределяться между процессорами в многопроцессорной системе.
Объект таймер используется для фиксирования временных интервалов и прерывания (по таймеру) операций.
Асинхронные вызовы процедур используются для прерывания выполнения специфицированной нити управления и передачи управления вызываемой процедуре в определенном режиме процессора.
Объекты прерывания используются для соединения источника прерывания и процедуры обслуживания прерывания через элемент таблицы управления прерываниями (IDT - Interrupt Dispatch Table). Каждый процессор имеет свою IDT, управляющую прерываниями этого процессора.
Объект ядра процесс используется для представления пространства виртуальных адресов и управляющей информации, необходимой для выполнения набора объектов и нитей. Этот объект содержит указатель на карту адресов, список готовых нитей управления, содержащий объекты соответствующего класса, список принадлежащих процессу нитей управления, общее накопленное время для выполнения нитей процесса, базовый приоритет и свойства потока по умолчанию.
Объект profile используется для определения распределения времени выполнения внутри блока кода.
Ядро выполняется полностью в привилегированном режиме и неперемещаемо в памяти. Программы ядра не являются выгружаемыми, для них не может производиться переключение контекста.
Ядро может выполняться одновременно на всех процессорах в мультипроцессорной конфигурации, соответствующим образом синхронизируя доступ к критическим областям.
Управляемые операционной системой ресурсы представляются соответствующими объектами.
Тип объекта включает определенный системой тип данных, список операций, которые могут выполняться над ним (например, создать и т.п.), и набор атрибутов объекта.
Диспетчер объектов является частью исполнительной системы Windows NT и обеспечивает унифицированные правила хранения, именования и безопасности объектов.
Для получения доступа к объекту процесс должен получить его описатель (object handle) через диспетчер объектов. Все описатели объектов создаются через него.
Кроме того, диспетчер объектов управляет глобальным пространством имен, которое используется для доступа ко всем именованным объектам, которые содержатся в локальной компьютерной среде.
Диспетчер процессов - компонент, который отслеживает два типа объектов: объекты процесса и объекты потоков (нитей управления).
Как уже было отмечено, процесс определяется своим адресным пространством, набором доступных процессу объектов и совокупностью выполняемых в контексте процесса потоков.
Поток является основным управляемым элементом в системе. Он имеет собственный набор регистров, собственный стек ядра, блок среды нити и стек пользователя в адресном пространстве процесса.
Диспетчер процессов управляет созданием и завершением процессов. Он также обеспечивает набор стандартных услуг по созданию и использованию нитей.
Модель процессов Windows NT работает совместно с моделью безопасности и диспетчером виртуальной памяти для обеспечения безопасности объектов.
Средства управления памятью, процессами и потоками в системе сосредоточены в KERNEL32.DLL.
Ядро ОС Windows 95 состоит из трех компонентов: User, Kernel и GDI. Каждый из них включает пару DLL: 32-битную и 16-битную (User32 и User16, Kernel32 и Kernel16 и GDI32 и GDI16). Они обеспечивают сервис для выполняемых приложений.
Компонент User управляет вводом с клавиатуры, от мыши и других координатных устройств, а также выводом через интерфейс пользователя (окна, меню, значки и т.д.). Кроме того, он управляет взаимодействием со звуковым драйвером, таймером и коммуникационными портами.
Модуль Kernel обеспечивает базовые функциональные возможности ОС, в том числе поддержку файлового ввода/вывода, управление виртуальной памятью и планирование задач. В момент запуска программы он загружает ее EXE- и DLL-файлы.
Еще один вид сервиса, предоставляемых этим модулем, - обработка исключений (прерываний).
При выполнении программы Kernel отвечает за исполнение потоков каждого процесса и распределение между ними процессорного времени.
Кроме того, данный модуль обеспечивает взаимодействие 16- и 32-битного кода.
Интерфейс графического устройства - GDI (Graphical Device Interface) - это графическая система, управляющая всем, что появляется на экране дисплея, и поддерживающая графический вывод на принтеры и другие устройства. Она отвечает за отрисовку графических примитивов, манипуляции растровыми изображениями и взаимодействие с графическими драйверами.
В среде Win32 для управления процессами и потоками используются соответствующие объекты ядра.
Все функции, создающие объекты ядра, имеют параметр, позволяющий задать атрибут защиты.
Однажды созданный объект ядра можно открывать из любого приложения, если оно имеет право доступа к нему. Следовательно, разные приложения могут работать с одними и теми же объектами. При этом, если приложение открывает уже существующий объект, то для его представления не выделяется новых блок памяти, а в существующем объекте увеличивается счетчик числа пользователей этого объекта, потоку же, открывшему объект, возвращается его описатель (handle), который идентифицирует данный объект.
Если поток завершает работу с объектом ядра, то он может сообщить об этом системе, вызвав функцию CloseHandle с параметром-дескриптором объекта. Эта функция уменьшает счетчик пользователей указанного объекта, а по достижении им нулевого значения освобождает память, которая была выделена для управления данным объектом. После вызова этой функции процесс теряет связь с объектом ядра.
Процесс создается при вызове функции CreateProcess. При этом система создает объект ядра “процесс” с начальным значением счетчика пользователей, равным 1. Этот объект представляет структуру данных для управления процессом.
Затем для нового процесса выделяется адресное пространство в 4 Гб, в него загружается код исполняемого файла и используемые DLL.
Следующее действие системы при создании процесса - создание объекта ядра “поток” со счетчиком пользователей, равным 1, для управления первичным потоком нового процесса, с которого и начнется выполнение процесса.
Родительский процесс в случае успешного выполнения этой функции получает описатели объекта “процесс” дочернего процесса и объекта “поток” его первичного потока, а также уникальные для системы идентификаторы этих объектов. При этом родительский процесс становится их пользователем, а соответствующие счетчики увеличиваются.
Родительский процесс управляет наследованием своих объектов, окружения дочерним процессом.
Первичный поток создается автоматически, но любой поток может создать новый поток с помощью функции CreateThread, выполняющей следующие действия: создает объект ядра “поток” для идентификации и управления создаваемым потоком; инициирует код завершения потока, сохраняя его в созданном объекте, значением STILL_ACTIVE; приравнивает счетчик простоя потока 1; создает для нового потока структуру CONTEXT; создает стек потока; инициализирует указатель стека в структуре CONTEXT.
Описатель вновь созданного объекта возвращается функцией.
Поток может быть завершен вызовом функции ExitThread в самом потоке или вызовом TerminateThread из любого другого потока, имеющего описатель прекращаемого потока. В Windows NT функция TerminateThread сохраняет стек уничтоженного потока, так как информацию из него могут использовать другие потоки, до завершения процесса, а в Windows 95 не сохраняет. Кроме того, при вызове TerminateThread об уничтожении потока не уведомляются DLL-модули, подключенные к процессу, что может привести к некорректному завершению процесса.
При завершении потока освобождаются все описатели объектов, принадлежащих потоку; объект ядра “поток” получает статус signaled (незанятый, свободный); код завершения потока меняется со STILL_ACTIVE на переданный в функцию код завершения; если данный поток является последним активным потоком процесса, то завершается процесс; счетчик числа пользователей объекта “поток” уменьшается на 1.
Объект ядра “поток” не освобождается из памяти, пока не будут закрыты все внешние ссылки на него.
Для завершенного потока можно проверить код его завершения с помощью функции GetExitCodeThread.
Для завершения всего процесса также существует несколько вариантов. Процесс может быть завершен автоматически при завершении всех его потоков или при возврате управления из первичного потока (например, при завершения основной функции main() или WinMain() в C++ с помощью return()).
Процесс может быть завершен вызовом функции ExitProcess одним из потоков процесса (при этом завершаются все его потоки). Процесс может быть также уничтожен любым другим процессом, которому доступен его описатель, с помощью функции TerminateProcess, которую может вызвать любой поток. Последняя функция может завершить процесс некорректно, так как не оповещает о его завершении связанные с ним DLL-модули.
При завершении процесса выполнение всех потоков в процессе прекращается; все объекты ядра закрываются, а остальные объекты, созданные процессом, уничтожаются; объект ядра “процесс” получает статус “свободного” (signaled); код завершения процесса меняется на переданное в качестве параметра значение; счетчик числа пользователей объекта ядра “процесс” уменьшается на 1.
Память объекта освобождается только после закрытия последней ссылки на него.
Для “покойного” процесса можно проверить код его завершения, если есть его описатель и он не закрыт.
Родительские процессы могут “порвать отношения” с дочерними процессами и “забыть о них” сразу после их создания, закрыв с помощью функции CloseHandle объекты ядра “процесс” и “поток”, описатели которых они получают после завершения функции создания процесса. В этом случае родительские процессы перестают быть их пользователями и уничтожение этих объектов не будет зависеть от родительского процесса.