Слайд 1Архитектура вычислительных систем.
Лекция 7.
Ловецкий К.П.
Москва,
декабрь 2011
Процессы: общие сведения
lovetskiy@gmail.com
Слайд 2Свойства процесса
Под процессом неформально можно понимать «программу, которая выполняется под
управлением операционной системы».
Процесс обладает, по меньшей мере, следующими свойствами:
• сегмент
кода;
• сегмент данных;
• стек;
• счетчик команд;
• права и полномочия;
• ресурсы, выданные в пользование процесса операционной системой (например, открытые файлы);
• идентификатор процесса.
Слайд 3Свойства процесса
Диаграмма состояний процесса
Слайд 4Свойства процесса
Физический процессор в системе может быть и один. В
общем случае, процессов в системе может быть больше, чем процессоров.
Поэтому в конкретный момент времени процесс может как исполняться, так и не исполняться, причем процесс, который в настоящее время не исполняется, может быть как готов к возобновлению исполнения, так и не готов (например, процесс может ожидать результатов операции ввода-вывода). Таким образом, процесс может находиться в одном из трех состояний: выполнение, блокировка (в ожидании события) и готовность. Переходы между состояниями выполнения и готовности осуществляются при вмешательстве планировщика системного времени (например, один процесс может быть снят с выполнения, то есть переведен из состояния выполнения в состояние готовности, а другой при этом, наоборот, поставлен на выполнение).
Слайд 5Свойства процесса
В состояние блокировки процесс может попасть только в случае,
если этот процесс произвел такой системный вызов, после которого немедленное
продолжение выполнения невозможно. Например, процесс мог затребовать чтение данных с диска; в этом случае продолжать выполнение имеет смысл не раньше, чем данные будут прочитаны, что требует времени. Также процесс мог в явном виде потребовать приостановить его выполнение на несколько секунд (вызов sleep()) или до поступления внешнего сигнала (вызов pause()).
Слайд 7Легковесные процессы
В некоторых системах поддерживается понятие легковесного процесса (thread,
lightweight process - поток, нить).
Легковесный процесс представляет собой дополнительную единицу
планировки в рамках одного обычного процесса; иначе говоря, обычный процесс в таких системах можно представить как группу легковесных процессов, работающих одновременно над одними и теми же кодом, данными и ресурсами.
Удачным примером использования легковесных процессов может служить интерактивная программа, работающая со сжатыми данными (например, музыкальный редактор, способный работать с mp3-файлами). С одной стороны, операции упаковки/распаковки данных требуют больших объемов вычислений и могут длиться по несколько минут и даже десятков минут. С другой стороны, интерактивная программа не может себе позволить в течение нескольких минут не реагировать на действия пользователя: например, пользователь может переместить окно программы на экране (что потребует его отрисовки) или даже принять решение об отмене текущей операции. В такой ситуации логично использовать один легковесный процесс для выполнения вычислений (упаковки/распаковки) и другой − для обработки интерфейсных событий, то есть для реакции на действия пользователя.
Слайд 8Легковесные процессы
В некоторых системах поддерживается понятие легковесного процесса
(thread,
lightweight process - поток, нить).
Легковесный процесс представляет собой дополнительную единицу
планировки в рамках одного обычного процесса; иначе говоря, обычный процесс в таких системах можно представить как группу легковесных процессов, работающих одновременно над одними и теми же кодом, данными и ресурсами.
Слайд 9Легковесные процессы
Легковесные процессы, работающие в рамках одного обычного процесса,
используют его сегменты кода и данных, но при этом у
каждого легковесного процесса имеется свой собственный стек (для хранения локальных переменных и адресов возврата из подпрограмм) и счетчик команд. Когда в рамках процесса выполняются легковесные процессы, о состоянии (выполнение, готовность, блокировка) имеет смысл говорить в отношении каждого из легковесных процессов.
Следует учитывать, что программирование с использованием легковесных процессов требует определенного мастерства и крайне высокой аккуратности, как и любое параллельное программирование с использованием разделяемых данных.
Слайд 10Стек
Стек (stack — стопка) — структура данных с методом
доступа к элементам LIFO (Last In — First Out, «последним
пришел — первым вышел»). Чаще всего принцип работы стека сравнивают со стопкой тарелок: чтобы взять вторую сверху, нужно сначала взять верхнюю.
Добавление элемента, называемое также проталкиванием (push), возможно только в вершину стека (добавленный элемент становится первым сверху), выталкивание (pop) — также только из вершины стека, при этом второй сверху элемент становится верхним.
Стеки широко используются в вычислительной технике — так для отслеживания точек возврата из подпрограмм стек вызовов, который является неотъемлемой частью архитектуры большинства современных процессоров. Языки программирования высокого уровня используют стек вызовов для передачи параметров при вызове процедур.
Слайд 11Стек
Стек (stack — стопка) — структура данных с методом
доступа к элементам LIFO (Last In — First Out, «последним
пришел — первым вышел»). Чаще всего принцип работы стека сравнивают со стопкой тарелок: чтобы взять вторую сверху, нужно сначала взять верхнюю.
Слайд 12Процессы в ОС Unix
В операционных системах семейства Unix процессы
имеют, по меньшей мере, следующие свойства:
1. Сегмент кода (возможно, разделяемый).
Собственно программа, выполняющаяся в данном процессе. Изменять данные в этой области памяти процесс не может, что позволяет при запуске нескольких копий
одной и той же программы держать в памяти один экземпляр кода.
2. Сегмент данных, включая стек.
3. Состояние регистров, включая счетчик команд, слово состояния (PSW), указатель стека и все регистры общего назначения. Когда процесс по тем или иным причинам находится вне состояния выполнения (то есть он либо заблокирован, либо готов к выполнению, но не выполняется из-за занятости процессора), содержимое его регистров хранится в специальной структуре данных в ядре.
4. Таблица дескрипторов файлового ввода-вывода, содержащая сведения об открытых файлах и других потоках ввода-вывода, доступных данному процессу.
5. Командная строка. Структура данных, содержащая аргументы командной строки, включая имя, по которому программа была вызвана.
Слайд 13Процессы в ОС Unix
6. Окружение. Структура данных, содержащая имена
и значения пере-
менных окружения в виде текстовых строк.
7. Текущий каталог.
Каждый процесс находится в одном из каталогов файловой системы; этот параметр определяет, в каком каталоге искать файлы, если не задан полный путь.
8. Корневой каталог. В ОС Unix можно ограничить файловую систему, видимую процессу и всем его потомкам, частью дерева каталогов, имеющей общий корень. Например, если установить процессу корневой каталог /foo, то под именем / процесс будет видеть каталог /foo, а под именем /bar − каталог /foo/bar. Каталоги за пределами /foo процессу и всем его потомкам вообще не будут видны ни под какими именами. Это используется для запуска отдельных программ в безопасном варианте − так, чтоб они не могли получить доступ ни к каким файлам кроме тех, которые предназначены специально для них.
9. Параметр umask. При создании новых файлов, каталогов и т.п. значение этого параметра побитово вычитается из значения «права доступа», заданного в системном вызове. Например, если параметр umask равен 0077, все создаваемые файлы будут полностью недоступны для всех пользователей, кроме владельца файла.
Слайд 14Процессы в ОС Unix
10. Счетчики потребленных ресурсов (процессорного времени,
памяти
и т.п.).
11. Информация о владельце процесса. Эта информация включает uid
(идентификатор пользователя), gid (идентификатор группы пользователей), euid и egid (эффективные идентификаторы пользователя и группы). В большинстве случаев эффективные идентификаторы совпадают с обычными; примером случая, когда это не так, являются так называемые suid-программы (то есть программы, выполняемые с правами пользователя, владеющего исполняемым файлом данной программы, а не того пользователя, который программу запустил). К числу таких программ относится passwd (программа смены пароля).
12. Идентификаторы процесса, родительского процесса, сеанса и группы процессов. Параметр pid представляет собой число − уникальный идентификатор процесса в системе. Параметр ppid равен идентификатору родительского процесса (процесса, породившего данный), если этот процесс еще существует; если родительский процесс завершается раньше дочернего, ppid дочернего становится равен 1.
Слайд 15Управление процессами
Порождение процесса
Единственный способ порождения процесса в ОС Unix
− это создание копии существующего процесса. Для этого используется системный
вызов
int fork(void);
В результате вызова создается дочерний процесс, являющийся точной копией родительского, за исключением следующих различий:
1. Дочерний процесс имеет свой идентификатор (pid), естественно, отличающийся от идентификатора родителя;
2. Параметр ppid дочернего процесса равен pid’у родительского процесса;
3. Счетчики потребленных ресурсов дочернего процесса сразу после
fork() равны нулю;
4. Выполнение обоих процессов (родительского и дочернего) продолжается с первой инструкции, следующей сразу за функцией fork() (обычно это присваивание возвращаемого ею значения какой-либо переменной), причем в родительском процессе fork() возвращает pid дочернего процесса, а в дочернем − число 0.
Слайд 16Управление процессами
Отметим, что после вызова fork() оба процесса (родительский и
дочерний) используют один и тот же сегмент кода (это возможно,
т.к. сегмент кода не может быть модифицирован). Что касается остальной памяти процесса, то она, за исключением нескольких специальных случаев, копируется.
Это означает, в частности, что в дочернем процессе присутствуют все переменные, существовавшие в родительском процессе, причем изначально они имеют те же значения, но изменения переменных в родительском процессе никак не отражаются на дочернем, и наоборот.
Копированию подвергаются открытые дескрипторы файлов, установленные обработчики сигналов и т.п.
Слайд 17Замена выполняемой программы
Запустить на выполнение другую программу в ОС
Unix можно путем замены выполняемой программы в рамках одного процесса.
Это действие осуществляется с помощью системного вызова
int execve(const char *path, char* const argv[], char* const envir[]);
Параметр path задает исполняемый файл программы, которую необходимо запустить на выполнение вместо текущей (файл можно задать как полным путем, так и относительно текущего каталога). Параметры argv и envir задают, соответственно, командную строку и окружение для запускаемой программы в виде адресов структур данных.
Для удобства программирования существуют еще несколько функций семейства exec, реализованных в библиотеке через вызов execve().
Начнем с функции
int execv(const char *path, char* const argv[]);
От вызова execve(), как можно заметить, эта функция отличается отсутствием параметра envir. Окружение для запускаемой программы в этом случае берется в точности то же, которое имело место у текущей программы, то есть окружение, попросту говоря, наследуется.
Слайд 18Замена выполняемой программы
Следующая полезная функция имеет точно такой же
прототип, как и предыдущая:
int execvp(const char *path, char* const argv[]);
Отличие
execvp() от execv() состоит в том, что имя, заданное в параметре path, может быть именем программы, исполняемый файл которой находится в одной из директорий, перечисленных в переменной окружения PATH; так, если переменная PATH включает директорию /bin, то вызвать программу ls можно просто по имени, не указывая полный путь.
Наконец, бывают случаи, когда уже на этапе написания исходной программы нам известно точное количество параметров командной строки для запускаемой программы. В этом случае нет необходимости формировать структуру данных, требующуюся для рассмотренных функций. Вместо этого можно использовать одну из двух функций
int execl(const char *path, const char *argv0, ...);
int execlp(const char *path, const char *argv0, ...);
Эти функции получают произвольное число аргументов, первый из которых задает исполняемый файл, остальные − аргументы командной строки.
Слайд 19Замена выполняемой программы
Различие между execl() и execlp() в том,
что первая требует указания явного пути к исполняемому файлу, тогда
как вторая выполняет поиск по переменной PATH, подобно тому, как это делает execvp().
Повторим, что все функции семейства exec заменяют в памяти процесса выполнявшуюся (и вызвавшую exec) программу на другую, указанную в параметрах вызова. Поэтому в случае успеха функции exec управление уже не возвращают (в самом деле, программы, в которую можно было бы вернуть управление, в этом случае уже нет; вместо нее работает новая программа). В случае ошибки возвращается значение -1, но проверять его не обязательно: сам факт возврата управления свидетельствует о происшедшей ошибке.
Отметим, что открытые файловые дескрипторы при выполнении exec остаются открытыми, что позволяет перед запуском на выполнение внешней программы произвести манипуляции с дескрипторами. Это свойство exec используется для перенаправления ввода-вывода.
Слайд 20Завершение процесса
Для завершения процесса используется вызов
void exit(int code);
Параметр code задает
код завершения процесса.
Считается, что значение 0 означает успешное завершение,
значения 1, 2, 3 и т.д. − что произошла та или иная ошибка или неудача.
Обычно в операционной системе используются значения, не превышающие 10, хотя это не обязательно.
Процесс также завершается, если заканчивает исполняться его функция main(). В этом случае в качестве кода завершения берется значение, возвращенное из функции main() (это является причиной того, что в Unix’е функция main() обязательно имеет тип возвращаемого значения int).
Отметим, что процесс также может быть уничтожен сигналом извне; в этом случае кода завершения у него не будет.
Слайд 21Процессы-зомби и их обработка
После завершения процесса в системе остается
информация о том, при каких обстоятельствах завершился процесс (сам ли
он завершился, если да − то с каким кодом завершения, если нет − то каким сигналом он уничтожен) и значения счетчиков потребленных ресурсов. Эту информацию должен затребовать родительский процесс. До тех пор, пока соответствующая информация не будет затребована родительским процессом, завершенный процесс продолжает существовать в системе в виде процесса-«зомби», то есть занимает место в таблице процессов, при этом не имея кода, данных и т.п., а только идентификатор, счетчики ресурсов и статус завершения.
Затребовать информацию (и убрать «зомби»-процесс из системы) позволяют системные вызовы семейства wait(). Простейший из них имеет следующий прототип:
int wait(int *status);
Слайд 22Жизненный цикл процесса
Жизненный цикл процесса - с учетом возможности
откачки необходимых процессу данных из памяти на внешние устройства
Слайд 24Дополнения
FIFO — акроним First In, First Out («первым пришёл —
первым ушёл», англ. ), абстрактное понятие в способах организации и манипулирования
данными относительно времени и приоритетов. Это выражение описывает принцип технической обработки очереди или обслуживания конфликтных требований путём упорядочения процесса по принципу: «первым пришёл — первым обслужен» (ПППО). Тот, кто приходит первым, тот и обслуживается первым, пришедший следующим ждёт, пока обслуживание первого не будет закончено, и т.д.
Этот принцип аналогичен поведению лиц, стоящих в очереди, когда люди получают обслуживание в том порядке, в котором они занимали очередь. То же самое происходит, например, на нерегулируемом перекрёстке, когда водители ожидают своей очереди на продолжение движения (в американских ПДД нет правила «помеха справа», приоритет определяется по принципу FIFO).
Слайд 25Дополнения
ПППО также используется как сокращённое название для алгоритма FIFO планирования
работы операционной системы, по которому процессорное время выделяется каждому процессу
в порядке их поступления на обслуживание. В более широком смысле, абстракция LIFO или Last-In-First-Out («последним пришёл — первым ушёл») является противоположностью абстракции FIFO.
Разница, возможно, станет яснее, если принять во внимание реже используемый синоним FILO, означающий First-In-Last-Out («первым пришёл — последним ушёл»). В сущности, обе абстракции являются конкретными случаями более общего понятия работы со списком. Разница не в списке (данных), а в правиле доступа к содержимому. В первом случае добавление делается к одному концу списка, а снятие с другого, во втором случае добавление и снятие делается на одном конце.
Слайд 26Стек
Стек (англ. stack — стопка) — структура данных, в которой доступ к элементам
организован по принципу LIFO (англ. last in — first out, «последним
пришёл — первым вышел»). Чаще всего принцип работы стека сравнивают со стопкой тарелок: чтобы взять вторую сверху, нужно снять верхнюю.
Добавление элемента, называемое также проталкиванием (push), возможно только в вершину стека (добавленный элемент становится первым сверху). Удаление элемента, называемое также выталкиванием (pop), тоже возможно только из вершины стека, при этом второй сверху элемент становится верхним.
Стеки широко применяются в вычислительной технике. Например, для отслеживания точек возврата из подпрограмм используется стек вызовов, который является неотъемлемой частью архитектуры большинства современных процессоров. Языки программирования высокого уровня также используют стек вызовов для передачи параметров при вызове процедур.
Арифметические сопроцессоры, программируемые микрокалькуляторы и язык Forth используют стековую модель вычислений.