Вопрос 1. Объекты ядра. Создание и закрытие объектов ядра………………..3
1. Объекты ядра операционной системы………………………………………. 3
3.2. Завершение работы потока………………………………………………. 11
3.3. Приостановка и возобновление выполнения потока…………………….12
Вопрос 2. Файлы, проецируемые в память. Отличие проецируемых файлов в ОС Windows и UNIX …………………………………………………………………..18
Список использованных источников……………………………………………..25
Объекты ядра. Создание и закрытие объектов ядра.
Объекты ядра операционной системы
Ядро — центральная часть операционной системы, управляющая выполнением процессов, ресурсами вычислительной системы и предоставляющая процессам координированный доступ к этим ресурсам.
Как основополагающий элемент ОС, ядро представляет собой наиболее низкий уровень абстракции для доступа приложений к ресурсам вычислительной системы, необходимым для их работы.
К объектам ядра ОС относятся:
Файлы, проецируемые в память
Каждый объект ядра — это блок памяти, выделенный ядром и доступный только ему. В блоке размещается структура данных, поля которой содержат информацию об объекте. Некоторые поля (например, дескриптор защиты и счётчик количества пользователей) присутствуют во всех объектах, но большая их часть специфична для объектов конкретного типа. Приложение не имеет прямого доступа к структурам данных, представляющим собой объекты ядра. Оперировать объектами ядра приложение может только через специальные функции Windows.
Когда вы вызываете функцию, создающую объект ядра (например, CreateFile), она возвращает дескриптор созданного объекта. Этот дескриптор может быть использован любым потоком вашего процесса. Для большей надежности операционной системы Microsoft сделано так, что значения дескрипторов объектов ядра действительны только в адресном пространстве процесса, их создавшего. Поэтому все попытки передачи такого дескриптора другому процессу и его использования в другом процессе приводят к ошибкам.
Объекты принадлежат ядру, а не процессу. Другими словами, если процесс создает какой-либо объект ядра, а затем завершает свою работу, то объект ядра может быть и не разрушен. В большинстве случаев объект все же разрушается, но если этим объектом пользуется другой процесс, то ядро не позволит разрушить объект до тех пор, пока второй процесс не откажется от него.
Ядру известно, сколько процессов использует конкретный объект ядра, поскольку в каждом объекте есть счётчик пользователей. В момент создания объекта ядра счётчику присваивается единичное значение. Когда к этому объекту обращается другой процесс, значение счётчика увеличивается на единицу. А когда какой-то процесс завершается, счётчики всех используемых им объектов ядра уменьшаются на единицу. Как только счётчик пользователей объекта обнуляется, ядро уничтожает этот объект.
Многие объекты ядра находятся всегда в одном из двух состояний: свободном состоянии (signaled state) либо несвободном состоянии (nonsignaled state). К таким объектам относятся:
Уведомления об изменении файлов
Переход из одного состояния в другое осуществляется по правилам, определенным Microsoft для каждого из объектов ядра. Эти состояния могут использоваться так называемыми wait-функциями для решения проблем синхронизации выполнения потоков.
Wait-функции позволяют потоку в любой момент приостановиться и ждать освобождения какого-либо объекта ядра. Важным свойством функций этого семейства является то, что они не тратят процессорное время, пока ждут освобождения объекта или наступления тайм-аута.
Успешный вызов функции WaitForSingleObject или WaitForMuttipleObjects на самом деле меняет состояние некоторых объектов ядра. Успешным считается такой вызов, который завершается освобождением соответствующего объекта или объектов. При этом функция возвращает значение WAIT_OBJECT_0 или значение, являющееся смещением относительно WAIT_OBJECT_0. Вызов считается неудачным, если возвращается значение WAIT_TIMEOUT или WAIT_FAILED. В этом случае состояние каких-либо объектов не меняется.
Пусть, например, поток вызвал функцию WaitForSingleObject и ждёт освобождения объекта «событие с автосбросом» (объекты-события рассматриваются в следующем разделе). Когда объект переходит в свободное состояние, функция обнаруживает это и возвращает значение WAIT_OBJECT_0. Но перед самым возвратом из функции объект-событие будет переведен в занятое состояние. Это и есть побочный эффект успешного ожидания.
Объекты ядра «событие с автосбросом» ведут себя подобным образом, потому что таково одно из правил, определенных Microsoft для объектов этого типа. Другие объекты дают иные побочные эффекты, а некоторые — вообще никаких. К последним относятся объекты ядра «процесс» и «поток».
Объекты ядра можно защитить с помощью дескриптора защиты (security descriptor). Он содержит информацию о том, кто создал объект и кто имеет права на доступ к нему. Дескрипторы защиты обычно используют при написании серверных приложений. Создавая клиентское приложение, можно просто игнорировать это свойство объектов ядра.
Почти все функции, создающие объекты ядра, принимают в качестве аргумента указатель на структуру SECURITY_ATTRIBUTES. В большинстве случаев на месте соответствующего аргумента можно указать значение NULL, и тогда объект создается с защитой по умолчанию. В этом случае создатель объекта и любой член группы администраторов получают полный доступ к объекту, а все прочие процессы к объекту не допускаются.
Вне зависимости от того, как был создан объект ядра, после окончания работы с ним его нужно закрыть вызовом функции CloseHandle:
BOOL CloseHandle(HANDLE hObject);
Эта функция сначала проверяет таблицу дескрипторов данного процесса, чтобы убедиться, что процесс имеет доступ к объекту hObject. Если доступ разрешен,то система получает адрес структуры данных объекта hObject и уменьшает в ней счётчик количества пользователей. Как только счётчик обнулится, ядро удаляет объект из памяти.
Если же дескриптор неверен, то функция CloseHandle возвращает значение FALSE, а функция GetLastError — код ERROR_INVALID_HANDLE.
Перед самым возвратом управления функция CloseHandle удаляет соответствующую запись из таблицы дескрипторов. После этого дескриптор hObject считается недоступным для данного процесса, и его нельзя более использовать. Но если счётчик пользователей этого объекта не обнулен, то объект остается в памяти. Это означает, что объект используется другими процессами. Когда и остальные процессы завершат свою работу с этим объектом, тоже вызвав функцию CloseHandle, он будет разрушен.
А если вы забыли вызвать CloseHandle — будет ли утечка памяти? И да, и нет. Утечка ресурсов вполне вероятна, пока процесс еще выполняется. Однако по завершении процесса операционная система гарантированно освобождает все ресурсы, принадлежавшие этому процессу.
Ранее указывалось, что объекты ядра используются только в рамках процесса, их создавшего. Но все же иногда возникает необходимость в совместном использовании объектов ядра несколькими процессами, например, в следующих ситуациях:
объект ядра «проекция файла» позволяет двум процессам, исполняемым на одной машине, совместно использовать одни и те же блоки памяти;
почтовые ящики и именованные каналы дают возможность программам обмениваться данными с процессами, исполняемыми на других машинах в сети;
мьютексы, семафоры и события позволяют синхронизировать потоки, исполняемые в разных процессах, чтобы одно приложение могло уведомить другое приложение об окончании той или иной операции.
Win32 API предоставляет три механизма, позволяющие процессам использовать одни и те же объекты ядра:
наследование дескриптора объекта в дочернем процессе;
дублирование дескрипторов объектов. В приводимых ниже примерах мы будем активно применять второй из указанных механизмов (совпадающие имена для разделяемых объектов).
Процесс – это контейнер для набора ресурсов, используемых потоками, которые выполняют экземпляр программы. На самом высоком уровне абстракции процесс включает следующее:
закрытое виртуальное адресное пространство – диапазон адресов виртуальной памяти, которым может пользоваться процесс;
исполняемую программу – начальный код и данные, проецируемые на виртуальное адресное пространство процесса;
список открытых описателей различных системных ресурсов – семафоров, коммуникационных портов, файлов и других объектов, доступных всем потокам в данном процессе (см. Рисунок 1);
контекст защиты, называемый маркером доступа (Access Token) и идентифицирующий пользователя, группы безопасности и привилегии, сопоставленные с процессом;
уникальный идентификатор процесса (PID) (во внутрисистемной терминологии называемый идентификатором клиента);
минимум один поток.
Каждый процесс обладает контекстом защиты, который хранится в объекте – маркере доступа. Маркер доступа содержит идентификацию защиты и определяет полномочия данного процесса.
Дескрипторы виртуальных адресов (Virtual Address Descriptors, VAD) – это структуры данных, используемые диспетчером памяти для учета виртуальных адресов, задействованных процессом.
Объекты ядра
Объект – блок памяти, выделенный ядром для своих целей и доступный только ядру.
Объект ядра содержит имя объекта, класс защиты объекта, счётчик количества пользователей и другую информацию (смещение при открытии файла и т.д.).
Все объекты имеют описатели (Handle). Большинство объектов обладают свойством наследования.
Объекты ядра Windows:
— Файловый объект – открытый файл.
— Проекция файла на память.
— Событие, Семафор, Мьютекс.
— Почтовый ящик, канал, сокет и т.д.
ОС ведет учет объектов и управляет ими. Пользователь может запросить информацию об объекте.
Объекты можно создавать, уничтожать, открывать и закрывать посредством системных вызовов ОС по просьбе внешней программы. Созданные объекты закрепляются (принадлежат) создавшим их процессам. При порождении дочернего процесса, объекты ядра (как правило) наследуются из родительского, при этом создается копия объекта, принадлежащая уже дочернему процессу. Например, так наследуются стандартные потоки в/в, открытые файлы, каналы, семафоры и др. Номер дескриптора при наследовании сохраняется.
Микроядро отличается от обычного ядра тем, что в нем содержится основные, более общие функции управления ОС, а остальные функции ОС вынесены в отдельные процессы (приложения пользовательского уровня). Благодаря чему разработчик ОС может легко добавлять к ОС новые функции и расширять существующие. Таким образом, обеспечивается более эффективная защита ядра, более высокая производительность (микроядро может быть целиком размещено в кэш процессора). Микроядро изолирует все Машинно-зависимые команды, содержит ограниченный набору услуг с малым количеством системных функций (при переносе ОС на другую аппаратную платформу нужно переработать только микроядро, а все остальное – только перекомпилировать). Следовательно, обеспечивается переносимость, расширяемость и надежность.
Пример – ОС QNX, Win-NT (HAL-аналог микроядра).
Win32 API
- Объекты и процессы ядра Windows
- Многозадачность
- Объекты ядра Windows
- Процессы и потоки
- Объекты ядра
- Объекты GDI и User
- Управление памятью в Win32
- Организация виртуальной памяти в Windows
- Кучи и менеджеры куч
- Динамические хранилища
- Обработка ошибок в Win32
- Обработка ошибок с помощью функции GetLastError
- Обработка ошибок с помощью функции SetErrorMode
- Экстренное завершение приложения
Любой программист, создающий приложения Windows, должен иметь представление о процессах, происходящих в операционной системе, для создания качественных и эффективных приложений. Такие знания помогут решить многие проблемы, возникающие при разработке приложений, тесно связанных с операционной системой. В рассмотренных ранее главах мы уже изучили понятие потока и многопоточности. Эти понятия имеют непосредственное отношение к процессам операционной системы.
В данной главе мы рассмотрим, что такое объект ядра и что такое процесс ядра операционной системы. Изучим объекты GDI и User. Рассмотрим, как Windows управляет памятью и как эта операционная система обрабатывает ошибки.
Объекты и процессы ядра Windows
Сразу оговоримся, что все сказанное в этой главе относится к следующим версиям Windows: Windows 95, 98, 2000 и Windows NT, т. к. только в данных версиях была введена поддержка 32-разрядных приложений.
Примечание.
Для среды Windows 3.1 Microsoft специально разработала пакет Win32s, позволяющий с некоторыми ограничениями использовать поддержку приложений Win32.
Ядро Windows (Windows kernel) — это часть операционной системы, которая обеспечивает поддержку низкоуровневых функций, необходимых для выполнения приложений. Например, всякий раз, когда приложению нужна дополнительная память, оно обращается к ядру Windows.
Между всеми вышеперечисленными системами существуют различия в поддержке 32-разрядных приложений. В табл. 1.8 перечислены некоторые отличия, существующие между тремя операционными системами.
Таблица 1.8. Различия операционных систем при поддержке Win32 API
Windows 3.1 с поддержкой Win32
32-битная система координат
Асинхронный файловый ввод/вывод
Асинхронная модель ввода информации
На уровне Windows 3.1
Поддержка многопроцессорных материнских плат
Динамический обмен данными (DDE) по сети
Поддержка процессоров других фирм-производителей (не Intel)
Безопасность (сертификат С2)
Разделяемое адресное пространство
Поддержка TAPI (Telephone API)
Системные ресурсы для объектов User и GDI
Практически не ограничены
Итак, Win32 API (Application Programming Interface) — это интерфейс разработки 32-разрядных приложений Windows.
Многозадачность
Многозадачность (multitasking) — свойство операционной системы выполнять одновременно несколько приложений.
Примечание
По сути, истинной многозадачности нет в большинстве из вышеперечисленных операционных систем. Происходит лишь переключение между задачами. Но, за счет того, что эти переключения происходят довольно часто, у пользователя создается впечатление, что приложения выполняются одновременно.
В ранних, 16-разрядных операционных системах поддерживалась так называемая кооперативная многозадачность (cooperative multitasking). Это такой вид многозадачности, когда приложение в процессе своего выполнения само «решает» передавать управление операционной системе или продолжать занимать процессорное время. При этом другие приложения, запущенные вместе с первым, просто не выполняют никаких действий. Данный вид многозадачности приводил к «зависаниям» операционной системы вместе со всеми запущенными приложениями, если «висло» приложение, захватившее ресурсы процессора.
На смену кооперативной многозадачности пришла вытесняющая многозадачность (preemptive multitasking). Вытесняющая многозадачность появилась лишь в 32-разрядных операционных системах. Данный вид многозадачности подразумевает, что управление всеми приложениями ведет операционная система. Она выделяет каждому приложению определенный квант времени процессора в зависимости от приоритета приложения (см. главу 3).
Объекты ядра Windows
Рассмотрим объекты, с которыми работает операционная система Windows.
Сразу обратим ваше внимание на то, что объекты Win32 и объекты Delphi — абсолютно разные объекты. В Win32 объекты делятся на объекты ядра (kernel) и объекты GDI и User.
Процессы и потоки
Процесс — это выполняющееся приложение Windows. Так как Windows — многозадачная операционная система, то в ней может работать сразу несколько процессов. Каждый процесс получает свое адресное пространство (размером до 4-х гигабайт). В этом пространстве хранится код приложения, его данные, а также все подключаемые библиотеки (DLL).
Сами процессы ничего не выполняют. Каждый процесс состоит из потоков (threads), которые выполняют код процесса (подробнее о потоках и об их создании см. главу 3). Любой процесс состоит как минимум из одного потока, который называется первичным или главным потоком (primary thread). Процесс может состоять из нескольких потоков, только один из которых будет главным.
Поток — это объект операционной системы, который представляет собой часть кода, находящегося внутри некоторого процесса. .
При создании процесса операционная система создает его главный поток, который может генерировать дополнительные потоки. При этом каждому потоку процесса Windows выделяет свои кванты времени процессора, в зависимости от приоритета потока.
Для работы с процессами Win32 API имеет встроенные функции, перечисленные в табл. 1.9.
Таблица 1.9. Функции Win32 API для работы с процессами
Объекты ядра
Объекты ядра (kernel objects) — это процессы, потоки, события, семафоры, мьютексы и т. д., т. е. все то, с чем работает ядро Windows.
При создании объекта он существует в адресном пространстве процесса. При этом дескриптор объекта доступен породившему его процессу. Данный дескриптор не может быть использован другими приложениями для доступа к объекту разных процессов. Но эта проблема разрешима: с помощью функций Win32 API процесс имеет возможность получения собственного дескриптора (отличного от дескриптора процесса, породившего объект) для уже существующего объекта.
Например, первый процесс создает именованный или неименованный мьютекс и возвращает его дескриптор с помощью команды createMutex. Для того чтобы данный мьютекс мог использоваться другим процессом, необходимо воспользоваться функцией openMutex, которая возвращает дескриптор уже существующего мьютекса.
При обращении к объектам ядра Windows использует счетчик обращений к объекту. При создании или использовании объекта ядра приложениями счетчик увеличивается. При прекращении использования объекта ядра приложением счетчик уменьшается. Наконец, при обнулении счетчика объект ядра уничтожается.
Объекты GDI и User
В Windows 3.1 не было объектов ядра. Доступ ко всем объектам операционной системы осуществлялся с помощью дескрипторов. Объекты операционной системы делились на две группы: объекты, находящиеся в локальной памяти модулей GDI и User, и объекты, находящиеся в глобальной памяти.
Интерфейс графического устройства (Graphical Device Interface, GDI) — это часть Windows, которая управляет шрифтами, средствами печати и другими графическими системами Windows.
Когда приложение выводит что-либо на экран, оно использует службы, представляемые GDI.
Объекты GDI- это палитры, изображения, шрифты, и т. д., т.е. то, чем управляет GDI.
Средства пользовательского интерфейса (User) — это часть Windows, отвечающая за все окна, которые создаются приложениями.
Объекты User — это, в первую очередь, окна, меню.
В 16-разрядной версии Windows имелась непосредственная связь между объектом и его дескриптором. Таким образом, в данной версии Windows существовала таблица, содержащая указатели на все объекты операционной системы. Эта таблица была доступна любому приложению или динамически компонуемой библиотеке Windows. Она называлась таблицей локальных дескрипторов (Local Descriptor Table). В результате, любое приложение (или DLL) могло обращаться к объекту, используемому другим приложением.
В 32-разрядной операционной системе объекты хранятся в собственных адресных пространствах процессов, и для каждого процесса существует своя собственная таблица дескрипторов объектов. Таким образом, каждый процесс теперь работает с собственными дескрипторами объектов.
Любые дескрипторы объектов GDI или User управляются специальными подсистемами Win32 API. Для GDI — это GDI.EXE, для User — USER.EXE. Данные подсистемы выполняют создание, освобождение и проверку корректности работы дескрипторов объектов.
Управление памятью в Win32
В данном разделе мы рассмотрим все, чего еще не касались ранее о распределении памяти в Win32.
Организация виртуальной памяти в Windows
Как нам уже известно, Win32 — это 32-разрядная операционная система. Таким образом, любое приложение, запущенное в Win32, может захватывать адресное пространство в размере 4 Гбайта. Каждое приложение (процесс операционной системы) обладает своим индивидуальным адресным пространством, которое не пересекается с адресными пространствами других приложений.
Процесс выделения памяти в Windows состоит из двух последовательных этапов:
— резервирования участка памяти (виртуального адресного пространства), необходимого размера для размещения приложения и его данных. На этом этапе, физически, операционная система не выделяет оперативную память. То есть, фактически, вы можете дать команду операционной системе зарезервировать 300 Мбайт адресного пространства, и при этом не займете практически никаких системных ресурсов. Вы можете непосредственно указать операционной системе адреса, которые резервируете, или предоставить это дело операционной системе;
Примечание
Адресное пространство в Win32 резервируется блоками по 64 Кбайта. Поэтому, несмотря на ваши пожелания, при резервировании адресного пространства операционная система реально зарезервирует первый (базовый) участок адресного пространства объемом 64 Кбайта. Данное разбиение адресного пространства на блоки служит для ускорения работы ядра операционной системы.
— выделения реальной, физической памяти в зарезервированный участок виртуального адресного пространства. На данном этапе операционная система выделяет реальный блок памяти, который также состоит из небольших блоков.
Примечание
Минимальный блок памяти, с которым работает операционная система, называется страницей памяти. Размер страницы памяти различен в разных операционных системах. Например, в Windows NT — он равен 8 Кбайт, а в Windows 95/98 — 4 Кбайта.
Так как объем оперативной памяти компьютера обычно достаточно небольшой (для современных компьютеров 32-128 Мбайт), то операционная система вынуждена при нехватке основной оперативной памяти использовать так называемый файл подкачки (swap file) или виртуальную память. Таким образом, если в оперативной памяти компьютера имеется несколько приложений, запущенных одновременно, и им не хватает оперативной памяти, Windows просто выгружает те страницы памяти, к которым длительное время не было обращений. В случае, когда приложению потребуется выгруженная страница памяти, Windows освободит страницу, выгрузив страницу, к которой давно не было обращений, и загрузит на ее место требуемую, после чего вернет управление приложению.
Все вышеперечисленные действия абсолютно незаметны для приложения. Приложению не нужно заботиться о выгрузке и загрузке страниц, все эти действия выполняет операционная система.
Кучи и менеджеры куч
Серьезные приложения интенсивно используют механизмы выделения и освобождения памяти. В коммерческих приложениях широко применяются такие элементы, как: динамические массивы, строки, объекты и многое другое. При этом, создание и удаление подобных элементов происходит довольно часто, а сами элементы имеют относительно небольшой размер.
Такая работа по выделению памяти и ее освобождению, по отношению к элементам небольшого размера, является неэффективной. Во-первых, снижается производительность, т. к. резервирование адресного пространства и выделение страниц памяти происходит на уровне ядра операционной системы. Во-вторых, теряются большие объемы памяти. Например, если приложение требует выделить 256 байт под строку, операционная система реально выделяет одну страницу памяти, объемом 4 или 8 килобайт (в зависимости от операционной системы).
Для решения проблемы выделения памяти небольшим элементам приложения была введена организация по принципу кучи (heap). Куча — это достаточно большой непрерывный участок памяти, из которого выделяются небольшие блоки. Для того чтобы куча могла функционировать, в операционную систему был включен так называемый менеджер кучи. Менеджер кучи — это специальный механизм, который следит за выделением и освобождением блоков памяти. Для каждого вновь созданного процесса Windows по умолчанию создает кучу. Все кучи, которые создаются операционной системой, являются потокобезопасными. Следовательно, у программиста есть возможность обращаться к одной куче из разных потоков одновременно. Для работы с менеджером кучи Win32 API можно пользоваться следующими функциями (табл. 1.10).
Таблица 1.10. Функции Win32 API для работы с кучей