Воскресенье, 19.05.2024, 02:03
Приветствую Вас Guest Member

Windows XP / 7 .

[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 2
  • 1
  • 2
  • »
Архив - только для чтения
Форум <<Помощь по компьютерам>> » Низкоуровневое программирование » Исходные коды » Исходные коды: С++ (Исходные коды: С++)
Исходные коды: С++
MafiozikOДата: Воскресенье, 23.08.2009, 10:10 | Сообщение # 1
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Исходные коды: С++

Библиотеки
2kLib

Библиотека параллельного программирования на C++ в среде MS-DOS. Позволяет в рамках одной DOS-программы запускать параллельные процессы, работающие в режиме кооперативной мультизадачности. Взаимодействие параллельных процессов осуществляется с помощью программных каналов.


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:11 | Сообщение # 2
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
2klib: Библиотека параллельного программирования на Си++

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


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:11 | Сообщение # 3
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Программа 1

Начнем с примера простой программы, которая по уже установившейся традиции для языка Си, выводит на экран дисплея текст приветствия и завершается. При описании мы предполагаем, что читатель достаточно хорошо знаком с языком Си++ и основами объектно-ориентированного программирования, поэтому будем рассматривать только те особенности программы, которые связаны с параллельным программированием.

Code
#include <iostream.h>
#include "2k.h"

void ТСценарий::тело_процесса(void)
{
   cout << "Здравствуй, мир\n";
}

В отличие от обычных программ на Си, начинающих свое выполнение с главной функции main(), программы с использование библиотеки 2KLIB начинаются с выполнения главного процесса (создаваемого исполняющим ядром библиотеки). Главный процесс имеет тип ТСценарий. ТСценарий - это класс, порождаемый от базового абстрактного класса ТПроцесс. Для каждого порождаемого от ТПроцесс класса необходимо переопределить чистую виртуальную функцию тело_процесса(), которая собственно и содержит исполняемый код. В нашем случае в теле процесса выполняется вывод строки в стандартный файл вывода, после чего программа завершается. Определения библиотеки 2KLIB подключаются к программе через файл 2k.h. В дальнейших примерах мы будем, для краткости, опускать указание включаемых файлов.


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:11 | Сообщение # 4
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Программа 2

Code
class Т : public ТПроцесс
{
   public:
     void тело_процесса(void)
     {
       cout << "Параллельный процесс\n";
     }
};

void ТСценарий::тело_процесса(void)
{
   new Т;
   cout << "Сценарий\n";
}

В этой программе в теле процесса-сценария создается новый процесс, описываемый классом Т, после чего оба процесса существуют одновременно. Выполнение программы завершится после того, как завершатся оба процесса. В ходе выполнения в стандартный вывод будут направлены строки: "Сценарий" и "Параллельный процесс". Класс Т порождается от базового класса ТПроцесс, наследуя его доступные члены. В классе Т не вводится никаких новых свойств процесса, а только определяется его тело - это самый тривиальный случай определения нового типа процессов. Создается новый процесс оператором new. Это приводит к тому, что выделяется память под контекст процесса и процесс устанавливается в очередь процессов, готовых для выполнения. После завершения процесс удаляется из очереди процессов и затем вызывается его деструктор, освобождая занятую процессом память. В данном примере процесс создается явно и динамически - оператором new, а уничтожается неявно (диспетчером процессов) после завершения тела процесса. Родительский процесс не контролирует завершение дочерних процессов и, после их создания, выполняется независимо, поэтому родственных связей между процессами нет. При создании каждому процессу назначается собственная область стека, размер которой можно указать как аргумент конструктора ТПроцесс. По умолчанию размер стека равен 1 КБайт. Вторым (необязательным) параметром конструктора ТПроцесс является строка имени процесса - она используется только для диагностики ошибок.


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:12 | Сообщение # 5
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Программа 3
Code

class Т : public ТПроцесс
{
   private:
     int индекс;

   public:
     Т(int а_индекс)
     {
       индекс = а_индекс;
     }

     void тело_процесса(void)
     {
       cout << "Параллельный процесс " << индекс << '\n';
     }
};

void ТСценарий::тело_процесса(void)
{
   for (int i = 1; i <= 300; ++i)
   new Т(i);
}

В этой программе процесс-сценарий создает массив из трехсот параллельных процессов, причем каждый процесс получает при своем создании уникальный индекс. Каждый элемент массива процессов порождается от базового класса ТПроцесс и кроме обязательной функции тело_процесса() имеет конструктор, аргументом которого является индекс процесса в массиве.


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:12 | Сообщение # 6
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Программа 4

Code
class Т : public ТПроцесс
{
   public:
     void тело_процесса(void);
     ~Т()
     {
       cout << "Уничтожение процесса Т\n";
     }
};

void Т::тело_процесса(void)
{
   for (;;)
   {
     cout << "Параллельный процесс\n";
     пауза(100);
   }
}

void ТСценарий::тело_процесса(void)
{
   Т* процесс = new Т;
   пауза(1000);
   delete процесс;
}

В этой программе процесс-сценарий создает параллельный процесс Т, приостанавливается на 1 секунду и затем уничтожает процесс Т. Программа демонстрирует явное динамическое завершение процесса - такое действие используется при естественном завершении циклических процессов или в случае аварийной ситуации. Определение процесса Т имеет деструктор, вызываемый при завершении процесса.


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:12 | Сообщение # 7
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Деструктор процесса используется для выполнения завершающих операций, например, закрытия файла, что гарантирует целостность данных, изменяемых в теле процесса. В случае управления аппаратными устройствами, конструктор должен выполнять все инициализирующие действия (например, начальный сброс), а деструктор - все завершающие действия (например, выключение привода).

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

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

* опыт работы с различными алгоритмами диспетчеризации на раз личных задачах,
* эффективность и простота реализации наиболее сложной пробле мы: взаимного исключения, необходимого для выполнения неделимых (атомарных) операций, связанных с синхронизацией параллельных процессов,
* нереентерабельность MSDOS.


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:12 | Сообщение # 8
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Таким образом, активный процесс выполняется до тех пор, пока самостоятельно не перейдет к ожиданию некоторого события. В библиотеке определен ряд операций, вызывающих ожидание различных событий. Наиболее простая - пауза(). Эта перегруженная функция может вызываться с одним аргументом - временем ожидания или без аргументов - в этом случае процесс не удаляется из очереди активных процессов, а остается в ней, но управление передается следующему готовому процессу.

Учитывая принятый способ диспетчеризации параллельных процессов было решено также отказаться от понятия явного приоритета процесса. Это довольно рискованное решение, особенно для систем реального времени, но оно было принято из-за весьма тяжелой реализации явной системы приоритетов, которая, в частности, требует перехода от простых и эффективных очередей типа FIFO (первым пришел - первым обслуживается) к приоритетным очередям, причем как для процессов, так и для межпроцессных сообщений. Вместо явной системы приоритетов вводится понятие неявной системы приоритетов, причем приоритетность закладывается на этапе программирования. Идея состоит в том, чтобы рассматривать приоритет как относительную частоту активизации процесса - приоритет тем выше, чем большее время процесс находится в активном состоянии. Недостатком такого решения является трудность обеспечения быстрой реакции на особо приоритетные процессы, хотя эту трудность можно обойти для наиболее важного случая - активизации процесса по аппаратному прерыванию - в этом случае обработка разделяется на две фазы - быстрое предварительное реагирование в драйвере обслуживания прерывания и последующая дообработка в теле процесса, активизируемого сообщением. Продемонстрируем введение неявных приоритетов на примерах:


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:13 | Сообщение # 9
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Quote
// цикл, расположенный в теле высокоприоритетного процесса
for (i = 0; i < 1000; ++i)
{
некоторое_действие();
}

// цикл, расположенный в теле процесса со средним приоритетом
for (i = 0; i < 1000; ++i)
{
некоторое_действие();
пауза();
}

// цикл, расположенный в теле низкоприоритетного процесса
for (i = 0; i < 1000; ++i)
{
некоторое_действие();
пауза(приоритет);
}



Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:13 | Сообщение # 10
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
В первом примере процесс активен все время, пока выполняется цикл. Во втором примере процесс после каждой итерации отдает процессорное время другим параллельным процессам не уходя при этом из очереди готовых процессов. В третьем примере процесс после каждой итерации уходит в ожидание на время, зависящее от значения переменной 'приоритет'. Изменяя значение этой переменной процесс может изменять частоту активизации - это соответствует понятию динамического приоритета. Отметим, что если переменную 'приоритет' сделать доступной для других процессов, то приоритетом данного процесса можно управлять внешним образом, исходя из некоторого глобального критерия.

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


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:13 | Сообщение # 11
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Code
Т* процесс = new Т;
int идентификатор_Т = процесс->идентификатор();

if (существует(процесс, идентификатор_Т))
   действие_с_процессом...
else
   восстановительные_действия...


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:13 | Сообщение # 12
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Исполняющее ядро библиотеки всегда контролирует целостность процессов и при попытке доступа к несуществующему процессу аварийно завершает программу с выдачей диагностического сообщения. Естественно, что для систем реального времени такое поведение программы будет недопустимым. Практика надежного программирования динамических процессов состоит в том, что перед операциями доступа к процессу нужно проверять его существование и, в зависимости от существования процесса, выполнять то или иное действие, например, попытку восстановления.

Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:14 | Сообщение # 13
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Программа 5
Code

class ТЧасы : public ТПроцесс
{
   public:
     void тело_процесса(void);
};

void ТЧасы::тело_процесса(void)
{
   ТВремя* время;
   for (;;)
   {
     время = текущее_время();
     cout << время->час << ':' << время->минута << ':'
       << время->секунда << '\n';
     пауза(1000);
   }
}

static ТЧасы часы;

void ТСценарий::тело_процесса(void)
{
   пауза(10000);
}


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:14 | Сообщение # 14
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
В этом примере процесс-сценарий вообще не выполняет никаких полезных действий, а просто приостанавливается на 10 секунд, после чего завершается. Полезную работу выполняет параллельный процесс ТЧасы; это статический процесс, который создается не оператором new, а описанием: "static ТЧасы часы". Это означает, что конструктор такого процесса вызывается до начала программы, а деструктор - после завершения программы. Программа выполняется до тех пор, пока существует хотя бы один динамический параллельный процесс (в данном случае - сценарий). Аргумент функции пауза() фактически и определяет в нашем случае время выполнения программы. Поскольку деструктор статического процесса обязательно вызывается в конце программы, статический процесс не может быть явно уничтожен в ходе программы. Обычно статические процессы содержат в теле оператор бесконечного цикла for(;;).

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


Админ
 
MafiozikOДата: Воскресенье, 23.08.2009, 10:14 | Сообщение # 15
~Web master~
Группа: Gold пользователь
Зарегистрирован: 10.04.2009
Откуда: Kharkiv
Пол: Мужчина
Сообщений: 366
Статус: Вне сайта
Программа 6

Quote
class Т : public ТПроцесс
{
public:
ТКанал<int> канал;

void тело_процесса(void);
};

void Т::тело_процесса(void)
{
int i;
канал >> i;
cout << "Принято сообщение " << i << '\n';
}

void ТСценарий::тело_процесса(void)
{
Т* процесс = new Т;
int i = 123;
процесс->канал << i;
}



Админ
 
Форум <<Помощь по компьютерам>> » Низкоуровневое программирование » Исходные коды » Исходные коды: С++ (Исходные коды: С++)
  • Страница 1 из 2
  • 1
  • 2
  • »
Поиск: