Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Процессы и потоки в ОС 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”); } Для корректного завершения дочернего процесса в родительском процессе необходимо использовать функцию wait() или waitpid(): pid_t wait(int *status); Функция wait приостанавливает выполнение родительского процесса до тех пор, пока дочерний процесс не прекратит выполнение или до появления сигнала, который либо завершает текущий процесс, либо требует вызвать функцию-обработчик. Если дочерний процесс к моменту вызова функции уже завершился (так называемый «зомби»), то функция немедленно возвращается. Системные ресурсы, связанные с дочерним процессом, освобождаются. Функция waitpid приостанавливает выполнение родительского процесса до тех пор, пока дочерний процесс, указанный в параметре pid, не завершит выполнение, или пока не появится сигнал, который либо завершает родительский процесс, либо требует вызвать функцию-обработчик. Если указанный дочерний процесс к моменту вызова функции уже завершился (так называемый «зомби»), то функция немедленно возвращается. Системные ресурсы, связанные с дочерним процессом, освобождаются. Параметр pid может принимать несколько значений: pid<-1 означает, что нужно ждать любого дочернего процесса, чей идентификатор группы процессов равен абсолютному значению pid. pid=-1 означает ожидать любого дочернего процесса; функция wait ведет себя точно так же. pid =0 означает ожидать любого дочернего процесса, чей идентификатор группы процессов равен таковому у текущего процесса. pid>0 означает ожидать дочернего процесса, чем идентификатор равен pid. Значение options создается путем битовой операции ИЛИ над следующими константами: WNOHANG - означает вернуть управление немедленно, если ни один дочерний процесс не завершил выполнение. WUNTRACED -означает возвращать управление также для остановленных дочерних процессов, о чьем статусе еще не было сообщено. Каждый дочерний процесс при завершении работы посылает своему процессу-родителю специальный сигнал SIGCHLD, на который у всех процессов по умолчанию установлена реакция "игнорировать сигнал". Наличие такого сигнала совместно с системным вызовом waitpid() позволяет организовать асинхронный сбор информации о статусе завершившихся порожденных процессов процессом-родителем. Для перегрузки исполняемой программы можно использовать функции семейства 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); 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. Если требуется передать более одного параметра, они собираются в структуру, и передается адрес этой структуры.При удачном вызове функция pthread_create возвращает значение 0 и помещает идентификатор новой нити исполнения по адресу, на который указывает параметр thread. В случае ошибки возвращается положительное значение, которое определяет код ошибки, описанный в файле <errno.h>. Значение системной переменной errno при этом не устанавливается. Параметр attr служит для задания различных атрибутов создаваемой нити. Функция нити должна иметь заголовок вида: 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, заносится адрес, который вернул завершившийся 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. В основной программе создать 2 потока. После этого процесс-отец создает файл, записывает в него строки вида: N pid текущее время в формате: часы: минуты: секунды: миллисекунды (где N – номер выводимой строки) и выводит формируемые строки в левой половине экрана. Количество записываемых в файл строк k = 100. Оба потока читают строки из файла и выводят их в правой части экрана в виде: потокid потока текущее время (мсек) строка из файла Варианты индивидуальных заданий 1. Разработать программу по условию п.3, но вместо потоков создать 3 процесса, которые осуществляют те же функции, что и в п.3 (читают строки из файла и выводят их в правой части экрана) в виде: процессpid процесса текущее время (мсек) строка из файла. 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, выводит на экран следующую информацию: номер процесса/потока pidppid текущее время (мсек). Время t =((номер процесса/потока по дереву)*200 (мсек).
Лабораторная работа №5
|
||||
Последнее изменение этой страницы: 2016-08-16; просмотров: 2170; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.12.34.192 (0.009 с.) |