Управление процессами

Презентация

Процесс

Процесс – это адресное пространство и единая нить управления — Э. Танненбаум

Понятие процесса включает в себя:

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

Нити

Нить управления (thread) — это одна логическая цепочка выполнения команд. На самом деле, в одном процессе может быть несколько одновременно запущенных таких цепочек, т.е. нитей. В таком случае имеет место многопоточность (multi-threading).

Дилемма:

  • управления нитями внутри процесса (green threads)‏
  • с помощью средств ОС (native threads)

Волокна (fibers) — аналогичны нитям, но используют кооперативную многозадачность (см. Планирование процессов)

Со-процедуры (co-routines) — особый вид процедур с несколькими точками входа. Реализует кооперативную многозадачность.

Простейший пример использования со-процедур: допустим, у вас имеется отношение производитель-потребитель. Первый добавляет в очередь некие предметы, второй их удаляет. Для повышения производительности предметы добавляются и удаляются по нескольку за раз, пока очередь не заполнится. В таком случае код (на неком абстрактном языке) будет выглядеть так:
var q := new queue

coroutine produce
    loop
        while q is not full
            create some new items
            add the items to q
        yield to consume

coroutine consume
    loop
        while q is not empty
            remove some items from q
            use the items
        yield to produce

После запуска со-процедуры produce очередь будет заполняться, пока не закончится место. После этого управление будет передано (команда yield) со-процедуре consume. Она будет удалять элементы очереди до тех пор, пока та не опустеет, после чего снова будет вызвана со-процедура produce.

Процесс для ОС

Структура для хранения информации о процессе (Process control block) содержит, как прави‏ло:

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

Пример реальной структуры (из Linux): pcb.txt

Жизненный цикл процесса

Процесс создания нового процесса в Linux состоит из двух шагов:
  • клонирование текущего процесса (функция fork())
  • запуск в клоне другой программы (семейство функций exec()execl, execlp, execle, execv, execvp). Впрочем, никто не запрещает клону продолжать выполнение главной программы.
Небольшой пример создания нового процесса (для Linux):
 1 #include <sys/types.h>
 2 #include <unistd.h>
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 
 6 int main() {
 7     /* Клонируем текущий процесс */
 8     pid_t childpid = fork();
 9 
10     /* Обратите внимание на то, что переменная childpid будет существовать и в
11      * оригинальном процессе, и в его клоне, но во втором случае будет равна
12      * нулю. Именно этот факт используется в следующей конструкции */
13     if(childpid == 0) {
14         /* Это клон. С помощью стандартной команды echo выведем приветствие и завершимся */
15 
16         char* command[3] = {"/bin/echo", "Hello, world!", NULL};
17 
18         execvp(command[0], command);
19 
20         /* Если не произошло никаких ошибок, execvp() не завершается и мы
21          * никогда не достигнем этого участка кода. Если же мы всё-таки сюда
22          * добрались, клону следует завершиться с кодом возврата,
23          * сигнализирующем об ошибке */
24         exit(EXIT_FAILURE);
25     } else if(childpid == -1) {
26         /* fork() возвращает -1 в случае ошибки */
27         fprintf(stderr, "Can't fork, exiting...\n");
28         exit(EXIT_FAILURE);
29     } else {
30         /* Это родительский процесс. Просто завершимся с кодом возврата 0 */
31         exit(EXIT_SUCCESS);
32     }
33 
34     return 0;
35 }
Завершение процесса:
  • процесс обязательно возвращает так называемый код возврата (exit code) — целое (int) число. В *nix-подобных ОС код возврата, равный нулю, сигнализирует об успехе, всё остальные говорят об ошибке (разработчик приложения волен произвольно сопоставлять ошибки возвращаемым значениям)
  • используя функции семейства wait() (wait(), waitpid()), один процесс может ожидать завершения другого. Это часто используется в родительских процессах, которые хотят получить информацию о завершении их потомков. Вызовы wait() являются блокирующими — функция не завершится, пока не завершится процесс, который она ждёт.

Если потомок завершается, но родительский процесс не вызывает wait(), потомок становится зомби. Таким образом, процессы-зобми — это завершившиеся процессы, информация о завершении которых никем не запрошена (т.е. никто не вызывал для этого процесса wait()). Впрочем, после завершения родительского процесса все его потомки переходят к процессу с PID 1, т.е. init. Он самостоятельно очищает информацию, оставшуюся после зомби.

Планирование процессов

Вытесняющая многозадачность (англ. «preemptive multitasking») — в системе работает планировщик (англ. «scheduler»), который имеет право в любой момент приостановить выполняющийся процесс и передать управление другому. Такое переключение между процессами носит имя «переключение контекста» (англ. «context switch»). Вытесняющая многозадачность даёт некоторые гарантии того, что каждый процесс получит некое количество процессорного времени.

Кооперативная многозадачность (англ. «cooperative multitasking») — каждый процесс должен самостоятельно передавать управление. За примером кооперативной многозадачности см. определение со-процессов в разделе о нитях.

Литература:

pcb.txt - Process Control Block (4.6 KB) vseloved, 2010-10-02 06:46

lec5.ppt (295 KB) vseloved, 2011-10-31 21:44

2010-10-22-posix.pdf (138.2 KB) vseloved, 2011-10-31 23:31

Also available in: HTML TXT