Добавил:
Developer Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ОС БГУИР.pdf
Скачиваний:
13
Добавлен:
25.03.2023
Размер:
771.61 Кб
Скачать

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

ПРОЦЕССЫ И ПОТОКИ В ОС LINUX

Цель работы: исследовать методы создания процессов в ОС Linux, ос- новные функции создания и управления процессами, обмен данными между процессами.

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

 

В ОС Linux для создания процессов используется системный вызов

fork():

Р

#include <sys/types.h>

#include <unistd.h>

pid_t fork (void);

 

В результате успешного вызова fork() ядро создает новый процессИ, который являет-

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

выполняет копию той же программы, что и создавший егоУпроцесс, при этом все

его объекты данных имеют те же самые значения, что и Гв вызывающем процессе. Созданный процесс называется доче ним процессом, а процесс, осущест-

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

процесса продолжают выполнение с оп р т , торый следует сразу же за вызо-

 

 

 

 

 

 

 

ора

вом fork(). Процессы выполняются в разных адресных пространствах, поэтому

 

 

 

 

 

 

 

к

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

 

Следующая программа бол

наглядно показывает работу вызова fork() и

использование процесса:

 

е

#include <stdio.h>

 

т

 

#include <unistd.h>

 

int main ()

 

 

 

 

 

 

о

 

 

{

 

 

 

 

 

pid_t pid;

/* дентификатор процесса */

 

 

 

 

и

 

 

 

 

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

 

 

л

 

 

 

 

 

pid = fork ();

/* создание нового процесса */

 

printf (“Уже два процесса\n”);

 

 

б

 

 

 

 

 

 

if (pid = = 0)

 

 

 

 

 

 

и{

 

 

 

 

 

 

 

printf (“Это Дочерний процесс его pid=%d\n”, getpid());

Б printf (“А pid его Родительского процесса=%d\n”, getppid());

}

else if (pid > 0)

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

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

}

17

Для корректного завершения дочернего процесса в родительском процес- се необходимо использовать функцию wait() или waitpid():

pid_t wait(int *status);

pid_t waitpid(pid_t pid, int *status, int options);

Функция wait приостанавливает выполнение родительского процесса до тех пор, пока дочерний процесс не прекратит выполнение, или до появления сигнала, который либо завершает текущий процесс, либо требует вызвать функцию- обработчик. Если дочерний процесс к моменту вызова функции уже завершился (так называемый «зомби»), то функция немедленно возвращается. Системные ре-

сурсы, связанные с дочерним процессом, освобождаются.

Р

 

Функция waitpid приостанавливает выполнение родительского процесса

до тех пор, пока дочерний процесс, указанный в параметреИpid, не завершит выполнение или пока не появится сигнал, который либо завершает родитель- ский процесс, либо требует вызвать функцию-обработчик. Если указанный до- черний процесс к моменту вызова функции уже завершился (так называемый «зомби»), то функция немедленно возвращается. Системные ресурсы, связан-

ные с дочерним процессом, освобождаются.

У

 

 

 

Параметр pid может принимать несколько значений:

pid < –1 означает, что нужно ждать любой дочернийГ процесс, чей иден-

тификатор группы процессов равен абсолютному значению pid.

pid = –1 означает, что нужно ожид ть любойБдочерний процесс; функция

wait ведет себя точно так же.

 

 

 

pid = 0 означает, что нужно ожид ть любой дочерний процесс, чей иден-

 

 

а

 

тификатор группы процессов рав н та овому у текущего процесса.

 

к

 

 

е

 

 

pid > 0 означает, что нужно ожидать дочерний процесс, чей идентифика-

тор равен pid.

о

Значение options с здае ся пу ем битовой операции ИЛИ над следую-

щими константами:

 

и

WNOHANG значаеттвернуть управление немедленно, если ни один до-

л

 

черний процесс не заверш л выполнение.

WUNTRACED означает возвращать управление также для остановлен- ных дочернихбпроцессов, о чьем статусе еще не было сообщено.

Каждый дочерний процесс при завершении работы посылает своему про- цессуи-род телю специальный сигнал SIGCHLD, на который у всех процессов поБумолчан ю установлена реакция «игнорировать сигнал». Наличие такого

сигнала совместно с системным вызовом waitpid() позволяет организовать асинхронный сбор информации о статусе завершившихся порожденных про- цессов процессом-родителем.

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

int execl(char *pathname, char *arg0, arg1, ..., argn, NULL);

int execle(char *pathname, char *arg0, arg1, ..., argn, NULL, char **envp); int execlp(char *pathname, char *arg0, arg1, ..., argn, NULL);

18

int execlpe(char *pathname, char *arg0, arg1, ..., argn, NULL, char **envp); int execv(char *pathname, char *argv[]);

int execve(char *pathname, char *argv[],char **envp); int execvp(char *pathname, char *argv[]);

int execvpe(char *pathname, char *argv[],char **envp);

Существует расширенная реализация понятия процесс, когда процесс

представляет собой совокупность выделенных ему ресурсов и набора нитей исполнения. Нити (threads) или потоки процесса разделяют его программный

код, глобальные переменные и системные ресурсы, но каждая нить имеет соб-

скольку традиционный процесс в концепции нитей исполнения трактуетсяРкак процесс, содержащий единственную нить исполнения, можно узнать иденти-

ственный программный счетчик, свое содержимое регистров и свой стек. Все

глобальные переменные доступны в любой из дочерних нитей. Каждая нить

исполнения имеет в системе уникальный номер идентификатор нити. По-

фикатор этой нити и для любого обычного процесса. Для этого используется

 

 

 

 

 

 

И

функция pthread_self(). Нить исполнения, создаваемую при рождении нового

процесса, принято называть начальной, или главной нитьюУисполнения этого

процесса. Для создания нитей используется функция pthread_create:

#include <pthread.h>

 

 

 

 

Г

 

 

 

 

 

 

 

 

 

int pthread_create( pthread_t *thread, const pthread_attr_t

*attr,

void

 

 

 

 

Б

 

 

*(*start_routine)( void*), void *arg);

 

 

Функция создает

новую

нить,

в оторой выполняется

функция поль-

зователя start_routine, и передает

й в

 

а

 

 

ач стве аргумента параметр arg. Если тре-

 

 

 

к

 

 

 

 

 

е

 

 

 

 

буется передать более одного парам тра, они собираются в структуру, и передает-

рый указывает параметр threadт. В случае ошибки возвращается положительное значение, которое определяет к д ошибки, описанный в файле <errno.h>. Значе-

ся адрес этой структуры. При удачном вызове функция pthread_create возвращает

ние системной переменнойerrno при этом не устанавливается. Параметр attr

значение 0 и помещает иден ифика ор новой нити исполнения по адресу, на кото-

служит для задан я разл чных атрибутов создаваемой нити. Функция нити долж-

на иметь заголовок вида

void * start routine (void *)

 

 

л

Завершен е функции потока происходит в следующих случаях:

 

б

функц я нити вызвала функцию pthread_exit();

и

 

Б

 

 

функция нити достигла точки выхода; ∙ нить была досрочно завершена другой нитью.

Функция pthread_join() используется для перевода нити в состояние ожидания:

#include <pthread.h>

int pthread_join (pthread_t thread, void **status_addr);

Функция pthread_join() блокирует работу вызвавшей ее нити исполнения до за- вершения нити с идентификатором thread. После разблокирования в указатель, расположенный по адресу status_addr, заносится адрес, который вернул завер-

19

Варианты индивидуальных заданий

шившийся thread либо при выходе из ассоциированной с ним функции, либо при выполнении функции pthread_exit(). Если пользователя не интересует, что вернула ему нить исполнения, то в качестве этого параметра можно использо- вать значение NULL.

Для компиляции программы с нитями необходимо подключить библиоте- ку pthread.lib следующим способом:

gcc 1.c –o 1.exe -lpthread

Время в Linux отсчитывается в секундах, прошедшее с начала этой эпохи (00:00:00 UTC, 1 Января 1970 года). Для получения системного времени можно

использовать следующие функции:

 

 

 

Р

#include <sys/time.h>

 

 

 

 

time_t

time (time_t *tt);

 

 

 

 

int gettimeofday(struct timeval *tv, struct timezone *tz);

 

 

 

 

 

 

struct timeval {

 

 

 

 

И

 

long

tv_sec;

/* секунды */

 

long

tv_usec;

 

/* микросекундыУ*/

 

};

 

 

 

Г

 

 

 

 

 

 

 

 

Порядок выполнения работы

 

 

 

 

 

 

Б

 

1.

Изучить теоретическую часть р боты.

 

2.

 

 

 

а

 

Написать программу, создающую два дочерних процесса, с использо-

ванием двух вызовов fork(). Родиткийльс

и два дочерних процесса должны

 

 

е

 

 

 

выводить на экран свой pid и pid родительского процесса (для дочерних про-

цессов) и текущее время втформа е часы: минуты: секунды: миллисекунды.

Используя вызов system(), выполни ь команду ps -x в родительском процессе. Найти свои процессы в списке запущенных процессов.

3.

В основной

грамме создать два потока. После этого процесс-отец

создает

 

 

 

про

файл, за сывает в него строки вида: N pid, текущее время в формате

 

 

 

пи

часы: минуты: секунды: миллисекунды (где N номер выводимой строки) и

вывод

формируемые строки в левой половине экрана. Количество записы-

 

 

л

 

 

б

 

 

ит

 

 

 

ваемых в файл строк k = 100. Оба потока читают строки из файла и выводят их вБправой части экрана в виде поток id потока текущее время (миллисекунды

(мсек)) строка из файла

1. Разработать программу по условию п. 3 порядка выполнения работы, но вместо потоков создать 3 процесса, которые осуществляют те же функции, что и в п. 3 (читают строки из файла и выводят их в правой части экрана), в ви-

де процесс pid процесса текущее время (миллисекунды (мсек)) строка из файла.

20

2. Написать программу нахождения массива K последовательных значений функции y[i]=sin(2*PI*i/N) (i=0,1,2…K-1 ) с использованием ряда Тейлора. Поль- зователь задает значения K, N и количество n членов ряда Тейлора. Для расчета каждого члена ряда Тейлора запускается отдельная нить. Каждая нить выводит на экран свой id и рассчитанное значение ряда. Головной процесс суммирует все члены ряда Тейлора и полученное значение y[i] записывает в файл.

3. Написать программу синхронизации двух каталогов, например Dir1 и Dir2. Пользователь задает имена Dir1 и Dir2. В результате работы программы файлы, имеющиеся в Dir1, но отсутствующие в Dir2, должны скопироваться в Dir2 вместе с правами доступа. Процедуры копирования должны запускаться с использованием функции fork() в отдельном процессе для каждого копируемо- го файла. Каждый процесс выводит на экран свой pid, имя копируемого файла и

число скопированных байт. Число запущенных процессов не должно превы-

шать N (вводится пользователем).

Р

 

4. Написать программу поиска одинаковых по содержимому файлов в

 

И

двух каталогов, например Dir1 и Dir2. Пользователь задает имена Dir1 и Dir2.

В результате работы программы файлы, имеющиеся в

УDir1, сравниваются с

файлами в Dir2 по их содержимому. Процедуры сравненияГдолжны запускать-

ся с использованием функции fork() в отдельном процессе для каждой пары

сравниваемых файлов. Каждый процесс выводит на экран свой pid, имя файла,

 

Б

число просмотренных байт и результаты ср внения. Число запущенных про-

цессов не должно превышать N (вводится пользов телем).

 

а

 

5. Написать программу поиска заданной пользователем комбинации из m

байт (m < 255) во всех файлах кущ каталогаго

. Пользователь задает имя ка-

е

 

 

талога. Главный процесс о крывает ка алог и запускает для каждого файла ка-

талога отдельный процесс поиска заданной комбинации из m байт. Каждый

процесс выводит на экран св й pid, имя файла, число просмотренных байт и ре-

зультаты поиска. Ч сло запущенных процессов не должно превышать N (вво-

 

т

дится пользовате ем). о

6. Разработать программу «интерпретатор команд», которая воспринима-

и

 

ет команды, вводимые с клавиатуры, и осуществляет их корректное выполне-

ние. Для этого каждая вводимая команда должна выполняться в отдельно за-

 

 

л

 

 

б

 

и

 

 

пускаемом процессе с использованием вызова exec(). Предусмотреть контроль

Б

 

 

 

ошибок.

 

 

 

7. Создать дерево процессов/потоков по индивидуальному заданию. Ка-

ждый процесс/поток постоянно,

через время t, выводит на экран следующую

информацию:

 

 

номер процесса/потока pid

ppid текущее время (миллисекунды (мсек)).

Время t =((номер процесса/потока по дереву)*200 (миллисекунды

(мсек)).

21

Соседние файлы в предмете Современные операционные системы