КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Программное порождение процессов
Процесс – не программа, но без программы, которая определяет действия процесса, он функционировать не может. Поэтому при создании процесса, в частности путем заявки другого процесса на создание процесса, как минимум необходимо дать ОС информацию, какой программой воспользоваться при создании и запуске процесса. Заметим, что в дальнейшем процесс может сменить используемую программу соответствующим системным вызовом. При вызове программы из командной строки иногда используются текстовые аргументы, задаваемые в этой строке после имени программы. Так, например, из командной строки часто нередко архиваторы. В ряде случаев такая возможность незаменима. Для ее использования в качестве аргументов вызова функции создания процесса может быть использована строка аргументов. Оригинальное и очень изящное решение для создания процессов принято в Unix. Для создания процесса здесь служит системная функция fork(), которая даже не имеет аргументов! Эта функция по существу раздваивает процесс (fork - вилка), в результате в ОС оказываются два почти идентичных процесса. Единственно различие между ними для ОС - их различные идентификаторы, под которыми они зарегистрированы в системе. Различие этих процессов для выполняемой программы в том, что процесс-родитель получает как результат функции fork() значение идентификатора нового - дочернего процесса, а дочерний процесс в качестве числа, возвращаемого функцией, получает нуль. Поэтому в программе, общей хотя бы на участке порождения дочернего процесса, анализом кода возврата функции можно решить, какой процесс в анализируемый момент обслуживает программа - родительский или дочерний. Дочерний процесс, как правило, распознав ситуацию и обнаружив себя, использует вызов запуска другой исполняемой программы. Имеется несколько вариантов системных функций такого запуска. Особенностью их в Unix является то, что они не создают новый процесс, но просто позволяют процессу сменить управляющую им программу. В листингах 7.2.1а и 7.2.1b приведены программы примера создания дочернего процесса, который работает одновременно с родительским и параллельно с ним выводит результаты на экран. Программы этого примера решают уже использовавшуюся демонстрационную задачу: выдают на экран сообщения о шаге цикла вывода для родительского и дочернего процессов.
#include <stdio.h> int main() {int rc, k; printf("Parent Proccess\n"); rc=fork(); if (!rc) {execl("child1.exe",0); } printf("For Child Process:\n"); printf("PID=%d \n", rc); for (k=0; k<10; k++) {printf("I am Parent (My K=%d)\n", k); usleep(2000000); } printf("Parent ended\n"); return 0; } Листинг 7.2.1а. Программа proces.c для Unix
#include <stdio.h> int main() {int k; printf("Demonstration processes, Child Proccess\n"); for (k=0; k<30; k++) {printf(" ------- I am Child... (child=%d)\n", k); sleep(1); } printf("Child ended\n"); return 0; } Листинг 7.2.1b. Программа child1.c для Unix
Программа proces.c для Unix после сообщения о начале работы процесса-родителя вызывает функцию разветвления fork (), возвращающую значение в переменную rc. Далее анализируется эта переменная rc, при нулевом ее значении вызывается функция замены программы execl(), которая использована в виде execl("child1.exe",0). Эта функция имеет переменное число аргументов, и последний из них должен иметь значение 0. В более общей форме эта функция позволяет задать аргументы для командной строки программы процесса. После выполнения вызова execl("child1.exe",0) дочерний процесс управляется программой child1.exe, а исходная программа не имеет к нему уже никакого отношения. Родительский же процесс использует свою программу proces.exe дальше участка анализа: выдает на экран сообщение об идентификаторе дочернего процесса For Child Process PID= номер и переходит к циклу выдачи на экран сообщений о своем очередном шаге. После каждого из этих циклов производится приостановка выполнения функцией usleep(). Последняя функция требует задания времени приостановки в микросекундах (а функции приостановки с параметром в миллисекундах в Unix нет!). Для ОС семейства Windows характерны наиболее сложные функции из рассматриваемых функций создания процессов. Порождение нового процесса достигается в этих ОС функцией CreateProcess с десятью параметрами, имеющей прототип: BOOL CreateProcess(LPCTSTR pNameModule, LPCTSTR pCommandLine, SECURITY_ATTRIBUTES *pProcessAttr, SECURITY_ATTRIBUTES *pThreadAttr, BOOL InheritFlag, DWORD CreateFlag, LPVOID penv, LPCTSTR pCurrDir, STARTUPINFO *pstartinfo, PROCESS_INFORMATION *pProcInfo). Здесь тип LPCTSTR задает дальний указатель (адрес в модели FLAT) для текстовой строки, а DWORD аналогичен типу unsigned LONG. Заметим, что параметры pProcessAttr и pThreadAttr относятся к средствам расширенной защиты в Windows NT и в большинстве приложений не используются, что указывается заданием соответствующего аргумента равным NULL. Параметр pNameModule является указателем на строку текста – на имя файла запускаемой программы (файла с расширением exe), а параметр pCommandLine – указателем на текст командной строки запуска программы. С формальной стороны эти два аргумента частично дублируют дрѓг друга, практически їаще всего используеђся лишь один из них. Если паѐаметр pNameModule задан равным NULL, а используется параметр pCommandLine (т.е. он задан не равным NULL), то из текста параметра pCommandLine извлекается первое слово – последовательность символов до первого пробельного символа. Это слово используется как имя файла запускаемой программы, причем при отсутствии в нем расширения к нему автоматически приписывается расширение EXE. Это имя вначале используется для поиска исполняемого файла в том же каталоге, где находится EXE-файл процесса, вызывающего функцию CreateProcess(). При не обнаружении требуемого файла, его поиск продолжается в текущем каталоге вызывающего процесса, потом (при еще одной неудаче) – в системном каталоге Windows, затем в основном каталоге Windows и, наконец, в последовательности каталогов, перечисленных в переменной окружения PATH. Параметр pNameModule позволяет задавать полное имя запускаемой программы или ее имя в текущем каталоге, причем обязательно с явно присутствующим в тексте расширением EXE. Если при этом дополнительно присутствует и ненулевой параметр pNameModule, то текст последнего используется как последовательность аргументов для командной строки. При нулевом pNameModule параметр CommandLine, задаваемый указателем pCommandLine, кроме имени программы задает после него и аргументы командной строки. Таким образом, параметр pCommandLine дает больше возможностей, но использование одного параметра pNameModule – проще. Параметр InheritFlag используется для задания наследования объектов дочерним процессом и в нашем изложении использоваться не будет (полагается равным FALSE, т.е. равным 0). Параметр pCurrDir относится к дополнительным возможностям функции создания и задает новый текущий каталог для запускаемого процесса. В ближайших примерах он всегда будет задаваться нулевым указателем, т.е. его возможности не будут использоваться. Параметр penv задает указатель на блок памяти, хранящий строки окружения, в простейших и наиболее частых ситуациях он равен NULL. Заметим, что кроме одной текстовой строки аргументов в ОС, начиная с Unix – родоначальницы всех современных ОС, представляется возможность применения так называемого окружения (environment) процесса. Окружение процесса (называемое также средой процесса) – это специальная текстовая область, служащая для передачи данных создаваемому процессу. В Unix она предназначена в первую очередь для передачи настроек программы от операционной системы или командной оболочки. В иных ОС окружение используется достаточно редко, но сама такая возможность оставлена. Чтобы не использовать передаваемую информацию для окружения, следует просто задать в качестве аргумента env нулевой указатель. Параметр CreateFlag задает флаги создания и, кроме достаточно тонких нюансов, определяет класс приоритета создаваемого процесса. Обычное значение этого флага задается константой NORMAL_PRIORITY_CLASS. Два последних параметра задают адреса двух специальных структур данных при создании процесса. Структура STARTINFO соответствует структуре описания сессии (сеанса) в OS/2 и содержит 18 полей, определяющих в первую очередь характеристики окна для создаваемого процесса. В простейших случаях все поля могут быть заполнены нулями, кроме поля с именем cb, в котором должна быть записана длина экземпляра этой структуры в байтах. Структура PROCESS_INFORMATION, задаваемая адресом в последнем параметре, содержит четыре поля для возврата учетной информации из ОС после создания нового процесса. Описывается она в заголовочном файле как typedef struct {HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION; Наиболее значимыми в ней является поле hProcess, возвращающее хэндл созданного процесса, и поле dwProcessId, возвращающее идентификатор (уникальный номер) созданного процесса. Возвращаемое функцией CreateProcess значение типа BOOL позволяет вызывающей программе решить, удачно ли прошел запуск – удалось ли создать процесс. При успешном создании возвращается значение TRUE, иначе FALSE, численно равное нулю. Следующий пример, представляемый программами в листингах 7.2.2а и 7.2.2b, демонстрирует простейшее использование функции создания процесса в Windows. Получаемые из этих исходных текстов программы должны обязательно строиться как консольные приложения, в частности, в интегрированной системе разработки Borland/Inprise языка C выбором опции Console. #include <windows.h> #include <stdio.h> int main() {int k; DWORD rc; STARTUPINFO si; PROCESS_INFORMATION pi; printf("Demonstration processes, Main Proccess\n"); memset(&si, 0, sizeof(STARTUPINFO)); si.cb=sizeof(si); rc=CreateProcess(NULL, "child1.exe", NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); if (!rc) {printf("Error create Process, codeError = %ld\n", GetLastError()); getchar(); return 0; } printf("For Child Process:\n"); printf("hProcess=%d hThread=%d ProcessId=%ld ThreadId=%ld\n", pi.hProcess, pi.hThread, pi.dwProcessId, pi.dwThreadId); for (k=0; k<10; k++) {printf("I am Parent... (my K=%d)\n", k); Sleep(2000); } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return 0; } Листинг 7.2.2а. Программа Parent.c для Windows #include <stdio.h> #include <windows.h> int main() {int k; printf("Demonstration processes, Child Proccess\n"); for (k=0; k<30; k++) {printf("I am Child... (k=%d)\n\r", k); Sleep(1000); } return 0; } Листинг 7.2.2b. Программа Child1.c для Windows
В программе описаны переменные si, pi типов STARTUPINFO и PROCESS_INFORMATION, причем в поле cb структуры si записана длина переменной si (делать это надо обязательно перед использованием такого аргумента в вызове функции CreateProcess), а остальные поля этой структуры обнулены обращением к функции memset(&si, 0, sizeof(STARTUPINFO)) Последняя записывает в память, начало которой задано первым аргументом, столько значений, заданных вторым аргументом, сколько указано числом в третьем аргументе. Значение параметра CreationFlags задает нормальный класс приоритета. Вызов функции создания процесса в качестве ненулевых содержит только параметры pCommandLine, CreateFlag, pstartinfo, pProcInfo, в качестве которых использованы "child1.exe", NORMAL_PRIORITY_CLASS, &si, &pi. После вызова функции ее код возврата в переменной rc используется для анализа ситуации. При rc, равной нулю, выдается сообщение об ошибке запуска, код которой извлекается системной функцией GetLastError(), и процесс завершается оператором return 0. При отсутствии ошибки на экран выдается информация о запущенном процессе путем вывода значений полей hProcess, hThread, dwProcessId, dwThreadId возвращаемой переменной pi. Далее 10 раз с приостановкой на 2 с. выдается сообщение о шаге работы процесса-родителя. Приостановка выполняется функцией Sleep(), аргумент которой задается в миллисекундах. В конце программы полученные при создании процесса хэндлы закрываются вызовом функции CloseHandle(). Для понимания следует обратить внимание, что в данном примере закрываются не только хэндлы, но и связанные с ними управляющие блоки (структуры описания процесса и нити). (Это имеет место, когда на управляющие блоки указывает только один хэндл, в общем случае дублирования хэндлов ситуация может быть иной.) Функция execl(), использованная в примере порождения дочернего процесса для Unix, является на самом деле лишь одним из представителей довольно широкого семейства функций с общим префиксом exec. В это семейство входят функции execl(), execlp(), execle(), execv(), execvp(), execve(). Несмотря на обилие этих функций, их действия идентичны, а отличаются они только наборами аргументов. Все перечисленные функции предназначены для замены текущей выполняемой программы на новую исполняемую программу, имя которой задается первым аргументом вызова всех перечисленных функций. Наличие и предназначение следующих в списке аргументов условно обозначается в именах рассматриваемых функций с помощью одной из дополнительных букв. Символ l в составе имени функции задает использование переменного числа аргументов, символ v указывает, что список аргументов командной строки задается массивом указателей на текстовые строки. (Поэтому символы l и v в имени рассматриваемых функций являются альтернативными и исключают друг друга.) При наличии символа p в составе имени єункции, последняя0ищет указанный пѐи вћзове0исполнџемћй0файл во вёех каталогах,0заданнћх переменной ёѐеды PQTH, все дрѓгие функции использѓюђ только явное указание имени функции. (Функция с символом p в имени по списку аргументов не отличается от соответствующей ей функции без этого символа.) При наличии в имени функции символа e в качестве дополнительной информации в списке аргументов присутствует аргумент – массив переменных окружения. В частности, прототипом функции execve() является int execve(char *filename, char *argv[ ], char *env[ ]), (которая и служит наиболее общей исходной системной функцией), а прототип функции execlp() описывается как int execlp(char *filename, char *arg1, char *arg2,..., char *argn, NULL), где последний аргумент нулевым указателем задает конец списка аргументов. (Заметим, что использование перечисленных функций с параметром argv[ ] позволяет программисту "обмануть" вызываемую программу, задав в качестве нулевого элемента списка аргументов текстовое наименование, отличное от имени запускаемой программы.) При неудачном запуске все рассматриваемые функции возвращают значение -1. (При их успешном запуске некому воспользоваться возвращаемым значением, так как после этого запуска будет выполняться совсем другая программа.) В операционных системах Windows аналогичный по назначению набор функций имеет названия, начинающиеся с префикса spawn. Набор этот состоит из функций spawnl(), spawnle(), spawnlp(), spawnlpe(), spawnv(), spawnve(), spawnvp(), spawnvpe(), где первая и последняя функция из набора задается прототипами int spawnl(int mode, char *path, char *arg0, arg1,..., argn, NULL), int spawnvpe(int mode, char *path, char *argv[ ], char *envp[ ]). Остальные функции имеют аналогичное строение и используют вспомогательные символы l, e, p и v по тому же соглашению, что и функции Unix. Первый аргумент mode всех рассматриваемых функций задает действия родительского процесса после создания дочернего. Если этот параметр задается символической константой P_WAIT, то родительский процесс приостанавливается до тех пор, пока не завершится запущенный дочерний процесс. При задании первого аргумента символической константой P_NOWAIT родительский процесс продолжается одновременно с дочерним, обеспечивая тем самым параллельное выполнение обоих процессов. Заметим, что функции набора spawn не входят в стандарт языка C, а специфичны для указанных операционных систем.
Дата добавления: 2014-01-05; Просмотров: 887; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |