Методические указания к лабораторным работам для студентов IV

СОДЕРЖАНИЕ: Цель работы: Изучить способы и средства написания параллельно выполняющихся процессов и потоков средствами языка C++ в операционных системах семейства Windows

Министерство образования и науки

Российской Федерации

НОВОСИБИРСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ

№ ____

СИСТЕМНОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ

Методические указания к лабораторным работам

для студентов IV курса АВТФ

(специальность 2 1 0 1 00)

дневной формы обучения

НОВОСИБИРСК

2004

Составитель: А.В. Гунько , канд. техн. наук, доц.

Работа подготовлена на кафедре автоматики

© Новосибирский государственный технический университет, 200 4 г.

ЛАБОРАТОРНАЯ РАБОТА № 1

МНОГОЗАДАЧНОЕ И МНОГОПОТОЧНОЕ

ПРОГРАММИРОВАНИЕ В WINDOWS

1. Цель работы: Изучить способы и средства написания параллельно выполняющихся процессов и потоков средствами языка C++ в операционных системах семейства Windows.

2. Краткие теоретические сведения.

Применяемые функции WinAPI, их параметры.

CreateProcess – создание процесса :

BOOL CreateProcess

(LPCTSTR lpApplicationName, // имя исполняемого модуля

LPTSTR lpCommandLine, // Командная строка

LPSECURITY_ATTRIBUTES lpProcessAttributes,

// Указатель на структуру SECURITY_ATTRIBUTES

LPSECURITY_ATTRIBUTES lpThreadAttributes,

// Указатель на структуру SECURITY_ATTRIBUTES

BOOL bInheritHandles, // Флаг наследования текущего процесса

DWORD dwCreationFlags, // Флаги способов создания процесса

LPVOID lpEnvironment, // Указатель на блок среды

LPCTSTR lpCurrentDirectory, // Текущий диск или каталог

LPSTARTUPINFO lpStartupInfo,// Указатель нас структуру STARTUPINFO

LPPROCESS_INFORMATION lpProcessInformation

// Указатель нас структуру PROCESS_INFORMATION

);

pApplicationName. Указатель на строку которая заканчивается нулем и содержит имя выполняемого модуля. Этот параметр может быть NULL - тогда имя модуля должно быть в lpCommandLine самым первым элементом. Если операционная система NT и модуль 16 разрядов - параметр NULL стоит обязательно. Имя модуля может быть абсолютным или относительным. Если относительное, то будет использована информация из lpCurrentDirectory или текущий каталог.

lpCommandLine. Командная строка. Здесь передаются параметры. Она может быть NULL. Здесь можно указать и путь и имя модуля.

lpProcessAttributes.Здесь определяются атрибуты защиты для нового приложения. Если указать NULL то система сделает это по умолчанию.

lpThreadAttributes. Здесь определяются атрибуты защиты для первого потока созданного приложением. NULL опять приводит к установке по умолчанию.

bInheritHandles. Флаг наследования от процесса, производящего запуск. Здесь наследуются дескрипторы. Унаследованные дескрипторы имеют те же значения и права доступа, что и оригиналы.

dwCreationFlags. Флаг способа создание процесса и его приоритет:

CREATE_DEFAULT_ERROR_MODE - новый процесс не наследует режим ошибок (error mode) вызывающего процесса.

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

CREATE_NEW_PROCESS_GROUP - создаваемый процесс - корневой процесс новой группы.

CREATE_SEPARATE_WOW_VDM - (только Windows NT): Если этот флаг установлен, новый процесс запускается в собственной Virtual DOS Machine (VDM).

CREATE_SHARED_WOW_VDM - (только Windows NT): Этот флаг указывает функции CreateProcess - запустить новый процесс в разделяемой Virtual DOS Machine.

CREATE_SUSPENDED - первичная нить процесса создается в спящем (suspended) состоянии и не выполняется до вызова функции ResumeThread.

CREATE_UNICODE_ENVIRONMENT - если этот флаг установлен, блок переменных окружения, указанный в параметре lpEnvironment, использует кодировку Unicode. Иначе - кодировку ANSI.

DEBUG_PROCESS - если этот флаг установлен, вызывающий процесс считается отладчиком, а новый процесс - отлаживаемым.

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

DETACHED_PROCESS - создаваемый процесс не имеет доступа к родительской консоли.

Этот флаг нельзя использовать с флагом CREATE_NEW_CONSOLE.

HIGH_PRIORITY_CLASS - указывает на то, что процесс выполняет критичные по времени задачи

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

NORMAL_PRIORITY_CLASS - указывает на процесс, без каких либо специальных требований к выполнению.

REALTIME_PRIORITY_CLASS - указывает процесс имеющий наивысший возможный приоритет.

lpEnvironment. Указывает на блок среды. Если NULL, то будет использован блок среды родительского процесса. Блок среды это список переменных имя=значение в виде строк с нулевым окончанием.

lpCurrentDirectory.Указывает текущий диск и каталог. Если NULL то будет использован диск и каталог процесса родителя.

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

STARTUPINFO sti;// структура

ZeroMemory(sti,sizeof(STARTUPINFO));// обнулить

sti.cb=sizeof(STARTUPINFO);// указать размер

lpProcessInformation Структура PROCESS_INFORMATION с информацией о процессе. Будет заполнена Windows.

В результате выполнение функций вернет FALSE или TRUE. В случае успеха TRUE. Пример использования.

#include stdafx.h

#include windows.h

#include iostream.h

void main()

{

STARTUPINFO cif;

ZeroMemory(cif,sizeof(STARTUPINFO));

PROCESS_INFORMATION pi;

if (CreateProcess(c:\\windows\\notepad.exe,NULL,

NULL,NULL,FALSE,NULL,NULL,NULL,cif,pi)==TRUE)

{

cout process endl;

cout handle pi.hProcess endl;

Sleep(1000);// подождать

TerminateProcess(pi.hProcess,NO_ERROR);// убрать процесс

}

}

CreateThread – создание потока:

HANDLE CreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes

DWORD dwStackSize ,

LPTHREAD_START_ROUTINE lpStartAddress ,

LPVOID lpParameter ,

DWORD dwCreationFlags ,

LPDWORD lpThreadId );

lpSecurityAttributes - обычно устанавливается в нуль, чтобы использовать заданные по умолчанию атрибуты защиты.

dwStackSize - размер стека. Каждый поток имеет собственный стек.

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

lpParameter - long 32 разрядный параметр, который передается функции, запускающей новый поток.

dwCreationFlags - 32 бит переменная флагов, которая позволяет Вам управлять запуском потока (активный, приостановленный и т.д.). Подробнее об этих флагах можно почитать в Microsofts online 32 bit reference.

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

Для упорядоченного вывода результатов работы процессов (потоков) на экран можно использовать функцию Sleep ():

VOID Sleep(

DWORD dwMilliseconds // время ожидания в милисекундах

);

Поток вызывает функцию Sleep для того, чтобы добровольно отложить свое выполнение. Функция Sleep не осуществляет возврата до тех пор, пока не истечет указанное время. В течение него выполнение потока приостанавливается, и выделения для него процессорного времени не происходит.

Если параметр функции Sleep задан равным нулю, то поток будет лишен остатка выделенного ему кванта процессорного времени.

Когда поток вызывает функцию Sleep, задержка на заданное время относится к этому потоку. Система продолжает выполнять другие потоки этого и других процессов.

3. Методические указания.

Проект может быть реализован на Visual C++ 6.0 или в среде Borland C++ 5.0. В первом случае выбирается консольное приложение Win32 без дополнительных библиотек. Во втором случае в программу необходимо добавить файл включения windows.h.

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

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

4.1. Написать и отладить программу, реализующую порожденный процесс.

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

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

5. Варианты заданий.

5.1. Поиск всех простых чисел в указанном интервале чисел, разделенном на несколько диапазонов:

5.1.1. разработать программу, вычисляющую все простые числа в произвольном интервале;

5.1.2. написать программу, разбивающую заданный интервал на несколько диапазонов и проводящую поиск в каждом диапазоне в отдельном процессе. Запуск написанной ранее программы осуществлять функцией CreateProcess().

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

5.2. Поиск указанной строки в указанном файле:

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

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

5.2.3. написанную программу поиска сочетания символов в одной строке представить в виде функции, которую следует вызывать из главной подпрограммы посредством функции CreateThread().

5.3. Умножение матрицы произвольного порядка на вектор:

5.3.1. Написать программу, умножающую строку произвольной длины на столбец;

5.3.2. разработать программу, реализующую произведение матрицы произвольного порядка на вектор. Умножение каждой строки производить в отдельном процессе, вызывая написанную подпрограмму умножения строки на столбец;

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

5.4. Свертка сигнала с импульсной характеристикой фильтра (). N – число отсчетов импульсной характеристики необходимо разбить на несколько диапазонов:

