Мегаобучалка Главная | О нас | Обратная связь


ЭКЗАМЕНАЦИОННЫЙ БИЛЕТ № 25



2016-01-02 483 Обсуждений (0)
ЭКЗАМЕНАЦИОННЫЙ БИЛЕТ № 25 0.00 из 5.00 0 оценок




1. Именованные объекты.

Второй способ, позволяющий нескольким процессам совместно использовать одни и те же объекты ядра, связан с именованием этих объектов. Именование допускают многие (но не все) объекты ядра. Например, следующие функции создают именованные объекты ядра

HANDLE CreateMutex(
PSLCURITY_ATTRIBUTES psa,
BOOL bInitialOwner,
PCTSTR pszName);

HANDLE CreateEvent(
PSECURITY_ATTRIBUTES psa,
BOOL bManualReset,
BOOL bInitialState,
PCTSTR pszName);

HANDLE CreateSemaphore(
PSECURITY_ATTRIBUTES psa,
LONG lInitialCount,
LONG lMaximumCount,
PCTSTR pszNarne);

HANDLE CreateWaitableTimer(
PSLCURITY_ATTRIBUTES psa,
BOOL bManualReset,
PCTSTR pszName);

HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTES psa,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);

HANDLE CreateJobObject(
PSECURITY_ATTRIBUTES psa,
PCTSTR pszName);

Последний параметр, pszName, у всех этих функций одинаков. Передавая в нем NULL, Вы создаете безымянный (анонимный) объект ядра. В этом случае Вы можете разделять объект между процессами либо через наследование (см. предыдущий раздел), либо с помощью DuplicateHandle (см. следующий раздел). А чтобы разделять объект по имени, Вы должны присвоить ему какое-нибудь имя. Тогда вместо NULL в параметре pszName нужно передать адрес строки с именем, завершаемой нулевым символом. Имя может быть длиной до MAX_PATH знаков (это значение определено как 260). К сожалению, Microsoft ничего не сообщает о правилах именования объектов ядра. Например, создавая объект с именем JeffObj, Вы никак не застрахованы от того, что в системе еще нет объекта ядра с таким именем. И что хуже, все эти объекты делят единое пространство имен. Из-за этого следующий вызов CreateSemaphore будет всегда возвращать NULL:

HANDLE hMutex = CreateMutex(NULL. FALSE, "JeffObj");
HANDLE hSem = CreateSemaphore(NULL, 1, 1, "JeffObj");
DWORD dwErrorCode = GetLastError();

После выполнения этого фрагмента значение dwErrorCode будет равно 6 (ERROR_INVALID_HANDLE). Полученный код ошибки не слишком вразумителен, но другого не дано.

Теперь, когда Вы научились именовать объекты, рассмотрим, как разделять их между процессами по именам. Допустим, после запуска процесса А вызывается функция:

HANDLE hMutexPronessA = CreateMutex(NULL, FALSE, "JeffMutex");

Этот вызов заставляет систему создать новенький, как с иголочки, объект ядра "мъютекс" и присвоить ему имя JeffMutex Заметьте, что описатель hMutexProcessA в процессе А не является наследуемым, — он и не должен быть таковым при простом именовании объектов.

Спустя какое-то время некий процесс порождает процесс В. Необязательно, что-бы последний был дочерним от процесса А; он может быть порожден Explorer или любым другим приложением. (В этом, кстати, и состоит преимущество механизма именования объектов перед наследованием.) Когда процесс В приступает к работе,
исполняется код:

HANDLE hMutexProcessB = CreateMutex(NULL, FALSE, "JeffMutex");

При этом вызове система сначала проверяет, не существует ли уже объект ядра с таким именем. Если да, то ядро проверяет тип этого объекта. Поскольку мы пытаемся создать мьютекс и его имя тоже JeffMutex, система проверяет права доступа вызывающего процесса к этому объекту. Если у него есть все права, в таблице описателей, принадлежащей процессу В, создается новая запись, указывающая на существующий объект ядра. Если же вызывающий процесс не имеет полных прав на доступ к объекту или если типы двух объектов с одинаковыми именами не совпадают, вызов CreateMutex заканчивается неудачно и возвращается NULL.

