Создание процесса
Самыми первыми создаются процессы в момент загрузки ОС.
1) Загрузка системы
При инициализации системы создаются несколько исходных процессов
- В Unix – это процессы «демоны» Sched(pid0) init(pid1) – другие высокоуровневые (веб-сервер, емейл-сервер). Ядро – не процесс. Идентификаторы в Unix идут последовательно с приращением 1.
- В Windows NT ядро – это системный процесс System(Pid4), далее загружаются система управления подсистемами smss. Идентификаторы в Win идут с приращением 4, идентификатор 0 зарезервирован, системный процесс — 4.
2) Текущий процесс порождает дочерний процесс.
Есть интересная особенность в построении ОС — так называемая иерархия процессов, исторически она существовала в ОС класса Unix. Там каждый процесс имеет строгое родство – есть родительский процесс и может быть дочерний процесс.
- Пример веб-сервер может порождать дочерний процесс для обработки нового запроса(это не есть хорошо, работает медленно);
- В Unix процесс init отслеживает авторизации пользователя для того, чтобы запустить оболочку(новые процессы).
3) Пользователь создает новый процесс
Пользователь вызывает команду из текстовой оболочки или запускает новую программу из графической оболочки. Это создает новый процесс, родитель которого – оболочка ОС.
Этапы создания процесса
Чтобы создать процесс надо:
- Присвоить уникальный идентификатор новому процессу
- Выделить ему место в памяти (для программы, данных и стека) – физически в памяти выделяются некоторые страницы (создается образ процесса на диске)
- Инициализировать РСВ (блок управления процессом)
- Добавить процесс в очередь «готовых» к выполнению.
Иерархия процессов ОС Unix
в UNIX cтрогая иерархия между процессами: дочерний и родительский всегда взаимосвязаны.
Идентификатор командной строки (оболочка пользователя Shell) является родительской для всех процессов, которые пользователь запускает из командной строки.
Существует понятие Группа процессов – любой процесс может принадлежать какой – либо группе, посылаем сигнал группе и его выполняют все процессы в группе.
Если пользователь посылает сигнал (напр. SIGKILL на завершение) группе процессов, то сигнал доставляется каждому процессу из группы. Это достаточно удобно для работы с группами процессов.
Создание процесса ОС Unix
Процессы создаются через две функции Fork() и exec()
- Начинается с Fork(), он создает точный клон вызывающего процесса, так называемый «дочерний» процесс
- Менеджер исполнения exec() заменяет образ процесса этого клона новой программой, которая должна быть выполнена.
Так сложилось исторически и другого способа породить процесс нет, поэтому существует иерархия, как основа основ.
После создания у родительского и дочернего процессов возникают собственные разные адресные пространства.
Некоторые ресурсы могут быть общими (например открытые файлы).
Поэтому системный вызов fork() возвращается дважды – один раз в родительский процесс, другой раз во вновь созданный.
Рассмотрим ключевой механизм создание процесса в ОС Unix.
Создание процесса ОС Unix ключевой механизм
создание процесса ОС Unix
После вызова Fork() создается почти такой же РСВ (блок управления процессом), только будет другой идентификатор у процесса, адресное пространство аналогичное.
Идентификатор берется из таблицы(она есть у каждой ОС).
Недостаток Unix — Fork(), эта функция работает очень медленно, надо вначале создать точную копию процесса, а затем уже ее заменить на новый процесс.
Как создать новую программу, а не еще одну копию старой?
Легко. Вначале fork(), потом exec().
Еxec() не создает новый процесс, а заменяет данные текущего процесса новыми данными.
У такой модели есть недостатки:
-Folk() очень медленный, нужно создавать полную копию всего, а потом ее заменять.
Решение было найдено в функциях vfolk() copy-on-write.
В операционной системе Linux есть функции:
Clone() – заменяет fork()/vfork()
Clone() обладает дополнительными опциями. Exec() в Linex не является системным вызовом.
Execve() – единственный вызов, аналогичный по функционалу exec().
Создание процесса ОС Windows
В ОС Windows процессы создаются через системный вызов NtCreatProcess() – данный вызов имеет множество параметров, многие из которых «по умолчанию».
Процесс порождается непосредственно по желанию, он не должен быть обязательной копией текущего, поэтому не соблюдается родство.
Хэндл процесса – существительное (дословно переводится «ручка»). Это сущность доступа к процессу – некоторое целое число, которое является индексом в определенной таблице и позволяет идентифицировать этот процесс – контроль доступа, права, наследование.
Pid – это смещение (индекс) в таблице процессов.
Иерархия процессов в ОС Windows
Ее нет, все равны. Поэтому встает вопрос взаимодействия.
Хэндл процесса – когда новый процесс создается родительским, то родитель получает хэндл дочернего процесса и таким образом может им управлять.
Этот хендл может передаваться другим процессам, в отличии от Unix, где родительский процесс не может менять множество дочерних процессов.
Хэндл процесса – идентификатор объекта процесса.
Переключение между процессами (самый простой вариант)
При необходимости переключиться на другой процесс ОС выполняет «переключение контекста».
- Состояние старого процесса сохраняется в его PCB;
- Состояние нового процесса восстанавливается в его PCB.
Время на переключение контекста – накладные расходы ОС (чем меньше время, тем быстрее работает).
Зависит от аппаратной реализации (т.е.от «железа компьютера», оптимизируется аппаратно).
Чем вызваны переключения? События, вызывающие переключения контекста:
- Прерывания
- Исключения
- Системные вызовы
Процесс состоит как минимум из:
1) Адресного пространства (набор инструкций –код программы, данные)
2) Состояние потока выполнения
Характеризуется состояние потока регистрами ЦП
- Счетчик команд (регистр IP)
- Указатель стека (SP)
- Другие регистры ЦП
3) Множество ресурсов ОС, которыми процесс владеет в данное время (открытые файлы, сетевые соединения) .
Все это находится в одном понятии процесса. Но это не есть хорошо. Три несвязанных между собой процесса хорошо бы разделить на три области.
Приходит на помощь понятие ПОТОК.
В следующей лекции рассмотрим это понятие.
Скачать презентацию к лекции «Процессы и потоки»
Скачать тест по теме «Процессы и потоки»
Понравилась статья, рекомендуйте Вашим друзьям!
Давайте дружить!
Для управления процессами в ядре Windows
используется объект Process
(процесс). Создание процесса осуществляется
вызовом функции CreateProcess:
BOOL
CreateProcess(
LPCTSTR
lpApplicationName,
// name of executable module
LPTSTR
lpCommandLine,
// command line string
LPSECURITY_ATTRIBUTES
lpProcessAttributes,
// SD
LPSECURITY_ATTRIBUTES
lpThreadAttributes,
// SD
BOOL
bInheritHandles,
// handle inheritance option
DWORD
dwCreationFlags,
// creation flags
LPVOID
lpEnvironment,
// new environment block
LPCTSTR
lpCurrentDirectory,
// current directory name
LPSTARTUPINFO
lpStartupInf, //
startup information
LPPROCESS_INFORMATION
lpProcessInformation //
process information
);
Как можно видеть, эта функция выглядит
не совсем традиционно для функций
создания объектов ядра. Вызвано это
тем, что она, на самом деле, создает не
один, а два объекта – процесс и поток
(существование в системе процесса без
потоков невозможно). Поэтому этой функции
передается два различных дескриптора
защиты, и возвращаемым значением является
не идентификатор созданного процесса,
а логическое значение, сообщающее об
успехе или не успехе операции. Получить
же дескрипторы созданных объектов мы
можем из структуры PROCESS_INFORMATION,
указатель на которую следует передать
последним аргументом нашей функции:
typedef struct
_PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
}
PROCESS_INFORMATION;
Наряду с дескрипторами процесса и потока
мы видим в этой структуре и значения
идентификаторов dwProcessId
и dwThreadId,
которые являются общесистемными
идентификаторами процесса и потока
соответственно. Если бы мы не имели
таких идентификаторов, а пользовались
бы только процессно-зависимыми
дескрипторами, то наладить взаимодействие
между процессами в системе было бы очень
тяжело.
Еще одной особенностью этой функции
является то, что созданные объекты
получают при инициализации значение
счетчика использования равное 2. Это
происходит потому, что объект процесс
используется как для работы самого
процесса, так и в родительском процессе,
получившим значение hProcess
после вызова функции CreateProcess.
Уничтожение объекта процесс (и поток)
в системе может таким образом произойти
только после того, как завершится сам
процесс (счетчик будет уменьшен на 1) и
будет закрыт дескриптор в родительском
процессе (счетчик уменьшен еще на 1).
Поэтому обязательно нужно закрыть
полученные дескрипторы с помощью функции
CloseHandle
после того, как они станут вам не нужны
(их можно использовать для того, чтобы
узнавать состояние процесса или код
его завершения).
Завершение процесса
Не нужно, однако, путать закрытие
дескриптора процесса и завершение
самого процесса. В первом случае мы
просто отказываемся от доступа к
процессу, сам процесс при этом не
уничтожается (на то у него есть еще одна
единичка в счетчике жизни) и вполне
может продолжать работу. Завершение
работы процесса может произойти в одном
из четырех случаев:
-
входная функция первичного потока
(обычно это main,
wmain,
WinMain
или wWinMain)
возвращает управление; -
один из потоков процесса вызывает
функцию ExitProcess; -
процесс другого потока вызывает функцию
TerminateProcess; -
все потоки процесса умирают.
Из всего этого многообразия лучшим
вариантом является первый. Только этот
способ гарантирует, что ваша программа
завершилась корректно. Конечно, Windows
в любом случае освободит все использованные
вашей программой ресурсы, однако при
аварийном завершении процесса не будут,
например, выполнены деструкторы объектов
C++, что может привести к
не сохраненным пользовательским данным,
не записанным изменениям в конфигурации
и так далее. Библиотека C++
(да и любого другого языка) на самом деле
выполняет довольно много полезных
операций, как перед стартом вашей
программы, так и после ее завершения, и
не надо мешать ей это делать. Библиотека
сама вызовет ExitProcess,
когда этому придет время.
Если же все-таки возникла необходимость
аварийно завершить процесс, то мы можем
воспользоваться функциями ExitProcess
или TerminateProcess.
Первая из этих функций уничтожает тот
процесс, который ее вызвал, а вторая
позволяет завершить любой процесс в
системе (если конечно к нему есть доступ),
указав его дескриптор. Обе функции
позволяют установить код возврата
процесса. После вызова этих функций наш
процесс прекратит свое существование,
все системные ресурсы будут освобождены,
однако возможности сохранить данные
процесс не получит.
Завершается процесс и в том случае, если
в нем не останется выполняющихся потоков.
Произойти такое может, например, в том
случае если потоки будут уничтожены
функциями ExitThread
или TerminateThread.
Такое завершение работы тоже нельзя
рассматривать как корректное.
Заметим, что завершение родительского
процесса в Windows не приводит
к завершению запущенных им дочерних
процессов.
При завершении процесса происходит
следующее:
-
выполнение всех потоков в процессе
прекращается; -
все открытые процессом объекты USER
и GDI уничтожаются, а
объекты ядра закрываются; -
код возврата процесса меняется со
значения STILL_ACTIVE
(0x103) на свое значение; -
объект ядра «процесс» переходит в
свободное (signaled) состояние
(про это состояние будет рассказано в
разделе про Wait-функции); -
счетчик использования объекта «процесс»
уменьшается на 1.
Если после выполнения этой операции
счетчик использования процесса все еще
не равен 0 (есть открытые дескрипторы в
других процессах), то сам объект ядро
не уничтожается. Однако использовать
этот объект можно только для того, чтобы
обращаться к Wait-функциями
или получить код возврата, используя
функцию GetExitCodeProcess.
После того, как все оставшиеся дескрипторы
будут закрыты, Windows
уничтожит и сам объект «процесс».
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
10.1 Создание процесса
Операционным системам необходим какой-нибудь способ для создания процессов. В самых простых системах или в системах, сконструированных для запуска только одного приложения (например, в контроллере микроволновой печи), появляется воз¬можность присутствия абсолютно всех необходимых процессов при вводе системы в действие. Но в универсальных системах нужны определенные способы создания и прекращения процессов по мере необходимости.
Существуют четыре основных события, приводящих к со¬зданию процессов.
- Инициализация системы.
- Выполнение работающим процессом системного вы¬зова, предназначенного для создания процесса.
- Запрос пользователя на создание нового процесса.
- Инициация пакетного задания.
При запуске операционной системы создаются, как правило, несколько процессов. Некоторые из них представляют собой высо-коприоритетные процессы, то есть процессы, взаимодействующие с пользователями и выполняющие для них определенную работу. Остальные являются фоновыми процессами, не связанными с кон¬кретными пользователями, но выполняющими ряд специфических функций. Например, фоновый процесс, который может быть создан для приема входящих сообщений электронной почты, основную часть времени проводит в спящем режиме, активизируясь только по мере появления писем. Другой фоновый процесс, который может быть создан для приема входящих запросов на веб-страницы, раз¬мещенные на машине, просыпается при поступлении запроса с це¬лью его обслуживания. Фоновые процессы, предназначенные для обработки какой-либо активной деятельности, связанной, напри¬мер, с электронной почтой, веб-страницами, новостями, выводом информации на печать и т. д., называются демонами. Обычно у больших систем насчитываются десятки демонов. В UNIX1 для отображения списка запущенных процессов может быть использо¬вана программа ps. В Windows для этой цели может использоваться диспетчер задач.
Вдобавок к процессам, созданным во время загрузки, новые процессы могут быть созданы и после нее. Часто бывает так, что работающий процесс осуществляет системный вызов для создания одного или более новых вспомогательных процессов. Создание но¬вых процессов особенно полезно, когда выполняемая работа может быть легко выражена в понятиях нескольких связанных друг с дру¬гом, но в остальном независимых друг от друга взаимодействую¬щих процессов. Например, если из сети выбирается большой объем данных для последующей обработки, наверное, будет удобно со¬здать один процесс для выборки данных и помещения их в общий буфер, чтобы в то же самое время второй процесс забирал эле¬менты данных и проводил их обработку. Также можно ускорить выполнение работы, если на многопроцессорной системе разре-шить каждому процессу работать на разных центральных процес¬сорах.
В интерактивных системах пользователи могут запустить программу вводом команды или щелчком (двойным щелчком) на значке. Любое из этих действий дает начало новому процессу и за¬пускает в нем выбранную программу. В основанных на применении команд UNIX-системах с работающей X-оболочкой новый процесс получает окно, в котором он был запущен. При запуске в Microsoft Windows процесс не имеет окна, но он может создать одно или не¬сколько окон, и в большинстве случаев так и происходит. В обеих системах пользователи могут одновременно открыть несколько окон, в каждом из которых запущен какой-нибудь процесс. Исполь¬зуя мышь, пользователь может выбрать окно и взаимодействовать с процессом, например, если потребуется, вводить данные.
Последнее событие, приводящее к созданию процесса, при¬менимо только к системам пакетной обработки данных, имею¬щимся на больших универсальных машинах. Представьте себе управление запасами товаров в конце рабочего дня в сети магази¬нов. Здесь пользователи могут отправлять системе пакетные зада¬ния (возможно, с помощью удаленного доступа). Когда операцион¬ная система решает, что у нее достаточно ресурсов для запуска еще одного задания, она создает новый процесс и запускает новое зада¬ние из имеющейся у нее очереди входящих заданий.
С технической точки зрения во всех этих случаях новый процесс создается за счет уже существующего процесса, который выполняет системный вызов, предназначенный для создания про¬цесса. Этим процессом может быть работающий пользовательский процесс, системный процесс, вызванный событиями клавиатуры или мыши, или процесс управления пакетными заданиями. Данный процесс осуществляет системный вызов для создания нового про¬цесса. Этот системный вызов предписывает операционной системе создать новый процесс и прямо или косвенно указывает, какую программу в нем запустить.
В UNIX существует только один системный вызов для со¬здания нового процесса — fork. Этот вызов создает точную копию вызывающего процесса. После выполнения системного вызова fork два процесса, родительский и дочерний, имеют единый образ па¬мяти, единые строки описания конфигурации и одни и те же откры¬тые файлы. И больше ничего. Обычно после этого дочерний про¬цесс изменяет образ памяти и запускает новую программу, выпол¬няя системный вызов execve или ему подобный. Например, когда пользователь набирает в оболочке команду sort, оболочка создает ответвляющийся дочерний процесс, в котором и выполняется ко¬манда sort. Смысл этого двухступенчатого процесса заключается в том, чтобы позволить дочернему процессу управлять его файло¬выми дескрипторами после разветвления, но перед выполнением execve с целью выполнения перенаправления стандартного ввода, стандартного вывода и стандартного вывода сообщений об ошиб¬ках.
В Windows все происходит иначе: одним вызовом функции Win32 CreateProcess создается процесс, и в него загружается нуж¬ная программа. У этого вызова имеется 10 параметров, включая выполняемую программу, параметры командной строки для этой программы, различные параметры безопасности, биты, управляю¬щие наследованием открытых файлов, информацию о приоритетах, спецификацию окна, создаваемого для процесса (если оно исполь¬зуется), и указатель на структуру, в которой вызывающей про¬грамме будет возвращена информация о только что созданном про¬цессе. В дополнение к функции CreateProcess в Win32 имеется около 100 других функций для управления процессами и их син-хронизации, а также выполнения всего, что с этим связано.
В обеих системах, UNIX и Windows, после создания про¬цесса родительский и дочерний процессы обладают своими соб¬ственными, отдельными адресными пространствами. Если какой-нибудь процесс изменяет слово в своем адресном пространстве, другим процессам эти изменения не видны. В UNIX первоначаль¬ное состояние адресного пространства дочернего процесса является копией адресного пространства родительского процесса, но это аб¬солютно разные адресные пространства — у них нет общей памяти, доступной для записи данных. Некоторые реализации UNIX делят между процессами текст программы без возможности его модифи¬кации. Кроме того, дочерний процесс может совместно использо¬вать всю память родительского процесса, но если память совместно используется в режиме копирования при записи (copy on write), это означает, что при каждой попытке любого из процессов модифици¬ровать часть памяти эта часть сначала явным образом копируется, чтобы гарантировать модификацию только в закрытой области па¬мяти. Следует также заметить, что память, используемая в режиме записи, совместному использованию не подлежит.
Тем не менее вновь созданный процесс может делить со своим создателем часть других ресурсов, например открытые файлы. В Windows адресные пространства родительского и дочер¬него процессов различаются с самого начала.
10.2. Завершение процесса
После создания процесс начинает работать и выполняет свою задачу. Но ничто не длится вечно, даже процессы. Рано или поздно новые процессы будут завершены, обычно в силу следую¬щих обстоятельств:
• обычного выхода (добровольно);
• выхода при возникновении ошибки (добровольно);
• возникновения фатальной ошибки (принудительно);
• уничтожения другим процессом (принудительно).
Большинство процессов завершаются по окончании своей работы. Когда компилятор откомпилирует заданную ему про¬грамму, он осуществляет системный вызов, сообщающий операци¬онной системе о завершении своей работы. Этим вызовом в UNIX является exit, а в Windows — ExitProcess. Программы, работающие с экраном, также поддерживают добровольное завершение. Тексто¬вые процессоры, интернет-браузеры и аналогичные программы все¬гда содержат значок или пункт меню, на котором пользователь мо¬жет щелкнуть, чтобы приказать процессу удалить все временные файлы, которые им были открыты, и завершить свою работу.
Вторая причина завершения — обнаружение процессом фа¬тальной ошибки. Например, если пользователь наберет команду
cc foo.c
с целью компиляции программы foo.c, а файла с таким именем не будет, то произойдет простое объявление о наличии данного факта и выход из компилятора. Выхода из интерактивных, использующих экран процессов при задании им неверных параметров обычно не происходит. Вместо этого появляется диалоговое окно с просьбой о повторной попытке ввода параметров.
Третья причина завершения — ошибка, вызванная самим процессом, чаще всего связанная с ошибкой в программе. В каче¬стве примеров можно привести неверную инструкцию, ссылку на несуществующий адрес памяти или деление на нуль. В некоторых системах (например, UNIX) процесс может сообщить операцион¬ной системе о своем намерении обработать конкретные ошибки самостоятельно, в таком случае, когда встречается одна из таких ошибок, процесс получает сигнал (прерывается), а не завершается.
Четвертая причина, из-за которой процесс может быть за¬вершен, — это выполнение процессом системного вызова, прика¬зывающего операционной системе завершить некоторые другие процессы. В UNIX этот вызов называется kill. Соответствующая функция Win32 называется TerminateProcess. В обоих случаях у процесса, вызывающего завершение, должны быть на это соответ¬ствующие полномочия. В некоторых системах при добровольном или принудительном завершении процесса тут же завершаются и все созданные им процессы. Но ни UNIX, ни Windows так не делают.
Создание процесса
Создание процесса
Одной из важнейших функций Windows, обеспечивающих управление процессами, является функция CreateProcess, которая создает новый процесс с единственным потоком. При вызове этой функции требуется указать имя файла исполняемой программы.
Обычно принято говорить о процессах-предках, или родительских процессах (parent processes), и процессах-потомках, или дочерних процессах (child processes), однако между процессами Windows эти отношения фактически не поддерживаются. Использование данной терминология является просто удобным способом выражения того факта, что один процесс порождается другим.
Гибкие и мощные возможности функции CreateProcess обеспечиваются ее десятью параметрами. На первых порах для упрощения работы целесообразно использовать значения параметров, заданные по умолчанию. Точно так же, как и в случае функции CreateFile, имеет смысл подробно рассмотреть каждый из параметров функции CreateProcess. Благодаря этому изучить другие аналогичные функции вам будет гораздо легче.
Прежде всего, заметьте, что возвращаемое значение функции не является дескриптором типа HANDLE; вместо этого функция возвращает два отдельных дескриптора, по одному для процесса и потока, передавая их в структуре, которая указывается при вызове функции. Эти дескрипторы относятся к создаваемому функцией CreateProcess новому процессу и его основного (primary) потока. Во избежание утечки ресурсов в процессе работы с примерами программ тщательно следите за своевременным закрытием обоих дескрипторов, когда они вам больше не нужны; забывчивость в отношении закрытия дескрипторов потоков является одной из самых распространенных ошибок. Закрытие дескриптора потока не приводит к прекращению ее выполнения; функция CloseHandle лишь удаляет ссылку на поток внутри процесса, вызвавшего функцию CreateProcess.
BOOL CreateProcess(lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpsaProcess, LPSECURITY_ATTRIBUTES lpsaThread, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurDir, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcInfo)
Возвращаемое значение: в случае успешного создания процесса и потока — TRUE, иначе — FALSE.
Параметры
Некоторые параметры потребуют дальнейшего подробного обсуждения в следующих разделах, тогда как смысл многих других станет для вас более понятным при рассмотрении примеров программ.
lpApplicationName и lpCommandLine (последний указатель имеет тип LPTSTR, а не LPCTSTR) — используются вместе для указания исполняемой программы и аргументов командной строки, о чем говорится в следующем разделе.
lpsaProcess и lpsaThread — указатели на структуры атрибутов защиты процесса и потока. Значениям NULL соответствует использование атрибутов защиты, заданных по умолчанию, и именно эти значения будут использоваться нами вплоть до главы 15, посвященной рассмотрению средств безопасности Windows.
bInheritHandles — показывает, наследует ли новый процесс наследуемые открытые дескрипторы (файлов, отображений файлов и так далее) из вызывающего процесса. Наследуемые дескрипторы имеют те же атрибуты, что и исходные, и их обсуждение будет продолжено в одном из следующих разделов.
dwCreationFlags — может объединять в себе несколько флаговых значений, включая следующие:
• CREATE_SUSPENDED — указывает на то, что основной поток будет создан в приостановленном состоянии и начнет выполняться лишь после вызова функция ResumeThread.
• DETACHED_PROCESS и CREATE_NEW_CONSOLE — взаимоисключающие значения, которые не должны устанавливаться оба одновременно. Первый флаг означает создание нового процесса, у которого консоль отсутствует, а второй — процесса, у которого имеется собственная консоль. Если ни один из этих флагов не указан, то новый процесс наследует консоль родительского процесса.
• Create_New_Process_Group — указывает на то, что создаваемый процесс является корневым для новой группы процессов. Если все процессы, принадлежащие данной группе, разделяют общую консоль, то все они будут получать управляющие сигналы консоли (Ctrl-C или Ctrl-break). Обработчики управляющих сигналов консоли описывались в главе 4, а их применение было продемонстрировано в программе 4.5. Упомянутые группы процессов в некотором отношении аналогичны группам процессов UNIX и рассматриваются далее в этой главе.
Некоторые из флагов управляют приоритетами потоков нового процесса. О возможных значениях этих флагов более подробно говорится в главе 7. Пока же нам будет достаточно использовать приоритет родительского процесса (этот режим устанавливается по умолчанию) или указывать значение NORMAL_PRIORITY_CLASS.
lpEnvironment — указывает на блок параметров настройки окружения нового процесса. Если задано значение NULL, то новый процесс будет использовать значения параметров окружения родительского процесса. Блок параметров содержит строки, в которых заданы пары «имя-значение», определяющие, например, пути доступа к файлам.
lpCurDir — указатель на строку, содержащую путь к текущему каталогу нового процесса. Если задано значение NULL, то в качестве текущего каталога будет использоваться рабочий каталог родительского процесса.
lpStartupInfo — указатель на структуру, которая описывает внешний вид основного окна и содержит дескрипторы стандартных устройств нового процесса. Используйте соответствующую информацию из родительского процесса, которую можно получить при помощи функции GetStartupInfo. Можно поступить и по-другому, обнулив структуру STARTUPINFO перед вызовом функции CreateProcess. Для указания стандартных устройств ввода, вывода информации и вывода сообщений об ошибках следует определить значения полей дескрипторов стандартных устройств (hStdInput, hStdOutput и hStdError) в структуре STARTUPINFO. Чтобы эти значения не игнорировались, следует задать для другого элемента этой же структуры, а именно, элемента dwFlags, значение STARTF_USESTDHANDLES и определить все дескрипторы, которые потребуются дочернему процессу. Убедитесь в том, что эти дескрипторы являются наследуемыми и что при вызове функции CreateProcess значение параметра bInheritHandles установлено равным TRUE. Более подробная информация по этому вопросу, сопровождаемая соответствующим примером, приводится в разделе «Наследуемые дескрипторы».
lpProInfо — указатель на структуру, в которую будут помещены возвращаемые функцией значения дескрипторов и глобальных идентификаторов процесса и потока. Структура PROCESS_INFORMATION, о которой идет речь, имеет следующий вид:
typedef struct PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
Зачем процессам и потокам нужны еще и дескрипторы, если они снабжаются глобальными идентификаторами (ID)? Глобальные идентификаторы остаются уникальными для данного объекта на протяжении всего времени его существования и во всех процессах, тогда дескрипторов процесса может быть несколько и каждый из которых может характеризоваться собственным набором атрибутов, например определенными разрешениями доступа. В силу указанных причин одним функциям управления процессами требуется предоставлять идентификаторы процессов, а другим — дескрипторы. Кроме того, необходимость в дескрипторах процессов возникает при использовании универсальных функций, которые требуют указания дескрипторов. В качестве примера можно привести функции ожидания, обсуждаемые далее в этой главе, которые обеспечивают отслеживание переходов объектов различного типа, в том числе и процессов, указываемых с помощью дескрипторов, в определенные состояния. Точно так же, как и дескрипторы файлов, дескрипторы процессов и потоков должны закрываться сразу же после того, как необходимость в них отпала.
Примечание
Новый процесс получает информацию об окружении, рабочем каталоге и иную информацию в результате вызова функции CreateProcess. По завершении этого вызова любые изменения характеристик родительского процесса никак не отразятся на дочернем процессе. Так, после вызова функции CreateProcess рабочий каталог родительского процесса может измениться, но на дочерний процесс это не окажет никакого влияния, если только он сам не сменит рабочий каталог. Оба процесса полностью независимы друг от друга.
Модели процесса в UNIX и Windows значительно отличаются друг от друга. Прежде всего, в Windows отсутствует эквивалент UNIX-функции fork, создающей копию родительского процесса, включая его пространство данных, кучу и стек. В Windows трудно добиться точной эмуляции fork, но как ни расценивать последствия этого ограничения, остается фактом, что проблемы с использованием функции fork существуют и в многопоточных системах UNIX, поскольку любые попытки создания точной реплики многопоточной системы с копиями всех потоков и объектов синхронизации, особенно в случае SMP-систем, приводят к возникновению множества трудностей. Поэтому в действительности функция fork вообще плохо подходит для многопоточных систем.
В то же время, функция CreateProcess аналогична обычной для UNIX цепочке последовательных вызовов функций fork и execl (или одной из пяти остальных функций exec). В отличие от Windows пути доступа в UNIX определяются исключительно переменной среды PATH.
Как ранее уже отмечалось, отношения «предок-потомок» между процессами в Windows не поддерживаются. Так, выполнение дочернего процесса будет продолжаться даже после того, как завершится родительский процесс. Кроме того, в Windows отсутствуют группы процессов. Существует, однако, ограниченная форма группы процессов, в которой все процессы получают управляющие события консоли.
Процессы Windows идентифицируются как дескрипторами, так и идентификаторами процессов, тогда как в UNIX дескрипторы процессов отсутствуют.
Читайте также
9.1.1. Создание процесса: fork()
9.1.1. Создание процесса: fork()
Первым шагом в запуске новой программы является вызов fork():#include <sys/types.h> /* POSIX */#include <unistd.h>pid_t fork(void);Использование fork() просто. Перед вызовом один процесс, который мы называем родительским, является запущенным. Когда fork() возвращается, имеется
Создание процесса
Создание процесса
Одной из важнейших функций Windows, обеспечивающих управление процессами, является функция CreateProcess, которая создает новый процесс с единственным потоком. При вызове этой функции требуется указать имя файла исполняемой программы.Обычно принято говорить о
Создание нового процесса
Создание нового процесса
В операционной системе Unix создание процессов происходит уникальным образом. В большинстве операционных систем для создания процессов используется метод порождения процессов (spawn). При этом создается новый процесс в новом адресном пространстве,
Создание нового процесса
Создание нового процесса
Созданию процессов (имеется в виду создание процесса из программного кода) посвящено столько описаний [1-9], что детальное рассмотрение этого вопроса было бы лишь пересказом. Поэтому мы ограничимся только беглым перечислением этих возможностей,
Внутри процесса ILE
Внутри процесса ILE
В этом разделе мы заглянем внутрь процесса ILE. Структура процесса ILE сложна, и, подобно многим другим затронутым нами темам, ее описание насыщено таким количеством имен, сокращений и терминов, что может загнать в угол любого специалиста по компьютерам. И
Атрибуты процесса
Атрибуты процесса
Процесс в UNIX имеет несколько атрибутов, позволяющих операционной системе эффективно управлять его работой, важнейшие из которых рассмотрены
Идентификаторы процесса
Идентификаторы процесса
Вы уже знаете, что каждый процесс характеризуется набором атрибутов и идентификаторов, позволяющих системе управлять его работой. Важнейшими из них являются идентификатор процесса PID и идентификатор родительского процесса PPID. PID является именем
Создание процесса
Создание процесса
Как уже обсуждалось, в UNIX проведена четкая грань между программой и процессом. Каждый процесс в конкретный момент времени выполняет инструкции некоторой программы, которая может быть одной и той же для нескольких процессов.[39] Примером может служить
Создание, завершение и просмотр учетной записи процесса
Создание, завершение и просмотр учетной записи процесса
К другим основным возможностям инструментария управления WMI относятся возможности работы с процессами, запущенными на удаленном или локальном компьютере. При этом инструментарий предоставляет возможности не
7.3.2. Концепции, касающиеся основных средств производственного процесса организации Основные средства производственного процесса организации (ППО)
7.3.2. Концепции, касающиеся основных средств производственного процесса организации
Основные средства производственного процесса организации (ППО)
Организация устанавливает и сопровождает набор основных средств производственного процесса, как показано на рис. 4.1. К
3.1.3. Уничтожение процесса
3.1.3. Уничтожение процесса
Для уничтожения процесса предназначена команда kill. Ей достаточно указать идентификатор требуемого процесса.Команда kill посылает процессу сигнал SIGTERM, являющийся запросом на завершение.[10] По умолчанию, если в программе отсутствует обработчик
3.4. Завершение процесса
3.4. Завершение процесса
Обычно процесс завершается одним из двух способов: либо выполняющаяся программа вызывает функцию exit(), либо функция main() заканчивается. У каждого процесса есть код завершения — число, возвращаемое родительскому процессу. Этот код передается в
Аннотация: Основные понятия. Структуры данных для процессов и потоков. Создание процесса.
Основные понятия
Программа (program) – это последовательность команд, реализующая алгоритм решения задачи. Программа может быть записана на языке программирования (например, на Pascal, С++, BASIC); в этом случае она хранится на диске в виде текстового файла с расширением, соответствующим языку программирования (например, .PAS, .CPP, .VB). Также программа может быть представлена при помощи машинных команд; тогда она хранится на диске в виде двоичного исполняемого файла (executable file), чаще всего с расширением .EXE. Исполняемый файл генерируется из текста программы при компиляции.
Процесс (process) – это программа (пользовательская или системная) в ходе выполнения.
В современных операционных системах процесс представляет собой объект – структуру данных, содержащую информацию, необходимую для выполнения программы. Объект «Процесс» создается в момент запуска программы (например, пользователь дважды щелкает мышью на исполняемом файле) и уничтожается при завершении программы.
Если операционная система умеет запускать в одно и то же время несколько процессов, она называется многозадачной (multitasking) (пример – Windows), иначе – однозадачной (пример – MS DOS).
Процесс может содержать один или несколько потоков (thread) – объектов, которым операционная система предоставляет процессорное время. Сам по себе процесс не выполняется – выполняются его потоки. Таким образом, машинные команды, записанные в исполняемом файле, выполняются на процессоре в составе потока. Если потоков несколько, они могут выполняться одновременно.
Замечание. «Одновременное» (или «параллельное») выполнение потоков подразумевает одну из двух принципиально разных ситуаций, зависящих от количества процессоров (ядер) на компьютере. В том случае, если имеется всего один процессор с одним ядром, в один момент времени может выполняться только один поток. Однако операционная система может быстро переключать процессор с выполнения одного потока на другой и вследствие высокой частоты процессоров у пользователя возникает иллюзия одновременной работы нескольких программ. Такая ситуация называется псевдопараллельное выполнение потоков. Если в компьютере установлен многоядерный процессор или количество процессоров больше одного, то возможно истинно параллельное или просто параллельное выполнение потоков.
Операционные системы, поддерживающие несколько процессоров, называются многопроцессорными.
Если система допускает наличие нескольких потоков в одном процессе, она называется многопоточной (multithreading).
Многопоточность – это средство распараллеливания действий внутри процесса.
Примеры.
- В современных браузерах каждой вкладке соответствует свой поток.
- В текстовом редакторе один поток может управлять вводом текста, второй – проверять орфографию, третий – выводить документ на принтер.
- В компьютерной игре-стратегии за обработку действий каждого игрока отвечает отдельный поток.
Каждый процесс имеет свое собственное виртуальное адресное пространство (см. лекцию 11 «Управление памятью«), в котором независимо друг от друга работают все потоки процесса. Например, поток 1 может записывать данные в ячейку с адресом 100, а поток 2 читать данные из ячейки с адресом 101.
Замечание. Конечно, если два (или более) потоков захотят записать что-то свое в одну и ту же ячейку, возникнет неопределенность – кто раньше? Это одна из подзадач обширной проблемы синхронизации.
Каждый поток имеет свою область памяти – стек (stack), в которой хранятся, например, локальные переменные и параметры, передаваемые в функции. Кроме того, во время выполнения потока изменяются значения регистров процессора, которые при переключении на другой поток должны быть сохранены. Эта информация является частью контекста потока, поэтому при переключении потоков происходит переключение их контекстов (сохранение в память одного и загрузка другого).
Структуры данных для процессов и потоков
В WRK за управление процессами отвечает диспетчер процессов (base\ntos\ps), а многие важные структуры данных описаны в заголовочных файлах base\ntos\inc\ps.h и base\ntos\inc\ke.h.
Процесс в Windows описывается структурой данных EPROCESS [5]. Эта структура в WRK содержится в файле base\ntos\inc\ps.h (строка 238). Рассмотрим некоторые её поля.
- Pcb (Process Control Block – блок управления процессом) – представляет собой структуру KPROCESS, хранящую данные, необходимые для планирования потоков, в том числе указатель на список потоков процесса (файл base\ntos\inc\ke.h, строка 944).
- CreateTime и ExitTime – время создания и завершения процесса.
- UniqueProcessId – уникальный идентификатор процесса.
- ActiveProcessLinks – элемент двунаправленного списка (тип LIST_ENTRY), содержащего активные процессы.
- QuotaUsage, QuotaPeak, CommitCharge – квоты (ограничения) на используемую память.
- ObjectTable – таблица дескрипторов процесса.
- Token – маркер доступа.
- ImageFileName – имя исполняемого файла.
- ThreadListHead – двунаправленный список потоков процесса.
- Peb (Process Environment Block – блок переменных окружения процесса) – информация об образе исполняемого файла (файл public\sdk\inc\pebteb.h, строка 75).
- PriorityClass – класс приоритета процесса (см. лекцию 9 «Планирование потоков»).
Структура для потока в Windows называется ETHREAD и описывается в файле base\ntos\inc\ps.h (строка 545). Её основные поля следующие:
- Tcb (Thread Control Block – блок управления потоком) – поле, которое является структурой типа KTHREAD (файл base\ntos\inc\ke.h, строка 1069) и необходимо для планирования потоков.
- CreateTime и ExitTime – время создания и завершения потока.
- Cid – структура типа CLIENT_ID, включающая два поля – идентификатор процесса-владельца данного потока и идентификатор самого потока.
- ThreadsProcess – указатель на структуру EPROCESS процесса-владельца.
- StartAddress – адрес системной стартовой функции потока. При создании потока сначала вызывается системная стартовая функция, которая запускает пользовательскую стартовую функцию.
- Win32StartAddress – адрес пользовательской стартовой функции.
Создание процесса
Процессы создаются либо пользователем, либо другим процессом, либо автоматически при загрузке операционной системы.
Процесс, создавший другой процесс, называется родителем, а созданный процесс – потомком. Таким образом, формируется иерархия процессов.
Любой процесс начинает свою работу с основного (main), или первичного, потока, который может запускать (порождать) другие потоки – так образуется иерархия потоков.
В Windows для создания процессов применяется одна из следующих WinAPI-функций: CreateProcess, CreateProcessAsUser, CreateProcessWithTokenW, CreateProcessWithLogonW [10]. Далее при описании будем использовать функцию CreateProcess.
Создание процессов в Windows включает 7 этапов [5; 2].
1. Проверка и преобразование параметров.
Параметры функции CreateProcess проверяются на корректность и преобразуются к внутреннему формату системы.
2. Открытие исполняемого файла.
Происходит поиск файла, который содержит запускаемую программу. Обычно это файл с расширением .EXE, но могут быть также расширения .COM, .PIF, .BAT, .CMD. Определяется тип исполняемого файла:
- Windows приложение (.EXE) – продолжается нормальное создание процесса;
- приложение MS-DOS или Win16 (.EXE, .COM, .PIF) – запускается образ поддержки Ntvdm.exe;
- командный файл (.BAT, .CMD) – запускается образ поддержки Cmd.exe;
- приложение POSIX – запускается образ поддержки Posix.exe.
3. Создание объекта «Процесс».
Формируются структуры данных EPROCESS, KPROCESS, PEB, инициализируется адресное пространство процесса. Для этого вызывается системная функция NtCreateProcess (файл base\ntos\ps\create.c, строка 826), которая затем вызывает функцию NtCreateProcessEx (тот же файл, строка 884), а та, в свою очередь, функцию PspCreateProcess (тот же файл, строка 1016).
Замечание. Начиная с Windows Vista при создании процесса вызов нескольких функций (NtCreateProcess, NtWriteVirtualMemory, NtCreateThread) заменен вызовом одной функции NtCreateUserProcess.
Рассмотрим некоторые важные действия, выполняемые функцией PspCreateProcess.
- Если в параметрах функции PspCreateProcess указан процесс-родитель:
- по его дескриптору определяется указатель на объект EPROCESS (функция ObReferenceObjectByHandle, строка 1076);
- наследуется от процесса родителя маска привязки к процессорам (Affinity, строка 1092).
- Устанавливается минимальный и максимальный размеры рабочего набора (WorkingSetMinimum = 20 МБ и WorkingSetMaximum = 45 МБ, строки 1093 и 1094, см. лекцию 11 «Управление памятью»).
- Создается объект «Процесс» (структура EPROCESS) при помощи функции ObCreateObject (строка 1108).
- Инициализируется двунаправленный список потоков при помощи функции InitializeListHead (строка 1134).
- Копируется таблица дескрипторов родительского процесса (строка 1270).
- Создается структура KPROCESS при помощи функции KeInitializeProcess (строка 1289).
- Маркер доступа и другие данные, связанные с безопасностью (см. лекцию 13 «Безопасность», копируются из родительского процесса (функция PspInitializeProcessSecurity, строка 1302).
- Устанавливается приоритет процесса, равный Normal; однако, если приоритет родительского процесса был Idle или Below Normal, то данный приоритет наследуется (строки 1307–1312, см. лекцию 9 «Планирование потоков»).
- Инициализируется адресное пространство процесса (строки 1337–1476).
- Генерируется уникальный идентификатор процесса (функция ExCreateHandle) и сохраняется в поле UniqueProcessId структуры EPROCESS (строки 1482–1488).
- Создается блок PEB и записывается в соответствующее поле структуры EPROCESS (строки 1550–1607).
- Созданный объект вставляется в хвост двунаправленного списка всех процессов (строки 1613–1615) и в таблицу дескрипторов (строки 1639–1644). Первая вставка обеспечивает доступ к процессу по имени, вторая – по ID.
- Определяется время создания процесса (функция KeQuerySystemTime) и записывается в поле CreateTime структуры EPROCESS (строка 1733).
4. Создание основного потока.
Формируется структура данных ETHREAD, стек и контекст потока, генерируется идентификатор потока. Поток создается при помощи функции NtCreateThread, определенной в файле base\ntos\ps\create.c, (строка 117), которая вызывает функцию PspCreateThread (тот же файл, строка 295). При этом выполняются следующие действия:
- создается объект ETHREAD (строка 370).
- Заполняются поля структуры ETHREAD, связанные с процессом-владельцем, – указатель на структуру EPROCESS (ThreadsProcess) и идентификатор процесса (Cid.UniqueProcess) (строки 396 и 398).
- Генерируется уникальный идентификатор потока (функция ExCreateHandle) и сохраняется в поле Cid.UniqueThread структуры EPROCESS (строки 400–402).
- Заполняются стартовые адреса потока, системный (StartAddress) и пользовательский (Win32StartAddress) (строки 468-476).
- Инициализируются поля структуры KTHREAD при помощи вызова функции KeInitThread (строки 490–498 для потока пользовательского режима и 514–522 для потока режима ядра).
- Функция KeStartThread заполняет остальные поля структуры ETHREAD и вставляет поток в список потоков процесса (строка 564).
- Если при вызове функции PspCreateThread установлен флаг CreateSuspended («Приостановлен») поток переводится в состояние ожидания (функция KeSuspendThread, строка 660); иначе вызывается функция KeReadyThread (строка 809), которая ставит поток в очередь готовых к выполнению потоков (см. лекцию 9 «Планирование потоков»).
5. Уведомление подсистемы Windows.
Подсистеме Windows отправляется сообщение о вновь созданных процессе и его основном потоке, в которое входят их дескрипторы, идентификаторы и другая информация. Подсистема Windows добавляет новый процесс в общий список всех процессов и готовится к запуску основного потока.
6. Запуск основного потока.
Основной поток стартует, но начинают выполняться системные функции, завершающие создание процесса – осуществляется его инициализация.
7. Инициализация процесса.
- Проверяется, не запущен ли процесс в отладочном режиме;
- проверяется, следует ли производить предвыборку блоков памяти (тех участков памяти, которые при прошлом запуске использовались в течение первых 10 секунд работы процесса);
- инициализируются необходимые компоненты и структуры данных процесса, например, диспетчер кучи;
- загружаются динамически подключаемые библиотеки (DLL – Dynamic Link Library);
- начинается выполнение стартовой функции потока.
Резюме
В этой лекции введены понятия «процесса» и «потока». Рассмотрены структуры данных, представляющие в операционной системе процесс (EPROCESS) и поток (ETHREAD). Описан ход создания процесса с использованием структур данных и функций Windows Research Kernel.
Следующая лекция посвящена алгоритмам планирования потоков и реализации этих алгоритмов в Windows.
Контрольные вопросы
- Приведите определение понятий «программа», «процесс», «поток», «стек».
- Опишите основные поля структуры EPROCESS.
- Какой структурой является поле Pcb структуры EPROCESS? Опишите поля этой структуры.
- Опишите основные поля структуры ETHREAD.
- Перечислите этапы создания процесса.
- Опишите этапы создания объекта «процесс».
- Опишите этапы создания основного потока.