5.4.1. написать программу, реализующую свертку сигнала с некоторым диапазоном отсчетов импульсной характеристики;

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

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

5.4.4. В качестве импульсной характеристики можно использовать характеристику идеального НЧ – фильтра: где - частота среза (00.5) .

5.5. Warcraft. Заданное количество юнитов добывают золото равными порциями из одной шахты, задерживаясь в пути на случайное время, до ее истощения:

5.5.1. написать программу работы одного юнита;

5.5.2. написать программу с произвольным количеством юнитов, работа каждого должна осуществляться в отдельном процессе. В качестве порожденного процесса применять написанную программу работы одного юнита, запуская ее из функции CreateProcess();

5.5.3. работу одного юнита представить в отдельной функции и вызывать ее в отдельном потоке для каждого юнита функцией CreateThread().

5.6. Винни-Пух и пчелы. Заданное количество пчел добывают мед равными порциями, задерживаясь в пути на случайное время. Винни-Пух потребляет мед порциями заданной величины за заданное время и столько же времени может прожить без питания.

5.6.1. реализовать программу работы одной пчелы;

5.6.2. разработать программу, в которой осуществляется работа Винни-Пуха и для заданного количества пчел вызывается отдельный процесс работы одной пчелы;

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

5.7. Net Send . Отправка заданного сообщения по указанному диапазону IP -адресов сети класса «С» (254 адреса, маска 255.255.255.0) разделенному на несколько поддиапазонов:

5.7.1. разработать программу отправки сообщения по диапазону IP-адресов;

5.7.2. написать программу, разбивающую заданный диапазон IP-адресов на поддиапазоны и для каждого поддиапазона в порожденном процессе запускать программу, осуществляющую отправку сообщения;

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

5.7.4. Для лабораторных работ 3, 4 вместо Net Send использовать команду ping.

5.8. Шарики. Координаты заданного количества шариков изменяются на случайную величину по вертикали и горизонтали, при выпадении шарика за нижнюю границу допустимой области шарик исчезает:

5.8.1. написать программу изменения координат одного шарика;

5.8.2. написать программу, создающую для каждого из заданного количества шариков порожденный процесс изменения их координат;

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

5.9. Противостояние двух команд – каждая команда увеличивается на случайное количество бойцов и убивает случайное количество бойцов участника.

5.9.1. написать программу, которая бы осуществляла уменьшение числа бойцов в противостоящей команде и увеличение в своей на случайную величину;

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

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

6. Содержание отчета.

6.1. Цель работы.

6.2. Вариант задания.

6.3. Листинги программ.

7. Контрольные вопросы.

7.1. Что такое API? Какие они бывают?

7.2. Задачи, решаемые API ОС.

7.3. Варианты реализации API. Чем они отличаются?

7.4. Реализация функций API на уровне ОС. Особенности.

7.5. Реализация функций API на уровне системы программирования. Особенности.

7.6. Реализация функций API с помощью внешних библиотек. Особенности.

7.7. Платформенно-независимый интерфейс POSIX.

7.8. Создание процессов средствами WinAPI.

7.9. Создание потоков средствами WinAPI.

7.10. Чем отличаются процессы и потоки?

7.11. Средства синхронизации потоков в WinAPI.

7.12. Средства получения информации о процессах и потоках.

ЛАБОРАТОРНАЯ РАБОТА №2

ОЗНАКОМЛЕНИЕ С ОС LINUX

1. Цель работы: Ознакомление с операционной системой и основными командами работы с файлами и каталогами ОС Unix/Linux.

2. Краткие теоретические сведения.

Лабораторная работа выполняется в среде Red Hat Linux, доступ к которой осуществляется с рабочего места, функционирующего в среде ОС Windows 2000 через защищенный протокол ssh, обеспечиваемый программой putty. При выполнении лабораторной работы Вы становитесь клиентом сервера Linux и используете окно программы putty как терминал сервера. Работа выполняется в текстовом режиме, мышь не поддерживается.

По умолчанию приглашением в Unix/Linux является символ #, в ответ не которое Вы можете вводить команды. Левее него в квадратных скобках указана Ваша учетная запись и имя сервера, а также текущий каталог.

В сеансе работы с Linux Вашим текущим (домашним) каталогом является каталог: /home/имя , где имя - Ваше сетевое имя. К этому каталогу Вы имеете права чтения, записи, выполнения. Вы не имеете права записи к каталогам, не являющимся подкаталогами вашего домашнего каталога, если их владельцы (или системный администратор) не дали Вам соответствующих прав. Не забывайте, что в Unix/Linux символ слэш - разделитель имен каталогов наклонен вправо: /!

