Главная страница
Навигация по странице:

  • ОСНОВНЫЕ СВЕДЕНИЯ Многозадачность в ОС Windows

  • Процесс ничего не исполняет, он просто служит контейнером потоков. Все потоки процесса размещены в едином виртуальном адресном пространстве.

  • CreateProcess ()

  • ShellExecute (). 2.2. Функция WinExec () UINT WinExec(LPCSTR lpCmdLine, UINT uCmdShow); Примечание

  • 2.3. Функция ShellExecute ()

  • В эти параметры можно занести NULL

  • Поэтому базовый вариант обращения к функции таков

  • Возвращаемое значение Пояснение

  • Объект "процесс" переходит в такое состояние при его завершении

  • WinExec

  • ShellExecute (). Поясните различия между дескриптором и идентификатором процесса. Каковы различия в назначении и использовании функций ExitProcess()

  • TerminateProcess( ) ЛИТЕРАТУРА

  • Лабораторная работа 1. Основные сведения


    Скачать 176 Kb.
    НазваниеОсновные сведения
    Дата28.04.2021
    Размер176 Kb.
    Формат файлаdoc
    Имя файлаЛабораторная работа 1.doc
    ТипЛабораторная работа
    #199864

    УПРАВЛЕНИЕ ПРОЦЕССАМИ И ПОТОКАМИ В ОС WINDOWS
    Лабораторная работа № 1
    1. ЦЕЛЬ РАБОТЫ
    Изучение организации мультипрограммирования в ОС Windows, приобретение практических навыков использования средств API Windows для создания процессов и потоков.


    1. ОСНОВНЫЕ СВЕДЕНИЯ




      1. Многозадачность в ОС Windows


    Мультипрограммирование или мультизадачность – это способ организации вычислительного процесса, при котором на одном процессоре попеременно выполняются сразу несколько программ. Эти программы совместно используют не только процессор, но и другие ресурсы компьютера. Мультипрограммирование призвано повысить эффективность использования вычислительной системы. Наиболее часто используемыми критериями эффективности являются:

    • Пропускная способность – количество задач, выполняемых системой в единицу времени;

    • Удобство работы пользователей – возможность интерактивной работы одновременно с несколькими приложениями на одном компьютере;

    В зависимости от выбранного критерия эффективности ОС делятся на системы пакетной обработки, системы разделения времени и системы реального времени.

    Одной из основных подсистем мультипрограммной ОС является подсистема управления процессами и потоками, которая занимается их созданием и уничтожением, поддерживает между ними взаимодействие, а также распределяет процессорное время между несколькими процессами и потоками. Эта подсистема отвечает также за обеспечение процессов необходимыми ресурсами и за синхронизацию потоков и процессов.

    Процесс в системах разделения времени может находиться в одном из следующих состояний:

    • Выполнения – все затребованные процессом ресурсы выделены. В однопроцессорной вычислительной системе в каждый момент времени может находиться только один процесс;

    • Готовности к выполнению – ресурсы могут быть предоставлены, тогда процесс перейдет в состояние выполнения;

    • Блокирования или ожидания – затребованные ресурсы не могут быть предоставлены, или не завершена операция ввода/вывода.

    Как правило, процесс появляется при запуске программы. За время своего существования процесс может неоднократно совершать переходы из одного состояния в другое. Это обусловлено обращениями к ОС для выделения ресурсов и выполнением системных функций, взаимодействием с другими процессами, появлением сигналов от периферийных устройств.

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

    • Идентификатор процесса;

    • Тип (или класс) процесса, который определяет для супервизора некоторые правила предоставления ресурсов;

    • Приоритет процесса, в соответствии с которым супервизор предоставляет ресурсы. В рамках одного класса процессов в первую очередь обслуживаются более приоритетные процессы.

    • Переменную состояния, которая определяет, в каком состоянии находится процесс.

    • Адрес области памяти, в которой хранится контекст процесса.

    • Информацию о ресурсах, которыми процесс владеет и/или имеет право пользоваться.

    • Адрес процесса на диске в его исходном состоянии и адрес на диске, куда он выгружается из оперативной памяти, если его вытеснит другой процесс.


    Windows является многозадачной операционной системой, когда на одном процессоре с разделением во времени выполняется несколько программ. Для каждой выполняемой программы (процесса) ОС выделяет необходимую область оперативной памяти, а также предоставляет процессорное время и доступ к таким ресурсам, как файлы, устройства ввода-вывода и др.

    Процесс – это совокупность исполняемой программы и выделенных ей системных ресурсов. Часть процессов являются прикладными или пользовательскими, другая часть инициализируется самой ОС и называется системными. Каждый процесс выполняется в собственном адресном пространстве. Процессы могут одновременно нуждаться в одних и тех же ресурсах, поэтому ОС выполняет планирование, управление и защиту ресурсов.

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

    В Windows реализован принцип вытесняющей многозадачности. Каждому потоку процесса система выделяет определенный квант времени, по истечении которого происходит переключение на выполнение другого потока. Величина кванта времени зависит от ОС и типа процессора, для Windows ХР она составляет примерно 20 мс. Реализация данного принципа обеспечивает не только параллельное выполнение программ, но и устойчивость среды к зависаниям, так как ни одно приложение не может получить неограниченный доступ к ресурсам компьютера.

    Каждый экземпляр программы соответствует только одному процессу. Windows рассматривает процесс как потребитель всех ресурсов, кроме одного, самого главного, - процессорного времени. Этот важнейший ресурс ОС распределяет между потоками. Поток (thread) представляет собой последовательность (поток выполнения) команд. В Windows процесс не исполняется, исполняются потоки процесса.
    Процесс ничего не исполняет, он просто служит контейнером потоков.

    Все потоки процесса размещены в едином виртуальном адресном пространстве.

    При запуске процесса система создает его главный поток (primary thread). Программист кроме главного потока может создать ещё несколько потоков, принадлежащих процессу. Это дает возможность процессу получать больше процессорного времени и выделять требующие больших затрат времени операции (например, печать текста) в отдельный поток. При этом главный поток может выполняться параллельно. Так реализуется потоковая многозадачность.

    Смена активного потока при вытесняющей многозадачности происходит, если:

    • поток завершился и покинул систему,

    • произошла ошибка выполнения,

    • исчерпан квант времени, предоставленный потоку,

    • поток перешел в состояние ожидания завершения ввода-вывода.

    Поток, который исчерпал свой квант, переводится в состояние готовности и ожидает, когда ему вновь будет предоставлен новый квант времени, а на выполнение в соответствии с определенным правилом выбирается новый поток из очереди готовых потоков.

    Для запуска внешнего приложения из работающей программы нужно создать новый процесс. В Win32 API для этого предусмотрен стандартный системный вызов CreateProcess(), однако эта функция имеет громоздкий синтаксис из-за множества аргументов. Более простыми, но и более ограниченными по возможностям являются функции WinExec() и ShellExecute().
    2.2. Функция WinExec()
    UINT WinExec(LPCSTR lpCmdLine, UINT uCmdShow);

    Примечание:

    LPCSTR = = const char *

    UINT = = unsigned int

    lpCmdLine – указатель на строку с нулевым символом в конце, содержащую имя выполняемого файла (возможно, с путем и доп. аргументами). Если имя находится в AnsiString str, то нужно приводить тип: str.c_str();

    uCmdShow - определяет вид окна запускаемого приложения. Наиболее употребительные значения параметра приведены в таблице:


    Значение

    Действие

    SW_HIDE

    Окно делается невидимым, а фокус передается другому окну.

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

    SW_MINIMIZE

    Минимизировано

    SW_MAXIMIZE

    Максимизировано

    SW_RESTORE

    Активизирует и отображает окно

    SW_SHOWNORMAL


    Если имя запускаемого приложения указано без пути, то его поиск производится в следующей последовательности:

    - каталог запускающего приложения;

    - текущий каталог;

    - системный каталог Windows (по GetSystemDirectory())

    - каталог Windows (по GetWndowsDirectory());

    - перечень каталогов, указанных в переменной окружения PATH.
    При успешном запуске приложения функция возвращает целое число больше 31, а иначе – код ошибки, в частности:

    Возвращаемое

    значение

    Символическое имя

    Причина неудачи

    0




    Нехватка памяти или ресурсов системы

    11

    ERROR_BAD_FORMAT

    Ошибочный формат файла

    (например, не Win32-приложение)

    2

    ERROR_FILE_NOT_FOUND

    Файл не найден

    3

    ERROR_PATH_NOT_FOUND

    Каталог не существует

    Использование WinExec() имеет недостатки:
    1. Нельзя управлять запущенной задачей и даже обнаружить факт ее завершения.


    Программа запускается и возвращает управление запускающему приложению, которое с этого момента ничего не сможет узнать о ее дальнейшей судьбе.

    1. Считается устаревшей и не рекомендуется к использованию в новых программах.


    Достоинства:

    1. Простота (короткий список аргументов).

    2. Совместима с ранними версиями Windows.



    2.3. Функция ShellExecute()
    Способна отобразить любой документ, для которого зарегистрировано расширение имени файла и ассоциированная с ним программа. Функция может сама отыскать приложение, связанное с типом открываемого файла, и запустить его.
    Примечание. Для использования в Builder необходимо добавить заголовочный файл:

    #include “ShellAPI.h”
    Прототип функции имеет вид:
    Void ShellExecute( HWND hWnd,

    const char *Operation,

    const char *FileName,

    const char *Parameters,

    const char *Directory,

    unsigned int CmdShow )
    hWnd - дескриптор родительского окна (можно указать Handle);

    FileName - имя файла (или папки)

    Параметр Operation

    Действие

    “open”

    Открыть файл FileName

    “print”

    Распечатать файл FileName

    “explore”

    Открыть папку в проводнике

    NULL

    По умолчанию – «open»


    Parameters – для задания дополнительных аргументов командной строки.

    Directory – каталог по умолчанию (при NULL – текущий каталог)

    CmdShow – как для WinExec().
    Примеры использования:

    1. Запуск Word c открытием файла:

    ShellExecute(Handle, NULL, “file.doc”, NULL, NULL, SW_RESTORE);

    1. Запуск калькулятора:

    ShellExecute(Handle, “open”, “Calc”, NULL, NULL, SW_RESTORE);

    1. Открывает в Проводнике указанную папку:

    ShellExecute(Handle, “open”, “c:\\Program Files\\Borland”, NULL, NULL,

    SW_RESTORE);
    Функция не возвращает значения. Если нужно диагностировать ошибку, то вызывается

    GetLastError(). Ее нулевое значение говорит о нормальном завершении, ненулевое дает код ошибки.
    int i = GetLastError();

    if(i >0) ShowMessage(“Код ошибки - ” + IntToStr(i));
    Дополнение.
    Если текст метки Label создать как гиперссылку (например, http://www.nstu.ru ), то можно открыть указанный сайт, если в ее обработчике OnClick записать:
    AnsiString adres = Label1->Caption;

    ShellExecute(Handle, “open”, adres.c_str(), NULL, NULL, SW_SHOW);
    Аналогично можно запустить почтовую программу, если в метке указать свой e-mail:
    AnsiString adres = “mailto:” + Label1->Caption;

    ShellExecute(Handle, “open”, adres.c_str(), NULL, NULL, SW_SHOW);


    2.4. Функция CreateProcess



    Новый процесс создается при вызове приложением функции CreateProcess
    BOOL CreateProcess(

    LPCSTR lpApplicationName,
    LPSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation

    );
    При этомсистема создает объект ядра "процесс" с начальным значением счетчика числа его пользователей, равным 1. Этот объект - не сам процесс, а структура данных, через которую операционная система управляет процессом. Затем система создает для нового процесса виртуальное адресное пространство и загружает в него код и данные как для исполняемого файла, так и для любых DLL (если таковые требуются).

    Далее система формирует объект ядра "поток" (со счетчиком, равным 1) для первичного потока нового процесса. Объект ядра "поток" — это также структура данных, через которую система управляет потоком. Если системе удается создать новый процесс и его первичный поток, Create Process возвращает true.

    Параметры lpApplicationName и lpCommandLine


    Эти параметры определяют имя исполняемого файла нового процесса и командную строку, передаваемую этому процессу.

    Чаще всего указывают lpApplicationName == NULL, поскольку имя запускаемого файла все равно должно быть указано в параметре lpCommandLine, который должен содержать полную командную строку создания нового процесса. Разбирая эту строку, функция полагает, что первый компонент в ней представляет собой имя исполняемого файла, который Вы хотите запустить. Если в имени этого файла не указано расширение, она считает его EXE. Далее функция приступает к поиску заданного файла и делает это в следующем порядке:


    1. Каталог, содержащий ЕХЕ-файл вызывающего процесса.

    2. Текущий каталог вызывающего процесса.

    3. Системный каталог Windows.

    4. Основной каталог Windows.

    5. Каталоги, перечисленные в переменной окружения PATH.


    Конечно, если в имени файла указан полный путь доступа, система сразу обращается туда и не просматривает эти каталоги. Найдя нужный исполняемый файл, она создает новый процесс, проецирует код и данные исполняемого файла на адресное пространство этого процесса и обращается к процедурам стартового кода библиотеки С/С++. Тот в свою очередь анализирует командную строку процесса и передает WinMain адрес первого (за именем исполняемого файла) аргумента как lpCommandLine.

    Параметры lpProcessAttributes, lpThreadAttributes


    Чтобы создать новый процесс, система должна сначала создать объекты ядра "процесс" и "поток" (для первичного потока процесса). Поскольку это объекты ядра, родительский процесс получает возможность связать с ними атрибуты защиты. Эти параметры позволяют определить нужные атрибуты защиты для объектов "процесс" и "поток" соответственно. В эти параметры можно занести NULL, и система закрепит за данными объектами дескрипторы защиты по умолчанию. В качестве альтернативы можно объявить и инициализировать две структуры SECURITY_ATTRIBUTES, чтобы присвоить объектам "процесс" и "поток" свои атрибуты защиты.

    Параметр blnheritHandles



    Обычно устанавливается в false. При true новый процесс наследует каждый наследуемый дескриптор, открытый в вызывающем процессе.

    Параметр dwCreationFlags


    Является комбинацией битовых флагов. Одна группа битовых флагов устанавливает способ создания процесса, другая – флаги приоритета процесса. Флаги комбинируются булевым оператором OR.

    Флаги, влияющие на то, как именно создается новый процесс.


    • Флаг DEBUG_PROCESS даст возможность родительскому процессу проводить отладку дочернего, а также всех процессов, которые последним могут быть порождены Если этот флаг установлен, система уведомляет родительский процесс (он теперь получает статус отладчика) о возникновении определенных событий в любом из дочерних процессов (а они получают статус отлаживаемых).

    • Флаг DEBUG_ONLY_THIS_PROCESS аналогичен флагу DEBUG_PROCESS с тем исключением, что заставляет систему уведомлять родительский процесс о возникновении специфических событий только в одном дочернем процессе — его прямом потомке. Тогда, если дочерний процесс создаст ряд дополнительных, отладчик уже не уведомляется о событиях, "происходящих" в них.

    • Флаг CREATE_SUSPENDED позволяет создать процесс и в то же время приостановить его первичный поток. Это позволяет родительскому процессу изменить приоритет дочернего процесса до того, как он получит шанс на выполнение. Внеся нужные изменения, родительский процесс разрешает выполнение дочернего вызовом функции ResumeThread(дескриптор).

    • Флаг DETACHED_PROCESS блокирует доступ процессу, инициированному консольной программой, к созданному родительским процессом консольному окну и сообщает системе, что вывод следует перенаправить в новое окно.

    • Флаг CREATE_NEW_CONSOLE приводит к созданию нового консольного окна для нового процесса. Имейте в виду, что одновременная установка флагов CREATE_NEW_CONSOLE и DETACHED_PROCESS недопустима.

    • Флаг CREATE_NO_WINDOW запрещает создание консольных окон для данного приложения и тем самым позволяет исполнять его без пользовательского интерфейса.

    • Флаг CREATE_NEW_PROCESS_GROUP служит для модификации списка процессов, уведомляемых о нажатии клавиш Ctrl+C и Ctrl+Break. Если в системе одновременно исполняется несколько CUI-процессов, то при нажатии одной из упомянутых комбинаций клавиш система уведомляет об этом только процессы, включенные в группу. Указав этот флаг при создании нового СUI-процесca, Вы создаете и новую группу

    • Флаг CREATE_DEFAULT_ERROR_MODE сообщает системе, чтo новый процесс не должен наследовать режимы обработки ошибок, установленные в родительском

    • Флаг CREATE_SEPARATE_WOW VDM полезен только при запуске 16-разрядного Windows-приложения. Если он установлен, система создает отдельную виртуальную DOS-машину (Virtual DOS-machine, VDM) и запускает 16-разрядное Windows-приложение именно в ней (По умолчанию все 16 разрядные Windows-приложения выполняются в одной, общей VDM). Выполнение приложения в отдельной VDM дает большое преимущество - "рухнув", приложение уничтожит лишь эту VDM, а программы, выполняемые в других VDM, продолжат нормальную работу. Кроме того, 16-разрядные Windows-приложения, выполняемые в раздельных VDM, имеют и раздельные очереди ввода. Эго значит, что, если одно приложение вдруг "зависнет", приложения в других VDM продолжат прием ввода. Единственный недостаток работы с несколькими VDM в том, что каждая из них требует значительных объемов физической памяти.

    • Флаг CREATE_SHARED_WOW_VDM полезен только при запуске 16-разрядного Windows-приложения . По умолчанию все 16-разрядные Windows-приложения выполняются в одной VDM, если только не указан флаг CREATE_SEPARATEWOW_VDM. Однако стандартное пoвeдeниeWindows 2000 можно изменить, присвоив значение "yes" параметру DefaultSeparateVDM в paздeлe HKEY_LOCAL_MACHINE\System\CurгentControlSet\Contгol\WOW. (Пocле модификации этого параметра систему надо перезагрузить.) Установив значение "yes", но указав флаг CREATE_SHARED_WOW_VDM, Вы вновь заставите выполнять все 16-разрядные Windows-приложения в одной VDM.

    • Флаг CREATE_UNICODE_ENVIRONMENT сообщает системе, что блок переменных окружения дочернего процесса должен содержать Unicode-символы. По умолчанию блок формируется на основе ANSI-символов.

    • Флаг CREATE_FORCEDOS заставляет систему выполнять программу MS-DOS, встроенную в 16-разрядное приложение OS/2.

    • Флаг CREATE_BREAKAWAY_FROM_JOB позволяет процессу, включенному в задание, создать новый процесс, отделенный от этого задания .


    Параметр dwCreationFlagsразрешает задать и класс приоритета процесса. Однако это необязательно и даже, как правило, не рекомендуется - система присваивает новому процессу класс приоритета по умолчанию. Возможные классы приоритета перечислены в следующей таблице.


    Класс приоритета

    Флаговый идентификатор

    Idle (простаивающий)

    IDLE_PRIORITY_CLASS

    Below normal (ниже обычного)

    BELOW_NORMAL_PRIORITY_CLASS

    Normal (обычный)

    NORMAL PRIORITY CLASS

    Above normal (выше обычного)

    ABOVE_NORMAL_PRIORITY_CLASS

    High (высокий)

    HIGH_PRIORITY_CLASS

    Realtime (реального времени)

    REALTIME_PRIORITY_CLASS


    Классы приоритета влияют на распределение процессорного времени между процессами и их потоками.

    Параметр lpEnvironment


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

    Параметр lpCurrentDirectory


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

    Параметр lpStartuplnfo


    Этот параметр указывает на структуру STARTUPINFO:
    typedef struct _STARTUPINFO {
    DWORD cb;
    PSTR lpReserved;
    PSTR lpDesktop;
    PSTR lpTitle;
    DWORD dwX;
    DWORD dwY;

    DWORD dwXSize;
    DWORD dwYSize;
    DWORD dwXCountChars;
    DWORD dwYCountChars;
    DWORD dwFillAttribute;
    DWORD dwFlags;
    WORD wShowWindOw;
    WORD cbReserved2;
    PBYTE lpReserved2;
    HANDLE hStdInput;
    HANDLE hStdOutput;
    HANDLE hStdError;
    } STARTUPINFO, *LPSTARTUPINFO;
    Элементы структуры STARTUPINFO используются Windows-функциями

    при создании нового процесса.
    Поясним один элемент - dwFlags. Он содержит набор флагов, позволяющих управлять созданием дочернего процесса. Большая часть флагов просто сообщает функции CreateProcess, содержат ли прочие элементы структуры STARTUPINFO полезную информацию или некоторые из них можно игнорировать. Список допустимых флагов приведен в следующей таблице.


    Флаг

    Описание

    STARTF_USESIZE

    Заставляет использовать элементы divSize и dwYSize

    STARTF_USESHOWWINDOW

    Заставляет использовать элемент wShowWindow

    STARTF_USEPOSITION

    Заставляет использовать элементы dwX и dwY

    STARTF_USECOTUNTCHARS

    Заставляет использовать элементы dwXCountChars и dwYCountCbars

    STARTF_USEFILLATTRIBUTE

    Заставляет использовать элемент dwFillAttnbute

    STARTF_USESTDHANDLES

    Заставляет использовать элементы hStdlnput, hStdOutput и bStdError




    STARTF_RUN_FULLSCREEN

    Приводит к тому, что консольное приложение на компьютере с процессором типа х86 запускается в полноэкран ном режиме


    Параметр ppiProclnfo


    Параметр ppiProcInfoуказывает на структуру PROCESS_INFORMATION, которую Вы должны предварительно создать; ее элементы инициализируются самой функцией CreateProcess. Структура представляет собой следующее:


    typedef struct _PROCESS_INFORMATION {
    HANDLE hProcess;
    HANDLE hThread;
    DWORD dwProcessId;
    DWORD dwThreadId;


    } PROCESS_INFORMATION;
    Значения элементов структуры:
    hProcess - дескриптор созданного процесса

    hThread - дескриптор первичного потока созданного процесса

    dwProcessId - глобальный идентификатор процесса

    dwThreadId - глобальный идентификатор потока.

    Дескриптор и идентификатор – это не одно и то же. Дескриптор используется для указания объекта во всех функциях, выполняющих операции над объектом. Он имеет смысл только в пределах данного процесса и его нельзя передавать из процесса в процесс.

    Идентификатор процесса предназначен для идентификации процесса другими процессами. Его можно передавать из одного процесса в другой. Для управления процессом из другого процесса нужно обратиться к функции OpenProcess(), которой передается идентификатор управляемого процесса.
    Примечания.
    1. Если известен дескриптор процесса, то его можно завершить принудительно. Например, если основной процесс создал дочерний процесс, заполнив свою структуру PROCESS_INFORMATION pinfo, то он же может его завершить так:

    TerminateProcess(pinfo.hProcess, 0);
    2. Если порожденный процесс имеет только один поток с дескриптором окна hWnd, то его можно завершить, послав окну (не потоку!!) сообщение WM_CLOSE:

    SendMessage(hWnd, WM_CLOSE,0,0);

    Разумеется, нужно знать дескриптор окна. Его можно получить, вызвав функцию:

    hWnd = FindWindow(<имя_класса_окна>,<текст_заголовка_окна>);
    В Builder имя класса окна получается из имени формы с добавлением в начале символа T (например, TForm1). Если имя класса неизвестно, то для окон верхнего уровня вместо имени класса пишется NULL.
    Еще одна возможность узнать имя класса - запустить программу WinSight32 (входит в поставку С++Builder и MSVC. После выполнения команды меню Spy-Find Window на экран выводится список всех зарегистрированных окон, класс окна в “{...}” и последним параметром - заголовок окна.
    Большинство приложений порождает процессы с атрибутами по умолчанию. Но и в этом случае необходимо инициализировать все элементы структуры STARTUPINFO хотя бы нулевыми значениями, а в элемент сb — заносить размер этой структуры. Следует

    также обнулять структуру PROCESS_INFORMATION.
    Разработчики приложений часто забывают о необходимости

    инициализации этих структур.
    Если Вы не обнулите их элементы, в них будет содержаться мусор, оставшийся в стеке вызывающего потока. Функция CreateProcess, получив такую структуру данных, либо создаст новый процесс, либо нет — все зависит от того, что именно окажется в этом мусоре.
    Поэтому базовый вариант обращения к функции таков:
    STARTUPINFO sinfo;

    PROCESS_INFORMATION pinfo;

    memset(&pinfo, 0, sizeof(pinfo));

    memset(&sinfo, 0, sizeof(sinfo));

    sinfo.cb = sizeof(sinfo);
    CreateProcess(NULL,”c:\\prg\\my.exe”, NULL, NULL, false, 0,

    NULL, NULL, &sinfo, &pinfo};

    Функция CreateProcess() возвращается без ожидания окончания инициализации порожденного процесса. Если нужно немедленно начать взаимодействие с порожденным процессом, то нужно дождаться окончания инициализации путем вызова функции:
    WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds).
    где hProcess – дескриптор процесса (брать как элемент структуры PROCESS_INFORMATION), dwMilliseconds - время ожидания.


    Эта функция возвращается, если порожденный процесс инициирован или истекло время ожидания:


    Возвращаемое значение

    Пояснение

    0

    Порожденный процесс инициирован

    WAIT_TIMEOUT

    Истек интервал ожидания

    0xFFFFFFFF

    Ошибка (код ошибки по GetLastError())


    Как уже говорилось, создание нового процесса влечет за собой создание объектов ядра "процесс" и "поток". В момент создания система присваивает счетчику каждого объекта начальное значение - единицу. Далее функция CreateProcess открывает объекты "процесс" и "поток" и заносит их описатели, специфичные для данного процесса, в элементы hProcess и hTbread структуры PROCESS_INFORMATION. Когда CreateProcessоткрывает эти объекты, счетчики каждого из них увеличиваются до 2.

    Это означает, что, перед тем как система сможет высвободить из памяти объект "процесс", процесс должен быть завершен (счетчик уменьшен до 1), а родительский процесс обязан вызвать функцию CloseHandle (и тем самым обнулить счетчик). То же самое относится и к объекту "поток" - поток должен быть завершен, а родительский процесс должен закрыть описатель объекта "поток".
    Не забывайте закрывать описатели дочернего процесса и его первичного потока, иначе, пока Вы не закроете свое приложение, будет происходить утечка ресурсов. Конечно, система высвободит все эти ресурсы после завершения Вашего процесса, но хорошо написанная программа должна сама закрывать описатели дочернего процесса и его первичного потока, как только необходимость в них отпадает.

    Почему-то многие разработчики считают, будто закрытие описателя процесса или потока заставляет систему уничтожить этот процесс или поток. Это абсолютно неправильно. Закрывая описатель, Вы просто сообщаете системе, что статистические данные для этого процесса или потока Вас больше не интересуют, но процесс или поток продолжает исполняться системой до тех пор, пока он сам не завершит себя


    Завершение процесса


    Процесс можно завершить четырьмя способами:


    • входная функция первичного потока возвращает управление (рекомендуемый способ),

    • один из потоков процесса вызывает функцию ExitProcess (нежелательный способ);

    • поток другого процесса вызывает функцию TerminateProcess (тоже нежелательно);

    • Все потоки процесса умирают по своей воле (большая редкость),



    Возврат управления входной функцией первичного потока



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

    • любые С++ - объекты, созданные данным потоком, уничтожаются соответствующими деструкторами;

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

    • система устанавливает код завершения процесса (поддерживаемый объектом ядра "процесс") — его и возвращает Ваша входная функция;

    • счетчик пользователей данного объекта ядра "процесс" уменьшается на 1.



    Функция ExitProcess



    Процесс завершается, когда один из его потоков вызывает ExitProcess:
    void ExitProcess(UINT fuExitCode);

    Эта функция завершает процесс и заносит в параметр fuExitCode код завершения процесса. Возвращаемого значения у ExitProcess нет, так как результат ее действия — завершение процесса. Если за вызовом этой функции в программе присутствует какой-нибудь код, он никогда не исполняется.

    Когда входная функция (WinMain или main) в Вашей программе возвращает управление, оно передастся стартовому коду из библиотеки С/C++, и тот проводит очистку всех ресурсов, выделенных им процессу, а затем обращается к ExitProcess, передавая ей значение, возвращенное входной функцией. Вот почему возврат управления входной функцией первичного потока приводит к завершению всего процесса. Обратите внимание, что при завершении процесса прекращается выполнение и всех других его потоков.

    Функция TerminateProcess



    Вызов функции TerminateProcess тоже завершает процесс:
    bool TerminateProcess( HANDLE hProcess, UINT fuExitCode);
    Главное отличие этой функции от ExitProcess в том, что ее может вызвать любой поток и завершить любой процесс. Параметр bProcess – дескриптор завершаемого процесса, параметр fuExitCode - код завершения процесса.

    Пользуйтесь TerminateProcess лишь в том случае, когда иным способом завершить процесс не удается. Процесс не получает абсолютно никаких уведомлений о том, что он завершается, и приложение не может ни выполнить очистку, ни предотвратить свое неожиданное завершение. При этом теряются все данные, которые процесс не успел переписать из памяти на диск.

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

    По завершении процесса (неважно каким способом) система гарантирует: после него ничего не останется — даже намеков на то, что он когда-то выполнялся.
    Завершенный процесс не оставляет за собой никаких следов.

    Когда все потоки процесса завершаются



    В такой ситуации (а она может возникнуть, если все потоки вызвали ExitThread или их закрыли вызовом TerminateThread)операционная система больше не считает нужным "содержать" адресное пространство данного процесса. Обнаружив, что в процессе не исполняется ни один поток, она немедленно завершает его. При этом код завершения процесса приравнивается коду завершения последнего потока.

    Что происходит при завершении процесса





    1. Выполнение всех потоков в процессе прекращается.

    2. Все User- и GDI-объекты, созданные процессом, уничтожаются, а объекты ядра закрываются (если их не использует другой процесс).

    3. Код завершения процесса меняется со значения STILL_ACTIVE на код, переданный в ExitProcess или TerminateProcess.

    4. Объект ядра "процесс" переходит в свободное, или незанятое (signaled), состояние. Прочие потоки в системе могут приостановить свое выполнение вплоть до завершения данного процесса.

    5. Счетчик объекта ядра "процесс" уменьшается на 1.


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

    порожденных им процессов.
    По завершении процесса его код и выделенные ему ресурсы удаляются из памяти. Однако область памяти, выделенная системой для объекта ядра "процесс", не освобождается, пока счетчик числа его пользователей не достигнет нуля. А это произойдет, когда все прочие процессы, создавшие или открывшие описатели для ныне покойного процесса, уведомят систему (вызовом CloseHandle)о том, что ссылки на этот процесс им больше не нужны.


    Зачем нужны дочерние процессы


    При разработке приложения часто бывает нужно, чтобы какую-то операцию выполнял другой блок кода. Поэтому приходится постоянно вызывать функции или подпрограммы. Но вызов функции приводит к приостановке выполнения основного кода программы до возврата из вызванной функции.

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

    Есть еще один прием - процесс порождает дочерний процесс и возлагает на него выполнение части операций. Если Вы просто создаете новый поток внутри того же процесса, то при его тестировании можно получить некорректный результат — может, ошиблись в алгоритме или запутались в ссылках и случайно перезаписали какие-нибудь важные данные в адресном пространстве своего процесса. Один из способов защитить адресное пространство основного процесса от подобных ошибок как раз и состоит в том, чтобы передать часть работы отдельному процессу. Далее можно или подождать, пока он завершится, или продолжить работу параллельно с ним.

    Часто дочернему процессу необходимы данные, содержащимися в адресном пространстве родительского процесса. Было бы неплохо, чтобы он работал исключительно в своем адресном пространстве, а из родительского просто считывал нужные ему данные, тогда он не сможет что-то испортить в адресном пространстве родительского процесса. В Windows предусмотрено несколько способов обмена данными между процессами.
    Один из самых удобных способов, обеспечивающих совместный доступ

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

    PROCESS_INFORMATION pinfo;
    DWORD dwExitCode;

    // порождаем дочерний процесс
    BOOL success = CreateProcess(..., &pinfo};

    if (success)

    {

    // закрывайте описатель потока, как только

    // необходимость в нем отпадает!
    CloseHandle(pinfo.hThread);


    // приостанавливаем выполнение родительского процесса,
    // пока не завершится дочерний процесс
    WaitForSingleObject(pinfo.hProcess, INFINITE);

    // дочерний процесс завершился; получаем код его завершения
    GetExitCodeProcess(pinfo.hProcess, &dwExitCode);

    // закрывайте описатель процесса, как только

    // необходимость в нем отпадает!
    CloseHandle(pinfo.hProcess);

    }
    В этом фрагменте кода создается новый процесс и, если это прошло успешно, вызывается функция WaitForSingleQbject :
    DWORD WaitForSingleObject(HANDLE hObject, DWORD dwTimeOut);
    Функция задерживает выполнение кода до тех пор, пока объект, определяемый параметром bObject, не перейдет в свободное (незанятое) состояние. Объект "процесс" переходит в такое состояние при его завершении. Поэтому вызов WaitForSingleObject приостанавливает выполнение потока родительского процесса до завершения порожденного им процесса.

    Когда WaitForSingleObject вернет управление, можно узнать код завершения дочернего процесса через функцию GetExitCodeProcess.

    Обращение к CloseHandle в приведенном выше фрагменте кода заставляет систему уменьшить значения счетчиков объектов "поток" и "процесс" до нуля и тем самым освободить память, занимаемую этими объектами.
    Очень часто приложение создает другие процессы как обособленные. Это значит, что после создания и запуска нового процесса родительскому процессу нет нужды с ним взаимодействовать или ждать, пока тот закончит работу.
    Чтобы обрубить все пуповины, связывающие с дочерним процессом, нужно вызовом CloseHandle закрыть свои описатели, связанные с новым процессом и его первичным потоком. Приведенный ниже фрагмент кода демонстрирует, как, создав процесс, сделать его обособленным:
    PROCESS_INFORMATION pinfo;

    BOOL success = CreateProcess( , &pinfo);
    if (success)

    {

    // разрешаем системе уничтожить обьекты ядра "процесс" и "поток"
    // сразу после создания дочернего процесса
    CloseHandle(pinfo.hThread);
    CloseHandle(pinfo.hProcess);

    }


    3. ЗАДАНИЕ И ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ





    • Изучите назначение и синтаксис функций WinExec() и ShellExecute(). Используйте для этого раздел 2 методических указаний к лабораторной работе, Help Windows API и рекомендуемую литературу.




    • Создайте в своем каталоге произвольный текстовый файл, используя Microsoft Word.




    • Напишите программу, запускающую Word с открытием указанного файла по нажатию кнопки.




    • Создайте небольшую Windows-программу (по своему усмотрению, скажем, выводящую в окне слово «Привет»), которая будет являться запускаемым дочерним процессом. Далее составьте программу, окно которой имеет три кнопки, служащие соответственно для:

        • - запуска дочернего процесса, используя функцию CreateProcess();

        • - закрытия дочернего процесса;

        • - завершения программы.




    • Подумайте, что произойдет, если запустить несколько дочерних процессов? Доработайте программу таким образом, чтобы все запущенные процессы можно было корректно закрыть.




    • Проверьте свою подготовленность по контрольным вопросам и защитите работу.


    4. КОНТРОЛЬНЫЕ ВОПРОСЫ


    1. Поясните термины «программа», «процесс», «поток».

    2. Что такое ресурсы компьютера?

    3. Может ли процесс в мультипрограммном режиме выполняться быстрее, чем в монопольном?

    4. Какие особенности имеет режим вытесняющей многозадачности?

    5. Какова роль и особенности организации потоков в Windows?

    6. Какие функции используются для создания процессов в Windows?

    7. Поясните различия в назначении функций WinExec(), ShellExecute() и CreateProcess?

    8. Составьте и выполните программу с использованием функции WinExec().

    9. Составьте и выполните программу с использованием функции ShellExecute().

    10. Поясните различия между дескриптором и идентификатором процесса.

    11. Каковы различия в назначении и использовании функций ExitProcess() и TerminateProcess()?



    1. ЛИТЕРАТУРА



    1. Рихтер Д. Windows для профессионалов: создание эффективных Win32-приложений с учетом специфики 64-разрядной версии Windows/ Пер. с англ. - 4-е изд. СПб: Питер; М.: Издательский отдел "Русская редакция ", 2004. - 749 с.




    1. Румянцев П.В. Азбука программирования в Win32 API. - М.: Радио и связь - Телеком, 1999 .




    1. Румянцев П.В. Работа с файлами в Win 32 API. 2-е изд. - М.: Радио и связь, Горячая

    линия. - Телеком, 2002. - 216 с.


    1. Таненбаум Э. Современные операционные системы. 2-е изд.: Пер. с англ. - СПб.:

    Питер, 2002. - 1040 с.

    (Электронная версия: khpi-iip.mipk.kharkiv.edu/library/extent/os/tan2/11.html )


    написать администратору сайта