Однако, хотя процесс В успешно вызвал CreateMutex, новый объект-мьютекс он не создал. Вместо этого он получил свой описатель существующего объекта-мьютекса. Счетчик объекта, конечно же, увеличился на 1, и теперь этот объект не разрушится, пока его описатели не закроют оба процесса — А и В. Заметьте, что значения описателей объекта в обоих процессах скорее всего разные, но так и должно быть, каждый процесс будет оперировать с данным объектом ядра, используя свой описатель.

NOTE:
Разделяя объекты ядра по именам, помните об одной крайне важной вещи.

Вызывая CreateMutex, процесс В передает ей атрибуты защиты и второй параметр. Так вот, эти параметры игнорируются, если объект с указанным именем уже существует! Приложение может определить, что оно делает: создает новый объект ядра или просто открывает уже существующий, — вызвав GetLastError сразу же после вызова одной из Create-функций:

HANDLE hMutex = CreateMutex(&sa, FALSE, "JeffObj");
if (GetLastError() == ERROR_ALREADY_EXISTS) {
// открыт описатель существующего объекта sa.lpSecurityDescriptor и второй параметр (FALSE) игнорируются
} else {
// создан совершенно новый объект sa.lpSecurityDescriptor и второй параметр (FALSE) используются при создании объекта
}

Есть и другой способ разделения объектов по именам. Вместо вызова Create-функции процесс может обратиться к одной из следующих Open-функций:

HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);

HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);

HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName),

HANDLE OpenWaitableTimer(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);

HANDLE OpenFileMapping(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);

HANDLE Openjob0bject(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);

Заметьте: все эти функции имеют один прототип. Последний параметр, pszName, определяет имя объекта ядра. В нем нельзя передать NULL — только адрес строки с нулевым символом в конце Эти функции просматривают единое пространство имен объектов ядра, пытаясь найти совпадение. Если объекта ядра с указанным именем нет, функции возвращают NULL, a GetLastError — код 2 (ERROR_FILE_NOT_FOUND). Но если объект ядра с заданным именем существует и если его тип идентичен тому, что Вы указали, система проверяет, разрешен ли к данному объекту доступ запрошенного вида (через параметр dwDesiredAccess). Если такой вид доступа разрешен, таблица описателей в вызывающем процессе обновляется, и счетчик числа пользователей объекта возрастает на 1 Если Вы присвоили параметру bInheritHandle значение TRUE, то получше наследуемый описатель.

Главное отличие между вызовом Create- и Open-функций в том, что при отсутствии указанного объекта Create-функция создает его, а Open-функция просто уведомляет об ошибке.

Как я уже говорил, Microsoft ничего не сообщает о правилах именования объектов ядра Но представьте себе, что пользователь запускает две программы от разных компаний и каждая программа пытается создать объект с именем "MyObject". Ничего хорошего из этого не выйдет. Чтобы избежать такой ситуации, я бы посоветовал создавать GUID и использовать его строковое представление как имя объекта.

Именованные объекты часто применяются для того, чтобы не допустить запуска нескольких экземпляров одного приложения. Для этого Вы просто вызываете одну из Create-функций в своей функции main или WinMain и создаете некий именованный

объект. Какой именно — не имеет ни малейшего значения. Сразу после Create-функции Вы должны вызвать GetLastError Если она вернет ERROR_ALREADY_EXISTS, значит, один экземпляр Вашего приложения уже выполняется и новый его экземпляр можно закрыть. Вот фрагмент кода, иллюстрирующий этот прием:

int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE, PSTR pszCmdLine, int nCmdShow} {
HANDLE h = CreateMutex(NULL, FALSE, "{FA531CC1-0497-11d3-A180-00105A276C3E}");
lf (GetLastError() == ERROR_ALREADY_EXISTS){
// экземпляр этого приложения уже выполняется
return(0),
}

// запущен первый экземпляр данного приложения
// перед выходом закрываем объект
CloseHandle(h),
return(0);
}

Пространства имен Terminal Server