Для получения справки по командам и программам служат инструкции man и info. Некоторые разделы справки даны на английском языке. Краткое описание применяемых в лабораторной работе команд и программ на русском языке доступно на сайте автора методических указаний: http://ait.cs.nstu.ru/~gun/ssw

Для окончания сеанса работы с Linux введите exit.

3. Методические указания.

3.1. Вводите команды ОС только при наличии приглашения оболочки (символ #).

3.2. В используемой оболочке имеется буфер команд, перемещение по которому производится стрелками управления курсором (вверх и вниз). Для редактирования команды из буфера используются клавиши - и -, BackSpace и Delete.

3.3. Для окончания работы инструкций man и info используйте символ q.

3.4. Для протоколирования результатов выполнения команд используйте команду script, которая позволяет создать протокол работы пользователя на терминале. Рекомендуется вводить команду script перед выполнением окончательной версии команды и заканчивать запись командой exit или сочетанием клавиш Ctrl-D, чтобы не загромождать файл протокола отладочными вариантами с ошибками. Для того, чтобы результаты работы накапливались в файле протокола, используйте команду script с опцией -a.

3.5. Возможно открыть новый терминал в пределах той же сессии, используя клавиши AltF2 - F6.

3.6. При работе в файловом менеджере Midnight Commander для вызова команд можно применять сочетания клавиш Ctrl символ или Alt символ, обозначаемые в его описании как С - символ или М - символ соответственно. Если указано сочетание трех клавиш, то первые две нажимаются одновременно, третья - отдельно. Если символ после служебной клавиши расположен в верхнем регистре, то одновременно нажимаются все три клавиши, включая Shift.

3.7. Для доступа к файлам, созданным на сервере Linux, применяйте протокол ftp, клиентская программа которого имеется в Windows 2000 и встроена в файловый менеджер FAR. При этом учетная запись и пароль те же, что и при подключении по протоколу ssh.

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

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

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

4.3. Выполнить указанные в задании действия над каталогами, файлами, ссылками.

4.4. Изучить команды изменения прав доступа и владения chmod и chown.

4.5. Провести изменения прав доступа к файлам и каталогам.

4.6. Изучить работу и ключи команды ps - получения сведений о процессах в операционной системе.

4.7. Освоить работу с файловым менеджером Midnight Commander. Изучить сочетания клавиш для выполнения часто применяемых команд.

5. Варианты заданий.

5.1. Для всех вариантов выполнить следующие действия:

5.1.1. Запустить программу putty, ввести адрес сервера для подключения (Host name) 217.71.139.65, выбрать протокол подключения SSH (порт 22), в категории Window/Appearance изменить (Change) шрифт на Courier Bold 16-pixel, вернуться в категорию Session, ввести имя сохраняемых настроек Saved Session, сохранить настройки (Save), открыть соединение (Open).

5.1.2. Ввести имя пользователя user№, пароль user№, где № состоит из двух цифр - порядкового номера группы в микропотоке и номера бригады.

5.1.3. Выполните команды help, ls, cd, pwd, mkdir, rmdir.

5.1.4. Изучить описания этих команд с помощью инструкций man и info.

5.1.5. Изучить описания команды script, запротоколировать с ее помощью выполнение пункта 5.1.3.

5.1.6. Установить связь с соседями, используя команды who, write, запротоколировать переписку.

5.1.7. Создать текстовый файл с произвольным содержанием при помощи команды te e. Просмотреть содержимое файла при помощи команды cat и исправить в нем ошибки при помощи текстового редактора vi.

5.1.8. Изучить справку на команды cp, unlink.

5.1.9. Сделать копию файла командой cp, удалить ее командой unlink, запротоколировать эти действия.

5.2. Создайте структуру каталогов в соответствии с вариантом. Черными линиями представлена вложенность файлов/подкаталогов в каталоги. Синими линиями представлены ссылки. Красными линиями - символические ссылки. Стрелка на красной линии указывает на целевой файл ссылки. Файлы создаются копированием ранее созданного файла командой cp с внесением в копии некоторых изменений. Ссылки создаются командой ln, символические ссылки - ей же, но с ключом -s:

Таблица 1.

Вариант 1.

Вариант 6.

Вариант 2.

Вариант 7.

Вариант 3.

Вариант 8.

Вариант 4.

Вариант 9.

Вариант 5.

Вариант 10.

5.3. Для всех вариантов выполнить следующие действия:

5.3.1. Создать ссылки (синие линии).

5.3.2. Создать символические ссылки (красные линии).

5.3.3. Сохранить протокол выполненных действий.

5.3.4. Провести ряд экспериментов, иллюстрирующих доступ к файлам по основным именам, по ссылкам и по символическим ссылкам. Для доступа использовать команду cat или редактор vi.

5.3.5. Провести ряд экспериментов, иллюстрирующих реакцию системы на удаление файла, на который имеются ссылки, и файла, на который имеются символические ссылки. Проверять результаты командой ls -la.

5.3.6. Уничтожить созданные подкаталоги и файлы в них, используя команды rmdir и unlink, сохранив, однако, файл, созданный в пункте 5.1.7 и одну его рабочую копию в домашнем каталоге.

5.3.7. Изучить справку к командам chmod и chown.

5.3.8. Открыть для своей группы доступ к своему домашнему каталогу - для поиска в каталоге и к рабочей копии файла в домашнем каталоге - для чтения и записи.

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

5.3.10. Получив от своего партнера аналогичное сообщение, выполнить попытку чтения файла в каталоге партнера, а затем - внесения изменений в этот файл.

5.3.11. Послать своему партнеру сообщение о том, что в его файл внесены изменения.

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

5.3.13. Закрыть доступ к своему домашнему каталогу.

5.3.14. Сохранить протокол выполнения пунктов 5.3.8 - 5.3.13.

5.3.15. Изучить справку к команде ps, выполнить ее с ключами -a, -e, a, x, ax, записать результаты в файл, например: ps -e ps.log.

5.3.16. Изучить справку к файловому менеджеру Midnight Commander, запустить его, изучить перечень доступных команд, сочетания клавиш для выполнения часто применяемых команд, особенности встроенного текстового редактора.

6. Содержание отчета.

6.1. Цель работы.

6.2. Вариант задания.

6.3. Протоколы выполненных действий согласно пунктам 5.1.5, 5.1.6, 5.1.9, 5.3.3, 5.3.14, 5.3.15.

7. Контрольные вопросы.

7.1. Какие ключи команды ls Вы знаете? Что они дают?

7.2. Чем отличаются man и info? Как с ними работать?

7.3. Команда script - назначение и применение.

7.4. Как обмениваться сообщениями с пользователями сервера Linux? Как блокировать прием сообщений?

7.5. Команды tee и cat. Назначение и применение. Чем cat отличается от more и less?

7.6. Основные команды редактора vi.

7.7. Ссылки и символические ссылки. Создание и применение.

7.8. Создание и копирование файлов и папок в Linux.

7.9. Перемещение и удаление файлов и папок в Linux.

7.10. Команды chmod и chown. Назначение и применение.

7.11. Какие права доступа Вы имеете к своему домашнему каталогу, каталогам /home и / ?

7.12. Команда ps. Назначение и применение. Ключи команды.

7.13. Особенности вызова команд Midnight Commander через сочетания клавиш.

7.14. Особенности встроенного текстового редактора файлового менеджера Midnight Commander.

7.15. Какие сочетания клавиш Midnight Commander можно было применить для выполнения этой лабораторной работы?

ЛАБОРАТОРНАЯ РАБОТА №3

МНОГОЗАДАЧНОЕ ПРОГРАММИРОВАНИЕ В LINUX

1. Цель работы: Ознакомиться с компилятором gcc, методикой отладки программ, функциями работы с процессами.

2. Краткие теоретические сведения.

Минимальным набором ключей компилятора gcc являются -Wall (выводить все ошибки и предупреждения) и -o (output file):

gcc -Wall -o print_pid print_pid.c

Команда создаст исполняемый файл print_pid.

Стандартная библиотека C (libc, реализованная в Linux в glibc), использует возможности многозадачности Unix System V (далее SysV). В libc тип pid_t определен как целое, способное вместить в себе pid. Функция, которая сообщает pid текущего процесса, имеет прототип pid_t getpid (void) и определена вместе с pid_t в unistd.h и sys/types.h).

Для создания нового процесса используется функция fork:

pid_t fork(void)

Вставляя задержку случайной длины при помощи функций sleep и rand, можно нагляднее увидеть эффект многозадачности:

sleep(rand()%4)

это заставит программу заснуть на случайное число секунд: от 0 до 3.

Чтобы в качестве дочернего процесса вызвать функцию, достаточно вызвать ее после ветвления:

pid = fork();

if (pid == 0) {

// если выполняется дочерний процесс, то вызовем функцию

pid=process(arg);

// выход из процесса

exit(0);

Часто в качестве дочернего процесса необходимо запускать другую программу. Для этого применяется функции семейства exec:

pid = fork();

if (pid == 0) {

// если выполняется дочерний процесс, то вызов программы

if (execl(./file,file,arg, NULL)0) {

printf(ERROR while start process\n);

exit(-2);

}

else printf( process started (pid=%d)\n, pid);

// выход из процесса

}

Часто родительскому процессу необходимо обмениваться информацией с дочерними или хотя бы синхронизироваться с ними, чтобы выполнять операции в нужное время. Один из способов синхронизации процессов — функции wait и waitpid:

#include sys/types.h

#include sys/wait.h

pid_t wait(int *status) - приостанавливает выполнение текущего процесса до завершения какого-либо из его процессов-потомков.

pid_t waitpid (pid_t pid, int *status, int options) - приостанавливает выполнение текущего процесса до завершения заданного процесса или проверяет завершение заданного процесса.

Если необходимо узнать состояние порожденного процесса при его завершении и возвращенное им значение, то используют макрос WEXITSTATUS, передавая ему в качестве параметра статус дочернего процесса.

status=waitpid(pid,status,WNOHANG);

if (pid == status) {

printf(PID: %d, Result = %d\n, pid, WEXITSTATUS(status)); }

Для изменения приоритетов порожденных процессов используются функции setpriority и . Приоритеты задаются в диапазоне от -20 (высший) до 20 (низший), нормальное значение - 0. Заметим, что повысить приоритет выше нормального может только суперпользователь!

#include sys/time.h

#include sys/resource.h

int process( int i) {

setpriority(PRIO_PROCESS, getpid(),i);

printf(Process %d ThreadID: %d working with priority %d\n,i, getpid(),getpriority(PRIO_PROCESS,getpid()));

return(getpriority(PRIO_PROCESS,getpid()));

}

Для уничтожения процесса служит функция kill:

#include sys/types.h

#include signal.h

int kill(pid_t pid, int sig);

Если pid 0, то он задает PID процесса, которому посылается сигнал. Если pid = 0, то сигнал посылается всем процессам той группы, к которой принадлежит текущий процесс.

sig - тип сигнала. Некоторые типы сигналов в Linux:

SIGKILL Этот сигнал приводит к немедленному завершению процесса. Этот сигнал процесс не может игнорировать.

SIGTERM Этот сигнал является запросом на завершение процесса.

SIGCHLD Система посылает этот сигнал процессу при завершении одного из его дочерних процессов. Пример:

if (pid[i] == status) {

printf(ThreadID: %d finished with status %d\n, pid[i], WEXITSTATUS(status));

}

else kill(pid[i],SIGKILL);

3. Методические указания.

3.1. Для ознакомления с опциями компилятора gcc, описанием функций языка С используйте инструкции man и info.

3.2. Для отладки программ удобно использовать встроенный редактор файлового менеджера Midnight Commander (MC), выделяющий цветом различные языковые конструкции и указывающий в верхней строке экрана положение курсора в файле (строка, столбец).

3.3. В файловом менеджере Midnight Commander имеется буфер команд, вызываемый сочетанием клавиш ESС - H, перемещение по которому производится стрелками управления курсором (вверх и вниз). Для вставки команды из буфера в командную строку используется клавиша Enter, для редактирования команды из буфера - клавиши - и -, BackSpace и Delete.

3.4. Помните, что текущая директория не содержится в path, поэтому из командной строки необходимо запускать программу как ./print_pid. В MC достаточно навести курсор на файл и нажать Enter.

3.5. Для просмотра результата выполнения программы используйте сочетание клавиш Ctrl - O. Они работают и в режиме редактирования файла.

3.6. Для протоколирования результатов выполнения программ целесообразно использовать перенаправление вывода с консоли в файл: ./test result.txt

3.7. Для доступа к файлам, созданным на сервере Linux, применяйте протокол ftp, клиентская программа которого имеется в Windows 2000 и встроена в файловый менеджер FAR. При этом учетная запись и пароль те же, что и при подключении по протоколу ssh.

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

4.1. Ознакомиться с опциями компилятора gcc, методикой отладки программ.

4.2. Для вариантов заданий из лабораторной работы №1 написать и отладить программу, реализующую порожденный процесс.

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

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

5. Варианты заданий. См. варианты заданий из лабораторной работы №1

6. Содержание отчета.

6.1. Цель работы.

6.2. Вариант задания.

6.3. Листинги программ.

6.4. Протоколы выполнения программ.

7. Контрольные вопросы.

7.1. Особенности компиляции и запуска С-программ в Linux.

7.2. Что такое pid, как его определить в операционной системе и программе?

7.3. Функция fork - назначение, применение, возвращаемое значение.

7.4. Как запустить на выполнение в порожденном процессе функцию? Программу?

7.5. Способы синхронизации родительского и дочерних процессов.

7.6. Как узнать состояние порожденного процесса при его завершении и возвращенное им значение?

7.7. Как управлять приоритетами процессов?

7.8. Как уничтожить процесс в операционной системе и программе?

ЛАБОРАТОРНАЯ РАБОТА №4

ПОТОКИ И ИХ СИНХРОНИЗАЦИЯ В LINUX

1. Цель работы: Освоить функции создания потоков и синхронизации их при помощи взаимных исключений.

2. Краткие теоретические сведения.

Взаимное исключение (mutex) является простейшей формой синхронизации. Оно используется для защиты критической области (critical region), предотвращая одновременное выполнение участка кода несколькими потоками (если взаимное исключение используется потоками) или процессами (если взаимное исключение пользуется несколькими процессами). Выглядит это обычно следующим образом.

блокировать_mutex( );

критическая область

разблокировать_mutex(. );

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

Взаимные исключения по стандарту Posix объявлены как переменные с типом pthread_mutex_t. Если переменная-исключение выделяется статически, ее можно инициализировать константой PTHREAD_MUTEX_INITIALIZER:

static pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;

При динамическом выделении памяти под взаимное исключение (например, вызовом malloc) мы должны инициализировать эту переменную во время выполнения, вызвав функцию pthread_mutex_init.

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

#include pthread.h

int pthread_mutex_lock(pthread_mutex_t *mptr);

int pthread_mutex_trylock(pthread_mutex_t * mptr);

int pthread_mutex_unlock(pthread_mutex_t *mptr);

Все три возвращают 0 в случае успешного завершения положительное значение Еххх - в случае ошибки.

При попытке заблокировать взаимное исключение, которое уже заблокировано другим потоком, функция pthread_mutex_lock будет ожидать его разблокирования, a pthread_mutex_trylock (неблокируемая функция) вернет ошибку с кодом BUSY.

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

Рис. 4.1. Производители и потребитель

В одном процессе имеется несколько потоков-производителей и один поток-потребитель. Целочисленный массив buff содержит производимые и потребляемые данные (данные совместного пользования) Для простоты производители просто устанавливают значение buff[0] в 0, buff[1] в 1 и т. д. Потребитель перебирает элементы массива, проверяя правильность записей. Поток-потребитель не будет запущен, пока все производители не завершат свою работу. Ниже приведена функция main этого примера.

//mutex/prodcons.с

#define MAXNITEMS 1000000

#define MAXNTHREADS 100

int nitems;/*только для чтения потребителем и производителем*/

struct {

pthread_mutex_t mutex;

int buff[MAXNITEMS];

int nput;

int nval;

} shared = {

PTHREAD_MUTEX_INITIALIZER

};

void *produce(void *), *consume(void *);

int main(int argc, char **argv)

{

int i, nthreads count[MAXNTHREADS];

pthread_t tid_produce[MAXNTHREADS], tid_consume;

if (argc != 3)

err_quit(usage: prodcons3 #items #threads);

nitems = min(atoi(argv[1]), MAXNITEMS).

nthreads = min(atoi(argv[2]) MAXNTHREADS)

/* создание всех производителей и одного потребителя */

for (i=0; i nthreads; i++) {

count[i] = 0;

pthread_create(tid_produce[i] NULL, produce. count[i];

}

pthread_create(tid_consume, NULL, consume, NULL);

/* ожидание завершения производителей и потребителя */

for (i= 0; i nthreads; i++) {

pthread_join(tid_produce[i], NULL);

printf(count[%d] =%d\n, i, count[i]);

}

pthread_join(tid_consume, NULL);

exit(0);

}

Программа ожидает завершения работы всех потоков-производителей, выводя содержимое счетчика для каждого потока, и потока-потребителя. Ниже приведен текст функций produce и consume:

//mutex/prodcons.с

void *produce(void *arg)

{

for ( ; ; ) {

pthread_mutex_lock(shared.mutex);

if (shared.nput = nitems) {

pthread_mutex_unlock(shared.mutex):

return(NULL); /* массив полный, готово */

}

shared.buff[shared.nput] = shared.nval;

shared.nput++;

shared nva1++;

pthread_mutex unlock(shared mutex);

*((int *) arg += 1;

}

}

void consume_wait(int i)

{

for ( ;; ) {

pthread_mutex_lock(shared.mutex);

if (i shared.nput) {

pthread_mutex_unlock(shared.mutex);

return; /* элемент готов */

}

pthread_mutex_unlock(shared.mutex);

}

}

void *consume(void *arg)

{

int i;

for (i = 0; i nitems; i++) {

consume_wait(i);

if (shared.buff[i] != i)

printf(buff[%d]=%d\n i, shared.buff[i]);

}

return(NULL);

}

3. Методические указания.

3.1. Для уточнения списка заголовочных файлов, необходимых для вызова функций библиотеки pthread используйте инструкции man и info.

3.2. Хотя функции работы с потоками описаны в файле включения pthread.h, на самом деле они находятся в библиотеке. Поэтому процесс компиляции и сборки многопоточной программы выполняется в два этапа, например:

gcc -Wall -c -o test.o test.c

gcc -Wall -o test test.o pathlibgcc.a –lpthread

3.3. Библиотеку libgcc.a рекомендуется скопировать в текущий каталог.

3.4. Для поиска библиотеки средствами файлового менеджера Midnight Commander используйте сочетание клавиш Alt - Shift - ?

3.5. Для просмотра результата выполнения программы используйте сочетание клавиш Ctrl - O. Они работают и в режиме редактирования файла.

3.6. Для протоколирования результатов выполнения программ целесообразно использовать перенаправление вывода с консоли в файл: ./test result.txt

3.7. Для доступа к файлам, созданным на сервере Linux, применяйте протокол ftp, клиентская программа которого имеется в Windows 2000 и встроена в файловый менеджер FAR. При этом учетная запись и пароль те же, что и при подключении по протоколу ssh.

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

4.1. Для вариантов заданий из лабораторной работы №1 написать и отладить программу, реализующую родительский процесс, вызывающий и отслеживающий состояние порожденных потоков.

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

5. Варианты заданий. См. варианты заданий из лабораторной работы №1

6. Содержание отчета.

6.1. Цель работы.

6.2. Вариант задания.

6.3. Листинги программ.

6.4. Протоколы выполнения программ.

7. Контрольные вопросы.

7.1. Чем потоки отличаются от процессов?

7.2. Функции создания и завершения потоков.

7.3. Зачем и когда необходимо присоединять (join) потоки? Когда и как без этого можно обойтись?

7.4. Атрибуты потоков.

7.5. Что такое мьютекс?

7.6. Особенности инициализации взаимных исключений.

7.7. Чем отличаются функции pthread_mutex_trylock и pthread_mutex_unlock?

7.8. Когда применение мьютексов не гарантирует синхронизации потоков?
Литература

1. Гордеев А.В., Молчанов А.Ю. Системное программное обеспечение. - СПб.: Питер, 2002. – 736 с.

2. Кейлингерт П. Элементы операционных систем. Введение для пользователей /Пер. с англ. - М.: Мир, 1985. - 295 с.

3. Олифер Н.А., Олифер В.Г. Сетевые операционные системы. - СПб.: Питер, 2001.- 560 с.

4. Эпплман Д. Win32 API и Visual Basic. - СПб.: САМС, 2001. – 962 с.

5. Робачевский А.М. Операционная система UNIX. - СПб.: BHV, 1997. - 528 с.

6. Стахнов А.А. LINUX. - СПб.: BHV-Петербург, 2003. - 912 с.

7. Эндрюс Г. Р. Основы многопоточного, параллельного и распределенного программирования. - М.: Вильямс, 2003. - 512 с.

8. Хоар Ч. Взаимодействующие последовательные процессы. - М.: Мир, 1989. - 264 с.

9. Стивенс У. UNIX: взаимодействие процессов. - СПб.: Питер, 2002. - 576 с.


СИСТЕМНОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ

Методические указания к лабораторным работам

Редактор

Технический редактор

Лицензия № 021040 от 22.02.96. Подписано в печать ___.___.___.
Формат 60 х 84 1/16. Бумага оберточная. Тираж
150 экз.
Уч.-изд.л. 1,0. Печ.л. 1. Изд. № ______. Заказ № ______ Цена договорная

Отпечатано в типографии

Новосибирского государственного технического университета

630092, г. Новосибирск, пр. К. Маркса, 20

Скачать архив с текстом документа