Создание процессов и исполнение программ



Скачать 336,76 Kb.
страница2/7
Дата12.09.2017
Размер336,76 Kb.
1   2   3   4   5   6   7

Системный вызов fork(2) - Пример


Этот пример иллюстрирует создание подпроцесса. После fork(2) два процесса будут исполнять одну и ту же программу. Они оба распечатают свой идентификатор процесса и идентификатор родителя. Эта программа работает следующим образом:

8-9 Процесс распечатывает идентификатор своего родительского процесса; при работе в терминальной сессии это обычно идентификатор процесса командного процессора.

11 Создается новый процесс. Новый (порожденный) процесс является [почти] точной копией вызывающего процесса (родителя).

13-14 Оба процесса исполняют этот оператор.

Файл: fork0.c

Замечание: Вызов getppid(2), исполняемый вновь порожденным процессом, может возвратить идентификатор процесса 1, то есть процесса init. После fork(2) родительский и порождённый процесс исполняются независимо. Родитель может завершиться раньше, чем порождённый, особенно если он выполняет меньше действий. Если родительский процесс завершается раньше порождённого, то последний "усыновляется" процессом init, то есть система устанавливает процесс с идентификатором 1 в качестве родителя подпроцесса.

СИСТЕМНЫЙ ВЫЗОВ fork(2) - ПРИМЕР
1 #include

2 #include

3 #include

4

5 main()



6 {

7

8 printf("[%ld] parent process id: %ld\n",



9 getpid(), getppid());

10

11 fork();



12

13 printf("\n\t[%ld] parent process id: %ld\n",

14 getpid(), getppid());

15 }
$ fork0

[18607] parent process id: 18606
[18608] parent process id: 18607
[18607] parent process id: 18606

      1. Системный вызов fork(2) - Пример

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

13-16 Значение, возвращенное fork(2), присваивается переменной pid. Положительное значение pid означает родительский процесс.

17-20 Нулевое значение pid означает порожденный процесс.

21-24 Если значение, возвращаемое fork(2), равно -1, то произошла ошибка. В этом случае вызывается функция perror(3), которая распечатывает сообщение о причине неуспеха. Затем программа завершается.

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

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

Файл: fork1.c

СИСТЕМНЫЙ ВЫЗОВ fork(2) - ПРИМЕР
1 #include

2 #include

3 #include

4 #include

5 static const int Bignumber = 10000;

6

7 main(int argc, char *argv[ ]) /* demonstrate fork(2) */



8 {

9 char ch, first, last;

10 pid_t pid;

11 int i;

12

13 if ((pid = fork()) > 0) { /* parent */



14 first = 'A';

15 last = 'Z';

16 }

17 else if (pid == 0) { /* child */



18 first = 'a';

19 last = 'z';

20 }

21 else { /* cannot fork(2) */



22 perror(argv[0]);

23 exit(1);

24 }

25 for (ch = first; ch <= last; ch++) {



26 /* delay loop */

27 for (i = 0; i < Bignumber; i++)

28 ; /* null */

29 write(1, &ch, 1);

30 }

31

32 exit(0);



33 }
      1. Системный вызов fork(2) - Пример

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

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

. время исполнения каждого процесса непредсказуемо.

. невозможно предсказать, какой из процессов закончится первым.

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

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

СИСТЕМНЫЙ ВЫЗОВ fork(2) - ПРИМЕР (ВЫВОД)


$ fork1

abcABdeCDfEFgGhijklHIJKmnopLMNOPQRqrstSTUuvwxyVWXYZ$ z


$ fork1

aAbBCDEFGHIJcdefghijkKLMNOPQRSlmnTUVopqrstWXYZ$ uvwxyz


$ fork1

abABCcdefgDEFGhijklmnoHIJKLMpNqOPrQsRtuvSTUwxVWyXzYZ$


$ fork1

abcAdeBCfDghEFGHIijkJKlLMNOmnopqPQRrsSTtuUVvWwxXyzYZ$


      1. Исполнение программы


Процесс может заменить текущую программу на новую, исполнив системный вызов exec(2). Этот вызов заменяет текст, данные, стек и остальные сегменты виртуального адресного пространства текущей программы на соответствующие сегменты новой программы. Однако пользовательская область при этом вызове сохраняется.

Существует шесть вариантов системного вызова exec(2). Обратите внимание, что за exec идет одна или несколько букв:

l (список аргументов),

v (вектор аргументов),

e (изменение среды) или

p (использование переменной PATH).

Формат вызова exec(2) определяет, какие данные передаются новой программе. Ниже приведены параметры различных версий exec(2):

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

file указывает на строку, содержащую имя загружаемого файла, который находится в одной из директорий, перечисленных в переменной PATH.

arg0,...,argn указывают на строки - значения параметров, которые надо передать новой программе. Эти значения помещаются в вектор argv[] - параметр функции main() новой программы. Количество параметров помещается в параметр argc функции main(). Список параметров должен завершаться нулевым указателем.

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

envp[] вектор указателей на строки, представляющие новые переменные среды для новой программы. Значения элементов этого массива копируются в параметр envp[] функции main() новой программы. Аналогично, environ новой программы указывает на envp[0] новой программы. Последний элемент envp[] должен содержать нулевой адрес.

cnup указатель на вектор указателей на строки, представляющие новые переменные среды новой программы; в действительности то же что и envp[].

arg0 или argv[0], следует устанавливать равным последней компоненте path или параметру file, то есть равным имени загружаемой программы. Некоторые программы воспринимают нулевой аргумент как значимый параметр. Так, программы gzip(1) и gunzip(1) (потоковые архиватор и деархиватор) обычно представляют собой один и тот же бинарный файл, который определяет, что ему делать (упаковывать или распаковывать) по имени команды, которой он был запущен, то есть по argv[0]. В некоторых дистрибутивах Linux используется утилита busybox (http://www.busybox.net/), которая, в зависимости от имени, под которым она была запущена, может имитировать большинство стандартных утилит Unix, таких, как ls(1), mv(1), cp(1), rm(1) а также ряд системных сервисов, таких, как crond(1M), telnetd(1M), tftpd(1M), всего более трёхсот разных программ. Таким образом, неправильное задание arg0 может привести к тому, что запускаемая программа поведёт себя совершенно неожиданным образом, например, вместо копирования файлов начнёт их удалять.




      1. Поделитесь с Вашими друзьями:
1   2   3   4   5   6   7


База данных защищена авторским правом ©grazit.ru 2017
обратиться к администрации

    Главная страница