Terminal Server несколько меняет описанный выше сценарий На машине с Terminal Server существует множество пространств имен для объектов ядра. Объекты, которые должны быть доступны всем клиентам, используют одно глобальное пространство имен. (Такие объекты, как правило, связаны с сервисами, предоставляемыми клиентским программам.) В каждом клиентском сеансе формируется свое пространство имен, чтобы исключить конфликты между несколькими сеансами, в которых запускается одно и то же приложение Ни из какого сеанса нельзя получить доступ к объектам другого сеанса, даже если у их объектов идентичные имена.

Именованные объекты ядра, относящиеся к какому-либо сервису, всегда находятся в глобальном пространстве имен, а аналогичный объект, связанный с приложением, Terminal Server по умолчанию помещает в пространство имен киентского сеанca. Однако и его можно перевести в глобальное пространство имен, поставив перед именем объекта префикс "Global\", как в примере ниже.

HANDLE h = CreateEvenL(NULL, FALSE, FALSE, "Global\\MyName");

Если Вы хотите явно указать, что объект ядра должен находиться в пространстве имен клиентского сеанса, используйте префикс "Local\":

HANDLE h = CreateEvent(NULL, FALSE, FALSE, "Local\\MyName");

Microsoft рассматривает префиксы Global и Local как зарезервированные ключевые слова, которые не должны встречаться в самих именах объектов. К числу таких слов Microsoft относит и Session, хотя на сегодняшний день оно не связано ни с какой функциональностью. Также обратите внимание на две вещи, все эти ключевые слова чувствительны к регистру букв и игнорируются, если компьютер работает без Terminal Server.

 

2. Выход из процесса и его завершение.

После создания процесс начинает работать и выполняет свою задачу. Но ничто не длится вечно, даже процессы. Рано или поздно новые процессы будут завершены, обычно в силу следующих обстоятельств:

1) обычного выхода (добровольно);.

2) выхода при возникновении ошибки (добровольно);

3) возникновения фатальной ошибки (принудительно);

4) уничтожения другим процессом (принудительно).

Большинство процессов завершаются по окончании своей работы. Когда компилятор откомпилирует заданную ему программу, он осуществляет системный вызов, сообщающий операционной системе о завершении своей работы. Этим вызовом в UNIX является exit, а в Windows — ExitProcess. Программы, работающие с экраном, также поддерживают добровольное завершение. Текстовые процессоры, интернет-браузеры и аналогичные программы всегда содержат значок или пункт меню, на котором пользователь может щелкнуть, чтобы приказать процессу удалить все временные файлы, которые им были открыты, и завершить свою работу.

Вторая причина завершения — обнаружение процессом фатальной ошибки. Например, если пользователь наберет команду сс foo.c

с целью компиляции программы foo.c, а файла с таким именем не будет, то произойдет простой выход из компилятора. Выхода из интерактивных, использующих экран процессов при задании им неверных параметров обычно не происходит. Вместо этого появляется диалоговое окно с просьбой о повторной попытке ввода параметров.

Третья причина завершения — ошибка, вызванная самим процессом, чаще всего связанная с ошибкой в программе. В качестве примеров можно привести неверную инструкцию, ссылку на несуществующий адрес памяти или деление на нуль. В некоторых системах (например, UNIX) процесс может сообщить операционной системе о своем намерении обработать конкретные ошибки самостоятельно, в таком случае, когда встречается одна из таких ошибок, процесс получает сигнал (прерывается), а не завершается.

Четвертая причина, из-за которой процесс может быть завершен, — это выполнение процессом системного вызова, приказывающего операционной системе завершить некоторые другие процессы. В UNIX этот вызов называется kill. Соответствующая функция Win32 называется Terminate Process. В обоих случаях у процесса, вызывающего завершение, должны быть на это соответствующие полномочия. В некоторых системах при добровольном или принудительном завершении процесса тут же завершаются и все созданные им процессы. Но ни UNIX, ни Windows так не делают.

Минимум 2 этапа завершения:

1. Процесс удаляется из всех очередей планирования, то есть ОС больше не планирует выделение каких-либо ресурсов процессу,

2. Сбор статистики о потреблённых процессом ресурсах с последующим удалением его из памяти.

Причины завершения процесса:

· Обычный выход,

· Выход по исключению или ошибке,

· Недостаточный объем памяти,

· Превышение лимита отведённого программе времени,

· Выход за пределы отведённой области памяти,

· Неверная команда (данные программы интерпретируются как инструкции для процессора),

· Ошибка защиты (выполнение непривилегированной команды),

· Завершение родительского процесса,

· Ошибка ввода-вывода,

· Вмешательство оператора.

 

3.Блокировка файлов.

Важный вопрос в любой системе с несколькими процессами —

координация и синхронизация доступа к совместно используемым объектам,

например файлам.

Win32 может блокировать файлы полностью или частично, т.е. так, чтобы

никакой другой процесс (выполняемая программа) не мог обращаться к

блокированной области файла. Заблокированный файл может быть открыт

только для чтения (совместный доступ) или для чтения и записи (монопольный

доступ). Следует подчеркнуть, что блокировка связана с процессом. Любая

попытка обратиться к части файла (с помощью ReadFile или WriteFile) в

нарушение существующей блокировки потерпит неудачу, так как блокировка

обязательна на уровне процесса. Любая попытка получить конфликтную

блокировку также будет неудачной, даже если процесс уже имеет блокировку.

Блокировка файлов — это ограниченная форма синхронизации параллельных

процессов и потоков.

Наиболее общая функция блокировки LockFileEx реализована только в

Windows 2000/NT. Менее общая функция LockFile может применяться в

Windows 9x.

LockFileEx принадлежит к классу функций расширенного ввода-вывода;

для указания 64-разрядных позиций в файле и размера блокируемой области

требуется структура перекрытия, которая уже применялась ранее для указания

позиции в функциях ReadFile и WriteFile.

BOOL LockFileEx ( HANDLE hFile, DWORD dwFlags,

DWORD dwReserved, DWORD nNumberOfBytesToLockLow,

DWORD nNumberOfBytesToLockHigh,

LPOVERLAPPED lpOverlapped)

Функция LockFileEx блокирует область байтов в открытом файле для

совместного (несколько читающих процессов) или монопольного (один

читающий и пишущий процесс) доступа.

Параметры

hFile — дескриптор открытого файла. Дескриптор должен иметь права

доступа либо GENERIC_READ, либо GENERIC_READ и GENERIC_WRITE.

dwFlags определяет режим блокировки и устанавливает, следует ли

ожидать, пока блокировка не станет возможна.

• LOCKFILE_EXCLUSIVE_LOCK обозначает требование на

монопольную блокировку с возможностью чтения/записи. Если этот флаг

не установлен, запрашивается совместная блокировка (с возможностью

чтения).

• LOCKFILE_FAIL_IMMEDIATELY указывает, что функция должна

завершиться немедленно, возвратив значение FALSE, если блокировку

нельзя установить. Если этот флаг не установлен, вызов функции

блокируется, пока блокировка файла не станет возможна.

dwReserved должен быть равен нулю. Следующие два параметра задают

длину области байтов и поэтому здесь не поясняются.

lpOverlapped указывает на структуру типа OVERLAPPED, содержащую

начало области байтов. Структура OVERLAPPED содержит три компонента

данных, которые должны быть заданы (остальные компоненты игнорируются);

первые два определяют позицию начала блокируемой области.

• DWORD offset (именно так, а не OffsetLow).

• DWORD OffsetHigh.

• HANDLE hEvent должен быть равен нулю.

Блокировка файла снимается с помощью функции UnlockFileEx;

параметры для нее указываются те же самые, кроме dwFlags.

BOOL UnlockFileEx ( HANDLE hFile, DWORD dwReserved,

DWORD nNumberOfBytesToLockLow,

DWORD nNumberOfBytesToLockHigh,

LPOVERLAPPED lpOverlapped)

При блокировке файлов следует учитывать несколько факторов.

• Снятие блокировки должно относиться точно к такой же области, что и

предшествующая блокировка. Не допускается, например, разблокировать

две заблокированных ранее области или часть заблокированной области.

Попытка разблокировать область, которая не точно соответствует

существующей блокировке, потерпит неудачу; функция возвратит FALSE,

и системное сообщение укажет, что блокировки не существует.

• Блокировки не могут пересекаться с ранее блокированными областями

файла, если при этом возникает конфликт.

• Допускается блокировать область, выходящую за пределы файла. Этот

подход может быть полезен, когда процесс или поток расширяет файл.

• Блокировки не наследуются вновь создаваемым процессом.

В табл. 9.1 показана логика блокировки в случае, когда вся область или ее

часть уже имеют блокировку. Эта логика действует и тогда, когда предыдущая

блокировка принадлежит этому же процессу.

Таблица 9.1. Логика требования блокировки

Требуемый тип блокировки

Существующая

блокировка

Совместная

блокировка

Монопольная

блокировка

Нет Предоставляется Предоставляется

Совместная (одна

или несколько)

Предоставляется Предоставляется

Исключительная Не предоставляется Не предоставляется

В табл. 9.2 показана логика, применяемая в том случае, когда процесс

пытается выполнить чтение или запись в области файла с одной либо

несколькими блокировками, принадлежащими разным процессам, во всей или в

части области, допускающей чтение/запись. Неудачное чтение или запись

может принимать форму частично завершенной операции, если блокирована

только часть записи.

Таблица 9.2. Блокировки и операции ввода-вывода

Операция ввода-вывода Существующа

я блокировка

Чтение Запись

Нет Проходит успешно Проходит успешно

Совместная

(одна или

несколько)

Проходит успешно. Вызываю-

щий процесс не обязательно

должен владеть блокировкой

Неудача

Исключитель

ная

Проходит успешно, если бло-

кировкой владеет вызывающий

процесс. В противном случае —

неудача

Проходит успешно, если бло-

кировкой владеет вызывающий

процесс. В противном случае —неудача

Операции чтения и записи обычно имеют форму функций ReadFile и

WriteFile или их расширенных версий ReadFileEx и WriteFileEx. Для

диагностики неудачи чтения или записи вызывается GetLastError.

Еще одна форма ввода-вывода — обращение к памяти, в которую

отображается файл. Конфликты блокировки не обнаруживаются при

обращении к памяти; они проявляются, когда вызывается функция

MapViewOfFile. Эта функция делает часть файла доступной для процесса, а

потому в этот момент следует проверить блокировку.

LockFile — это ограниченная специальная версия функции блокировки.

Она обеспечивает рекомендательную блокировку (advisory locking). В этом

случае предоставляется только монопольный доступ и функция возвращает

управление немедленно. Иначе говоря, LockFile не блокируется. Установлена

ли блокировка, можно проверить по возвращаемому значению.

Снятие блокировки файла

За каждым успешным вызовом LockFileEx должно следовать одно

соответствующее ему обращение к UnlockFileEx (это правило

распространяется также на LockFile и UnlockFile). Если программа не может

снять блокировку или задерживает ее дольше, чем необходимо, другие

программы не смогут продолжать работу или же это негативно повлияет на их

быстродействие. Следовательно, программы должны быть аккуратно

спроектированы и реализованы так, чтобы блокировки снимались как можно

скорее; логики, которая заставляла бы программу обходить операцию разбло-

кировки, следует избегать.

Удобный способ убедиться, что блокировка снята, предоставляют

обработчики завершения.

 

 



2016-01-02 483 Обсуждений (0)
ЭКЗАМЕНАЦИОННЫЙ БИЛЕТ № 25 0.00 из 5.00 0 оценок









Обсуждение в статье: ЭКЗАМЕНАЦИОННЫЙ БИЛЕТ № 25

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:
Генезис конфликтологии как науки в древней Греции: Для уяснения предыстории конфликтологии существенное значение имеет обращение к античной...
Как распознать напряжение: Говоря о мышечном напряжении, мы в первую очередь имеем в виду мускулы, прикрепленные к костям ...



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (483)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.023 сек.)