Тема: Управление процессами. Создание и уничтожение процессов. Тины процессов.
Цель работы: овладеть навыками использования системных вызовов для порождения, дублирования завершения процессов.
Теоретические сведения.
Порождение процессов.
Новый процесс порождается системным вызовом fork (), который создает дочерний процесс - копию родительского. В дочернем процессе выполняется та же программа, что и в родительском, и когда дочерний процесс начинает выполняться, он выполняется с точки возврата из системного вызова fork () . Системный вызов fork () возвращает родительскому процессу PID дочернего процесса, а дочернему процессу - 0. По коду возврата вызова fork () дочерний процесс может "опознать" себя как дочерний. Свой PID процесс родитель может получить при помощи системного вызова getpid(), a PID родительского процесса - при помощи системного вызова getpp.idd (). Б.сли требуется, чтобы в дочернем процессе выполнялась программа, отличная от программы родительского процесса, процесс может сменить выполняемую в нем программу при помощи одного из системных вызовов семейства exec (). Все вызовы этого семейства загружают для выполнения в процессе программу из заданного в вызове файла и отличаются друг от друга способом передачи параметров этой программе. Таким образом, наиболее распространенный контекст применения системного вызова forк () выглядит примерно так:
/* порождение дочернего процесса и запоминание его PID */
if (!(ch_pid-fork())
/* загрузка другой программы в дочернем процессе */ exec(программа);
else
продолжение родительского процесса
Fork ()
порождение нового процесса
Синтаксис
#include <unistd.h>
pid_ t. fork (void) ;
Описание
fork создает новый процесс, почти точную копию родительского процесса.
Новый процесс отличается от родительского только значениями идентификатора текущего процесса (PID) и идентификатором родительского процесса (PPID), а также тем, что счетчики использования ресурсов установлены в 0.
Основные характеристики родительского процесса, наследуемые дочерним процессом следующие:
· Окружение.
· Способы обработки сигналов, адреса функций обработки сигналов.
· Приоритет.
· Все присоединенные разделяемые сегменты памяти.
· Идентификатор группы процессов.
· Идентификатор группы терминала.
· Текущий рабочий каталог.
· Корневой каталог.
Блокировки файлов и сигналы, ожидающие обработки, не наследуются.
Порожденный процесс получает свои собственные копии родительских дескрипторов файлов. Каждый дескриптор файла порожденного процесса разделяет с соответствующим родительским дескриптором файла общий указатель текущей позиции в файле.
Возвращаемое значение
При успешном завершении родителю возвращается PID дочернего процесса, а дочернему процессу возвращается 0. При неудаче родительскому процессу возвращается -1, дочерний процесса не создается, а переменной errno присваивается код ошибки.
Getpid(), getppid ()
получение идентификатора процесса
Синтаксис
#include <unistd.h>
pidt getpid(void); pid_t getppid(void);
Описание
fork возвращает идентификатор текущего процесса.
fork возвращает идентификатор родительского процесса
Execl, execv, execle, execve, execlp, execvp (3)
выполнение файла
Синтаксис
#include <unistd.h>
extern char **environ;
int execl( const char *path, const char *arg, ...);
int execlp( const char *file, const char *arg, ...);
int execle( const char *path, const char *arg , …, char * const envp[]);
int execv( const char *path, char *const argv[]);
int execvp( const char *file, char *const argv[]);
Описание
Системные вызовы семейства exec заменяют текущий образ процесса на новый образ, считываемый из заданного файла.
Параметры
Первый аргумент всех функций является указателем на символьную строку, содержащую полное имя исполняемого файла (path). Для функций execlp и execvp имя файла может задаваться без пути (file). Если первый аргумент этих функций не содержит символа "/", то файл ищется в каталогах, определенных в переменной окружения PATH.
Аргументы arg, . . . функций execl, exelp, execle составляют список указателей на символьные строки, содержащие параметры, передаваемые программе. По соглашениям первый элемент чэтого списка должен содержать имя программного файла. Список параметров должен заканчиваться пустым указателем - NULL или (char *)0.
В функциях execv и execvp параметры, передаваемые программе, передаются через массив символьных строк. Аргумент argv является указателем на этот массив.
Аргумент envp функции функций execle также является массивом указателей на символьные строки. Эти строки представляют собой окружение среду для нового образа процесса. Последний элемент массива envp должен быть пустым указателем.
Возвращаемое значение
При нормальном выполнении функции возвращают 0. При ошибках выполнения функции возвращают -1 и устанавливают errno
Текст программы fork1.c:
'$cat fork1.c'
#include <stdio.h>
main()
{int pid;
Printf("I`m the original process with pid %d and ppid %d\n", getpid(), getppid());
pid = fork();
If (pid!=0)
{printf("I'm the parent process with pid %d and ppid %d\n", getpid(), getppid());
printf("my child's pid is %d\n", pid);
}
else
{/* esli pid=0, this is child process*/
printf("I'm the child process with pid %d and ppid %d/n", getpid(), getppid()); }
printf("/npid %d terminates./n", getpid());
}
Выполнение программы fork1.exe:
'$ps'
PID TTY TIME COMD
638 term/tty014 0:01 sh
1260 term/tty014 0:05 ps
$fork1
I'm the original process with pid 1265 and ppid 638
I'm the child process with pid 1266 and ppid 1265
pid 1266 terminates.
I'm the parent process with pid 1265 and ppid 638
My child's pid is 1266
pid 1265 terminates.
Описание работы программы fork1.exe:
Вводится переменная целого типа pid. Запуск программы fork1.exe имеет свой номер PID 1265 и родительский номер PPID 638 (номер процесса работы shell), что и прописывается на дисплее.
Далее системным вызовом fork() происходит создание нового процесса, а точнее дублирование существующего процесса под номером 1265, таким образом переменной pid присваивается значение дочернего процесса 0 и 1266. Таким образом, у нас получается два параллельно идущих процесса - родительский и дочерний.
Сначала проходить ветвь условного оператора при значении условия - ложь, т.е. значение PID=0, показывается надпись о дочернем процессе и он завершается.
Далее проходить ветвь условного оператора при значении условия - истина, т.е.значение РЮ=1266, и показывается надпись родительском процессе и его завершении.
Текст программы fork2.c:
‘$cat fork2.с'
#include <stdio.h>
main()
{int pid;
printf(“I`m the original process with %d and ppid %d\n", getpid(), getppid());
pid = fork();
if(pid!=0)
{printf(“I`m the parent process with pid %d and ppid %d\n", getpid(), getppidO);
printffMy child's pid is %d\n", pid);}
else
{/*esli pid=0, this is child process*/
sleep(5);
prinf("I'm the child process with pid %d and ppid %d\n", getpid(), getppid());
printf("\n was deserted");
}
printf("\n pid %d terminatesAn", getpid());}
Выполнение программы fork2.exe:
‘$ps’
PID TTY TIME COMD
638 term/tty014 0:01 sh
1387 term/tty014 0:05 ps
'$fork2’
I'm the original process with pid 1395 and ppid 638
I'm the parent process with pid 1395 and ppid 638
My child's pid is 1396
pid 1395 terminates
I'm the child process with pid 1396 and ppid 1
Was deserted
pid 1396 terminates.
Описание работы программы fork2.exe:
Вводится переменная целого типа pid. Запуск программы fork2.exe имеет свой номер PID 1395 и родительский номер РРГО 638 (номер процесса работы shell), что и прописывается на дисплее.
Далее системным вызовом fork() происходит создание нового дочернего процесса, а точнее дублирование существующего процесса под номером 1395, таким образом, переменной pid присваивается значение дочернего 1396 и родительского процесса 0. Соответственно, у нас тоже получается два параллельно работающих процесса.
Но т.к. в процессе выполнения программы включается функция sleep(5), которая осуществляет засыпание дочернего процесса на некоторое время и осуществляется работа условного оператора при значении условия - истина, т.е. PID=1396, далее показывается надпись о родительском процессе, который завершает свою работу.
Проходит работа оператора sleep(5), дочерний процесс оживает и продолжает идти по телу цикла. Но т.к. родительский процесс уже заверил свою работу, то его родителем становится процесс инициализации (init). Далее дочерний процесс завершает свою работу.
Текст программы myexec.c:
‘$cat myexec.с'
#include <stdio.h>
main()
{printf("I'm process of main program %d and I'm about to exec an ls -l\n", getpld());
exec(“/bin/ls", "ls", "-l", NULL);
printf("this is line should be never executed\n");
}
Выполнение программы myexec.exc:
‘myexec'
"I'm process of main program 1456 and I'm about tj exec an ls –l
…
…
…
Описание работы программы mуехес.ехе:
В процессе выполнения программы процесс программы под номером PID=1456 заменяется на новый процесс, указанный в функции ехес(). Таким образом, происходит создание нового процесса.
Синтаксис:
exec("const char*path, char*argl, ...char*argN, NULL) - сначала указывается путь, откуда берется значение аргумента, далее берегся значение аргумента.
Текст программы redirect.c:
‘$cat redirect.c'
#include <stdio.h>
#iriclude <fcntl.h>
main (argc, argv) //аргументы командной строки
int argc;
char *argv[2];
{int fd; //файловый дескриптор (номер файла, с которым происходит работа в программе, используемой ОС)
fd = open(argv[1],O_CREAT|O_TRUNC|O_WRONLY_0600);
dup2(fd,1);
close(fd);
execvp(argv[2], &argv[2]);
perror("main");
}
Выполнение программы rcdirect.c:
‘$redirect fff who'
'$cat fff'
User term/tty010 Jan 00:09
…
Описание работы программы redirect.c:
В программе происходит создание файла пользователем (если файла нет) или дополняет
файл(если файл существует), а затем перенаправление системного вызова, в нашем
примере - это системный вызов who, а файл называется fff. В программе используется
команда open, которая создает файл при помощи макросов:
O_CREAT - создать, если не существует,
O_JTRUNC - сократить (truncate) существующий,
O_WRONLY - только запись,
0600 - файл можно читать и писать.
Далее происходит дублирование в файл при помощи команды dup(). т.к. используя указанный файловый дескриптор fd. мы связали команды. Далее закрываем файл. А затем при помощи команды execvp() мы создаем процесс п добавляем его в файл.
open(<имя файла>,<режим открытия>,<права доступа к файлу>) - функция открытия файла, возвращает целое число, именуемое пользовательский дескриптор файла.
dup(<дескриптор файла, копируемый функцией>) - функция копирует дескриптор файла в первое свободное место в таблице пользовательских дескрипторов, возвращая новый дескриптор пользователю.
close(<файловый дескриптор открытого файла>) процесс закрывает открытый файл, когда процессу больше не нужно обращаться к файлу.
Лабораторная работа №3