Разбиение С++ программ на множество задач
Коль выполнение параллельных процессов возможно на более низком (нейронном) уровне, то на символическом уровне мышление человека с принципиальной точки зрения можно рассматривать как последовательную машину, которая использует временно создаваемые последовательности процессов, выполнение которых длится сотни миллисекунд. Герберт Саймон(Негbеrt. Simon), The Machine As Mind
—
Параллельность в С++-программе достигается путем ее (программы) разложения на несколько процессов или потоков. Несмотря на существование различных вариантов организации логики С++-программы (например, с помощью объектов, функций или обобщенных шаблонов), под параллелизмом все же понимается использование множества процессов и потоков. Прочитав эту главу, вы поймете, что такое процесс и как С++-программы можно разделить на несколько процессов.
Определение процесса
Процесс (process) — это некоторая часть (единица) работы, создаваемая операционной системой. Важно отметить, что процессы и программы — необязательно эквивалентные понятия. Программа может состоять из нескольких процессов. В некоторых ситуациях процесс может быть не связан с конкретной программой. Процессы - это артефакты операционной системы, а программы — это артефакты разработчика. Такие операционные системы, как UNIX/Linux позволяют управлять сотнями или даже тысячами параллельно загружаемых процессов.
Чтобы некоторую часть работы можно было назвать процессом, она должна иметь адресное пространство, назначаемое операционной системой, и идентификатор, или идентификационный номер (id процесса). Процесс должен обладать определенным статусом и иметь свой элемент в таблице процессов. В соответствии со стандартом POSIX он должен содержать один или несколько потоков управления, выполняющихся в рамках его Процесс состоит из множества выполняющихся инструкций, размещенных в адресном пространстве этого процесса. Адресное пространство процесса распределяется между инструкциями, данными, принадлежащими процессу, и стеками, обеспечивающими вызовы функций и хранение локальных переменных.
Два вида процессов
При выполнении процесса операционная система назначает ему некоторый процессор. Процесс выполняет свои инструкции в течение некоторого периода времени. Затем он выгружается, освобождая процессор для другого процесса. Планировщик операционной системы переключается с кода одного процесса на код другого, предоставляя каждому процессу шанс выполнить свои инструкции. Различают пользовательские процессы и системные. Процессы, которые выполняют системный код, называются системными и применяются к системе в целом. Они занимаются выполнением таких служебных задач, как распределение памяти, обмен страницами между внутренним и вспомогательным запоминающими устройствами, контроль устройств и т.п. Они также выполняют некоторые задачи «по поручению» пользовательских процессов, например, делают запросы на ввод-вывод данных, выделяют память и т.д. Пользовательские процессы выполняют собственный код и иногда обращаются к системным функциям. Выполняя собственный код, пользовательский процесс пребывает в пользовательском режиме (user mode). В пользовательском режиме процесс не может выполнять определенные привилегированные машинные команды. При вызове системных функций (например read(), write () или open ()) пользовательский процесс выполняет инструкции операционной системы. При этом пользовательский процесс «удерживает» процессор до тех пор, пока не будет выполнен системный вызов. Для выполнения системного вызова процессор обращается к ядру операционной системы. В это время о пользовательском процессе говорят, что он пребывает в привилегированном режиме, или режиме ядра (kernel mode), и не может быть выгружен никаким другим пользовательским процессом.
Блок управления процессами
Процессы имеют характеристики, используемые для идентификации и определения их поведения. Ядро поддерживает необходимые структуры данных и предоставляет системные функции, которые дают возможность пользователю получить доступ к этой информации. Некоторые данные хранятся в блоках управления процессами (process control block—PCB), или БУП. Данные, хранимые в БУП-блоках, описывают процесс с точки зрения потребностей операционной системы. С помощью этой информации операционная система может управлять каждым процессом. Когда операционная система переключается с одного процесса на другой, она сохраняет текущее состояние выполняющегося процесса и его контекст в области сохранения БУП-блока, чт обы надлежащим образом возобновить выполнение этого процесса в следующий раз, когда ему снова будет выделен центральный процессор (ЦП). БУП-блок считывается и обновляется различными модулями операционной системы. Модули «отвечают» за контроль производительности операционной системы, планирование, распределение ресурсов и доступ к механизму обработки прерываний и/или модифицируют БУП-блок. Блок БУП содержит следующую информацию:
• текущее состояние и приоритет процесса;
• идентификатор процесса, а также идентификаторы родительского и сыновнего процессов;
• указатели на выделенные ресурсы;
• указатели на область памяти процесса;
• указатели на родительский и сыновний процесс;
• процессор, занятый процессом;
• регистры управления и состояния;
• стековые указатели.
Среди данных, содержащихся в БУП-блоке, есть такие, которые «отвечают» за управление процессом, т.е. отражают его текущее состояние и приоритет, указывают на БУП-блоки родительского и сыновнего процессов, а также выделенные ресурсы и память. Кроме того, этот блок включает информацию, связанную с планированием, привилегиями процессов, флагами, сообщениями и сигналами, которыми обмениваются процессы (имеется в виду межпроцессное взаимодействие— mterprocess communication, или IPC). С помощью информации, связанной с управлением процессами, операционная система может координировать параллельно выполняемые процессы. Стековые указатели и содержимое регистров пользователя, управления и состояния содержат информацию, связанную с состоянием процессора. При выполнении процесса соответствующая информация размещается в регистрах ЦП. При переключении операционной системы с одного процесса на другой вся информация из этих регистров сохраняется. Когда процесс снова получает ЦП во «временное пользование», ранее сохраненная информация может быть восстановлена. Есть еще один вид информации, который связан с идентификацией процесса. Имеется в виду идентификатор процесса (id), или PID, и идентификатор родительского процесса (PPID). Эти идентификационные номера (которые представлены положительными целочисленными значениями) уникальны для каждого процесса.
Анатомия процесса
Адресное пространство процесса делится на три логических раздела: текстовый (для кода программы), информационный (для данных программы) и стековый (для стеков программы). Логическая структура процесса показана на рис.3.1. Текстовый раздел (расположенный в нижней части адресного пространства) содержит подлежащие выполнению инструкции, которые называются программным кодом. Раздел данных (расположенный над текстовым разделом) содержит инициализированные глобальные, внешние и статические переменные процесса. Раздел стеков содержит локально создаваемые переменные и параметры, передаваемые функциям. Поскольку процесс может вызывать как системные функции, так и функции, определенные пользователем, в стековом разделе поддерживаются два стека: стек пользователя и стек ядра. При вызове функции создается стековый фрейм функции, который помещается в стек пользователя или стек ядра в зависимости от того , в каком режиме пребывает процесс в данный момент: в пользовательском или привилегированном (режиме ядра). Стековый раздел имеет тенденцию расти в направлении раздела данных. При выходе из функции ее стековый фрейм извлекается из стека. Разделы кода, данных и стеков, а также блок управления процессом образуют часть того, из чего складывается образ процесса (process image).