Как выполняется программа в Windows

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

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

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

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

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

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

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

Дополнительные сведения см. в следующих разделах:

  • Многозадачность
  • Планирование
  • Несколько потоков
  • Дочерние процессы
  • Пулы потоков
  • Объекты заданий
  • Планирование в пользовательском режиме
  • Волокон

Источник: learn.microsoft.com

Процессы Windows

Понятие «процесса» существовало в операционных системах Windows задолго до появления платформы .NET. Попросту говоря, под процессом понимается выполняющаяся программа. Однако формально — это концепция уровня операционной системы, которая используется для описания набора ресурсов (таких как внешние библиотеки кода и главный поток) и необходимой памяти, используемой выполняющимся приложением. Для каждого загружаемого в память файла *.ехе в операционной системе создается отдельный изолированный процесс, который используется на протяжении всего времени его существования. Благодаря такой изоляции приложений, исполняющая среда получается гораздо более надежной и стабильной, поскольку выход из строя одного процесса никак не сказывается на работе других процессов.

Более того, доступ напрямую к данным в одном процессе из другого процесса невозможен, если только не применяется API-интерфейс распределенных вычислений, такой как Windows Communication Foundation. Из-за всех этих моментов процесс может считаться фиксированной и безопасной границей выполняющегося приложения.

Каждый процесс Windows получает уникальный идентификатор процесса (Process ID — PID) и может независимо загружаться и выгружаться операционной системой (в том числе программно). Как уже наверняка известно, в окне WindowsTask Manager (Диспетчер задач) имеется вкладка Processes (Процессы), на которой можно просматривать различные статические данные о выполняющихся на данной машине процессах, в том числе их PID-идентификаторы и имена образов. Чтобы открыть окно диспетчера задач, нажмите комбинацию клавиш :

Читайте также:
Программа для удаления обновления с Виндовс 7

Диспетчер задач Windows

Роль потоков

В каждом процессе Windows содержится первоначальный «поток», который является входной точкой для приложения. Потоком называется используемый внутри процесса путь выполнения. Формально поток, который создается первым во входной точке процесса, называется главным потоком (primary thread). В любой исполняемой программе .NET (консольном приложении, приложении Windows Forms, приложении WPF и т.д.) входная точка обозначается как метод Main(). При вызове этого метода главный поток создается автоматически.

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

Из-за такого потенциального недостатка однопоточных приложений, API-интерфейс Windows (а также платформа .NET) предоставляет возможность для главного потока порождать дополнительные вторичные потоки (также называемые рабочими потоками). Это делается с применением набора функций из API-интерфейса Windows, таких как CreateThread() . Каждый поток (первичный или вторичный) в процессе становится уникальным путем выполнения и может параллельно получать доступ ко всем разделяемым элементам данных внутри соответствующего процесса.

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

Однако такого может и не происходить: в случае использования слишком большого количества потоков в одном процессе его производительность может даже ухудшаться из-за возникновения у ЦП необходимости переключаться между активными потоками в процессе (что отнимает определенное время).

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

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

Источник: professorweb.ru

Выполнение программы

Компьютерная программа — это последовательность инструкций, которые компьютер (вычислительная машина) будет исполнять. Иногда под программой понимают исходный код этой программы. Когда пользователь компьютера «запускает программу», он создаёт т.н. процесс, который соответствует этой программе. Процесс — выполнение инструкций программы.

Кроме того, каждый процесс может иметь один или несколько потоков выполнения (англ. thread). Этот курс не подразумевает использования нескольких потоков, поэтому понятие процесс и поток будут взаимозаменяемыми.

Логично, что процесс должен начинать выполнение с какой-либо команды. Место программы, откуда начинается выполнение программы, называется точкой входа в программу. В C++ точка входа в программу может быть только одна и записывается в следующем виде:

Читайте также:
Как снимать видео без программ на Виндовс 7

int main() < return 0; >;

Фигурные скобки, <>, в С++ используются для группировки. В данном случае они указывают на начало и конец функции main, которая обязательно должна присутствовать
в программе, и с которой начинается работа программы. Точка с запятой (;) ставится после каждого оператора языка, например, после оператора «return», возвращающего значение 0 и, таким образом, завершающий программу. Целое значение, с которым завершается программа, называется кодом ошибки. Если код ошибки — 0, то такая программа завершилась успешно.

Разберём следующий код:

int main() < int addOne = 6; int addTwo = 8; return 0; int sum = addOne + addTwo; >;

Внутри блока объявляются 3 переменные: addOne, addTwo и sum. Так как объявление переменной sum стоит после оператора return, то программа завершится ещё до того, как сможет узнать про существование переменной sum. Таким образом, sum не будет ни создана, ни инициализирована.

Операторы ветвления (выбора)

Часто необходимо выполнить (или не выполнить) последовательность команд в зависимости от осуществления какого-либо условия. К примеру, модуль любого числа равен самому числу, если оно не меньше 0, и самому числу, взятому с обратным знаком, в противном случае:

Перепишем это условие на языке C++

int main() < int value = 6;// Исходное значение X int result;// Сюда сохраним результат if (value >= 0) < // Если X ≥ 0, result = value; // тогда сохраняем само число >else < // Иначе result = -value; // его же, взятым с обратным знаком >; return 0; >;

Ветвь else является необязательной. В этом случае оператор if ограничивает блок кода, который может быть выполнен только при наступлении определённого условия. Операторы if могут быть вложенными. Это позволяет использовать 2 и более ограничений. Листинг 4 реализует вычисление результата для следующей функции:

