Рефераты. Теория вычислительных процессов и структур

QWER

REEEt

WEEEEEEERSIIIONN

Файл, полученный в результате работы программы:


1. 4

2. 5

3. 16

итого: 3 строки 25 символов


      7. Написать программу поиска заданного пользователем файла в текущем каталоге. Предусмотреть контроль ошибок.


      8. Написать программу сравнения двух заданных  пользователем файлов по их содержимому. Предусмотреть контроль ошибок.


9. Написать программу сравнения двух заданных пользователем каталогов.

Предусмотреть контроль ошибок.



 

 

 

 

 

 

 

 

Лабораторная работа №2

 

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

 

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

 

Теоретическая часть


Для создания процессов используется системный вызов fork:

#include <sys/types.h>

#include <unistd.h>

pid_t fork (void);

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

Созданный процесс называется дочерним процессом, а процесс, осуществляющий вызов fork, называется родительским.

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

Идею, заключённую в вызове fork, быть может, достаточно сложно понять тем, кто привык к схеме последовательного программирования. Ниже приведен пример, иллюстрирующий это понятие (рис. 2.1). На рисунке рассматриваются три строки кода, состоящие из вызова printf, за которым следуют вызов fork и ещё один вызов printf. Рисунок разбит на две части: До и После. Часть рисунка До показывает состояние до вызова fork. Существует единственный процесс А (его обозначили буквой А только для удобства, для системы это ничего не значит). Стрелка, обозначенная РС (Program counter – программный счётчик), указывает на выполняемый в настоящий момент оператор. Так как стрелка указывает на первый оператор printf, на стандартный вывод выдаётся тривиальное сообщение One.

Часть рисунка После показывает ситуацию сразу же после вызова fork. Теперь существуют два выполняемых одновременно процесса: А и В. Процесс А – это тот же самый процесс, что и в части рисунка До. Процесс В – это новый процесс, порождённый вызовом fork. Этот процесс является копией процесса А, кроме одного важного исключения – он имеет другое значение идентификатора (процесса pid), но выполняет ту же  самую программу, что и процесс А, т. е. те же три строки исходного кода, приведённые на рисунке. В соответствии с введенной выше терминологией процесс А является родительским процессом, а процесс В – дочерним. Две стрелки с надписью РС в этой части рисунка                                                                                                                                                                                               


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


        






Рис. 2.1. Вызов fork

показывают, что следующим оператором, который выполняется родителем и потомком после вызова fork, является вызов printf. Другими словами, оба процесса А и В продолжают выполнение с той же точки кода программы, хотя процесс В и является новым процессом для системы. Поэтому сообщение Two выводится дважды.

Вызов fork не имеет аргументов и возвращает идентификатор процесса pid_t. Родитель и потомок отличаются значением переменной pid: в родительском процессе значение переменной pid будет ненулевым положительным числом, для потомка же оно равно нулю. Так как возвращаемые в родительском и дочернем процессе значения различаются, то программист может задавать различные действия для двух процессов.

Следующая короткая программа более наглядно показывает работу вызова fork и использование процесса:

#include <unistd.h> 

main ()

{

    pid_t pid;       /*process-id в родительском процессе */

    printf (“Пока всего один процесс\n”);

    printf (“Вызов fork … \n”);

    pid = fork ();     /*Создание нового процесса */

    if (pid = = 0)

        printf (“Дочерний процесс\n”);

    else if (pid > 0)

        printf (“Родительский процесс, pid потомка %d\n, pid”);

    else

        printf (“Ошибка вызова fork, потомок не создан\n”);

}

Оператор if, следующий за вызовом fork, имеет три ветви. Первая определяет дочерний процесс, соответствующий нулевому значению переменной pid. Вторая задаёт действия для родительского процесса, соответствуя положительному значению переменной pid. Третья ветвь неявно соответствует отрицательному (а на самом деле равно –1) значению переменной pid, которое возвращается, если вызову fork не удаётся создать дочерний процесс. Это может означать, что вызывающий процесс попытался нарушить ограничения (например – число процессов одновременно выполняющихся и запущенных одним пользователем). В обоих случаях переменная errno содержит код ошибки EAGAIN. Обратите также внимание на то, что поскольку оба процесса, созданных программой, будут выполняться одновременно без синхронизации, то нет гарантии, что вывод родительского и дочернего процессов не будет смешиваться.

Для смены исполняемой программы можно использовать функции семейства exec. Основное отличие между разными функциями в семействе состоит в способе передачи параметров. Как видно из рис. 2.2, все эти функции выполняют один системный вызов execve.


Рис. 2.2. Дерево семейства вызовов exec

 

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

#include <unistd.h>

/* Для семейства вызовов execl аргументы должны быть списком, заканчивающимся NULL*/

/* Вызову execl нужно передать полный путь к файлу программы */

int execl (const char *path, const char *arg0,..., const char argn, (char *)0);

/* Вызову execlp нужно только имя файла */

int execlp (const char *file, const char *arg0,..., const char argn, (char *)0);

/* Для семейства вызовов execv нужно передать массив аргументов */

int execv (const char *path, char *const argv[]);

int execvp (const char *file, char *const argv[]);

Следующая программа использует вызов execl для запуска программы вывода содержимого каталога ls:


#include <unistd.h>

main()

{

  printf (“Запуск программы ls\n”);

  execl (“/bin/ls”, “ls”, “-l”, (char*)0);

/* Если execl возвращает значение, то вызов был неудачным*/

  perror(“Вызов execl не смог запустить программу ls”);

  exit(1);

}

Работа этой программы показана на рис. 2.3.


 














Рис. 2.3. Вызов exec

Другие формы вызова exec упрощают задание списков параметров запуска загружаемой программы. Вызов execv принимает два аргумента: первый является строкой, которая содержит полное имя и путь к запускаемой программе. Второй аргумент является массивом строк. Первый элемент этого массива указывает на имя запускаемой программы (исключая префикс пути). Оставшиеся элементы указывают на все остальные аргументы программы. Следующий пример использует вызов execv для запуска той же программы ls, что и в предыдущем примере:


include <unistd.h>

main()

 {

   char * const av[]={“ls”, “-l”, (char *)0};

   execv(“/bin/ls”, av);

/* Если мы оказались здесь, то произошла ошибка*/

   perror(“execv failed”);

   exit(1);

 }


Функции execlp и execvp почти эквивалентны функциям execl и execv. Основное отличие – первый аргумент есть просто имя программы, а не полный путь к ней.

Системные вызовы fork и exec, объединенные вместе, представляют мощный инструмент для программиста. Благодаря ветвлению при  использовании вызова exec во вновь созданном дочернем процессе программа может выполнять другую программу в дочернем процессе, не стирая себя из памяти. Следующий пример показывает, как это можно сделать:

Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9



2012 © Все права защищены
При использовании материалов активная ссылка на источник обязательна.