int main() < int value = 6; int result = 0; if (value >= 1) < result = value — 1; >else if (value < -1) < result = -value — 1; >else < result = 0; >; return 0; >;

В этом примере во внешнем блоке if (value >= 1) в части else располагается внутренний блок if (value . Внешний блок выбирает все значения не меньшие 1, тогда как внутренний делит оставшееся множество (т.е. все значения меньшие 1) на два: на множество значений, меньшие –1 и множество не меньшее –1 и меньшее 1.

На заметку

В языке программирования Python комбинация else if была заменена на сокращённый вариант elif. Также в Python отказались от оператора выбора switch, т.к. с помощью оператора if-elif-else легко реализуется его функциональность.

Кроме оператора if существует оператор выбора switch:

int main() < char op = ‘-’; float result = 0.0; float a = 4.0, b = 3.0; switch (op) < case ‘+’: result = a + b; break; case ‘-’: result = a — b; case ‘*’: result = a * b; break; case ‘/’: result = a / b; break; default: /* Вывести ошибку */; >; return 0; >;

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

В фигурных скобках перечисляются значения, с которыми будет происходит сравнение переменной. Выполнение начнётся с той ветви, в которой произошло совпадение (в примере это ‘–’). Оператор break устанавливается для того, чтобы поток программы закончил выполнение ветвей и продолжил работу за закрывающейся фигурной скобкой. Поэтому в приведённом примере забытый оператор break приведёт к ошибке, т.к. после того, как будет вычислена разность поток программы перейдёт на следующую ветвь и выполнит умножение.

Необязательная ветвь default выполнится в том случае, если значение переменной не совпала не с одним из перечисленных в case-ветвях.

На заметку

Часто программисты используют перечисления для использования с оператором switch. Это достаточно правильное и умное использование оператора switch. Более того, достаточно умные компиляторы способны во время компиляции проверить, все ли значения перечисления были учтены, и сообщить об этом программисту.

Читайте также:
Как удалить программу с реестра Windows 8

Циклы

Циклы позволяют выполнять один и тот же блок кода, пока выполняется определённое условие. В языке C++ существует 3 цикла: while, for и do-while. Первые два являются циклами с предусловием, последний — с постусловием. Когда некоторое логическое условие цикла истинно, то начинает выполняться тело цикла. Программисту необходимо следить, чтобы цикл мог когда-нибудь закончиться, т.е. рано или поздно
логическое условие должно стать ложным, иначе программа «зациклится».

Цикл while трактуется так: «пока логическое условие верно, выполнять блок кода». В коде синтаксически записывается следующим образом:

int main() < string text = “So beautiful text!”; int pos = 0; while (text[pos] != ‘t’) < pos++; >; return 0; >;

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

Синтаксис оператора цикла for следующий:

int main() < int arr1[] = ; int arr2[3]; for (int i = 0; i < 3; i++) < arr2[i] = arr1[i]; >; return 0; >;

Оператор цикла for в данном случае имеет следующее значение: «присвоить переменной i значение 0; пока i меньше 3-х копировать i-й элемент массива и увеличить i на 1». Все три части оператора (присвоение переменной значения, проверка логического условия и изменение переменной) могут быть опущены по желанию программиста, т.е. заменены на пустой оператор.

Запись for (int i = 0; i и

int i = 0; while (i < 3) < i++; >;

Условие цикла do-while проверяется после того, как блок кода будет выполнен, другими словами, определённый кусок кода будет выполнен хотя бы один раз, тогда как в циклах с предусловием он может не выполниться вовсе. Синтаксически цикл записывается так:

do < // выполнить хотя бы один раз >while (true);

Отметим, что при приведённой записи код будет выполняться бесконечно, так как логическое условие состоит только из значения true. Чтобы выйти из цикла можно внутри блока воспользоваться уже знакомым оператором break, позволяющий прервать выполнение цикла с любого места. Кроме оператора break существует оператор continue, который завершает текущую итерацию и переходит к выполнению следующей итерации
цикла.

Область видимости переменной

Объявление переменной вводит имя в область видимости (scope); это значит, что имя можно использовать лишь в ограниченной части программы. Для имени, объявленного в конкретном блоке кода (его называют локальным), область видимости простирается от точки объявления до конца содержащего это объявление блока.

Переменная называется глобальной, если она объявлена вне функции main и других блоков кода. Чтобы отличить глобальную переменную от локальной можно использовать два двоеточия перед её именем. Лучше вовсе избегать использования глобальных переменных во избежания перекрытия имён и случайного изменения в коде значения глобальной переменной, вместо локальной.

Следующий код полностью поясняет все возможные ситуации:

int global = 5; // создание глобальной переменной int main() < int global = 4; // создание локальной переменной global; // =4, вызов локальной переменной ::global; // =5, вызов глобальной переменной int outer = 0; // создание локальной переменной < // начало внутреннего блока outer; // =0, вызов лок. переменной внешнего блока float outer = 3.0; // перекрытие имени внеш. перемен. outer; // =3.0 вызов лок. переменной типа float int inner = 2; // создание лок. внутр. переменной inner; // =2, вызов лок. переменной >// inner; // ошибка! Такой переменной в этом блоке нет. // float outer; // ошибка! Такая переменная уже существует return 0; >;

Источник: markoutte.me

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru