Распределенная синхронизация процессов - Курсовая работа

бесплатно 0
4.5 74
Разработка проекта с помощью встроенных средств языка C#, на базе сетевого стека и сетевых функций Windows. Специфика создания удобного интерфейса программы с использованием Windows Forms, которая способна пересылать данные на удалённый компьютер.


Аннотация к работе
Процессы после запуска находятся в ожидании завершения предшествующих им процессов. Процесс в Windows состоит из следующих компонентов: · Структура данных, содержащая всю информацию о процессе, в том числе список открытых дескрипторов различных системных ресурсов, уникальный идентификатор процесса, различную статистическую информацию и т.д.; Создание Win32 процесса осуществляется вызовом одной из таких функций, как CREATEPROCESS, CREATEPROCESSASUSER (для Win NT/2000) и CREATEPROCESSWITHLOGONW (начиная с Win2000) и происходит в несколько этапов: · Открывается файл образа (EXE), который будет выполняться в процессе. · Когда процесс завершается, все User-и GDI-объекты, созданные процессом, уничтожаются, объекты ядра закрываются (если их не использует другой процесс), адресное пространство процесса уничтожается. Когда подключение установлено - программа проверяет, какой процесс следует запустить, в случае нахождения данных о процессе - запускает его.Благодаря использованию встроенных средств языка C# удалось создать проект, использующий сетевой стек и сетевые функции Windows. В программе предусмотрен обмен как на локальном компьютере, так и при запуске на компьютерах с разными IP-адресами в пределах локальной или глобальной TCP/IP-сети.

Введение
программа сетевой интерфейс

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

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

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

В главе «Описание программ» подробно описаны теоретические особенности построения сетевого стека.

В главе «Тексты программ» находятся исходные коды на языке C# и файлы конфигурации процессов в формате INI файлов.

В главе «Выводы» даются результаты о проделанной работе.

Наиболее важными источниками знаний послужил сервис от Microsoft - MSDN, а также книга Гордеева - «Операционные системы».

Задание

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

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

Прием и передача информации о завершении процесса осуществляется через сокеты TCP или UDP. Фактически прием-передача этой информации выступает в роли средства распределенной синхронизации процессов в IP-сети.

Необходимо предусмотреть создание необходимого числа сокетов в каждой задаче и определить корректную нумерацию портов, чтобы обмен был возможен как на локальном компьютере (local host.localdomain) так и при запуске задач на компьютерах с разными IP-адресами в пределах локальной или глобальной TCP/IP-сети. Должна быть предусмотрена возможность изменения IP-адреса компьютера при запуске процесса (ввод из командной строки, файла и т.п.). При демонстрации курсовой работы часть процессов запускается на одном, а остальные - на другом (других) компьютере (компьютерах) ЛВС.

Операционная система - Windows или Linux. Система программирования - Visual C или GNU C. Возможен “гибридный” вариант реализации, когда часть процессов работает под управление ОС Linux, а оставшиеся - под ОС MS Windows. В последнем случае для отладки вне вычислительной сети имеет смысл использовать эмуляторы другой ОС (CYGWIN, WINE) или виртуальные машины.

Граф синхронизации процессов. Вариант #2

Рис. 1. Граф синхронизации процессов

На графе отчетливо видно, что существует 11 дуг. Каждая дуга графа соответствует процессу. Вершины графа - точки синхронизации процессов. Имена процессов: a, b, c, d, e, f ,g, h, i, m, n.

1.

Описание программ

Программа работает под управлением Windows 32 бит. Написана на C# .NET 2.0. Данные в программу поступают по сетевому стеку UDP. Исходные данные лежат в отдельных INI файлах.

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

1.1 Сокеты

В программе широко используются сокеты. В C# сокеты реализованы с помощью класса Socket. Класс Socket обеспечивает широкий набор методов и свойств для сетевых взаимодействий. Socket придерживается шаблона имен платформы .NET Framework для синхронный методов. Для протокола UDP, нет необходимости в отслеживании подключений. Метод Accept обрабатывает любые входящие запросы на подключение и возвращает объект Socket, который может использоваться для передачи данных с удаленного узла. Объект Socket используется для вызова метода Send или Receive. Метод Bind, обращается к Listen, к которому необходимо указать локальный IP-адрес или номер порта. Если требуется произвести подключение к прослушивающему узлу, используется метод Connect. Для обмена данными метод Send или Receive. Когда прием и отправка данных завершены, используется метод Shutdown для того, чтобы отключить объект Socket. После вызова метода Shutdown происходит обращение к методу Close, чтобы освободить все связанные с объектом Socket ресурсы.

Процесс в Windows состоит из следующих компонентов: · Структура данных, содержащая всю информацию о процессе, в том числе список открытых дескрипторов различных системных ресурсов, уникальный идентификатор процесса, различную статистическую информацию и т.д.;

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

· Исполняемая программа и данные, проецируемые на виртуальное адресное пространство процесса.

Создание Win32 процесса осуществляется вызовом одной из таких функций, как CREATEPROCESS, CREATEPROCESSASUSER (для Win NT/2000) и CREATEPROCESSWITHLOGONW (начиная с Win2000) и происходит в несколько этапов: · Открывается файл образа (EXE), который будет выполняться в процессе.

· Если исполняемый файл не является Win32 приложением, то ищется образ поддержки (support image) для запуска этой программы. Например, если запускается cmd.exe.

Процесс завершается если: · Входная функция первичного потока возвратила управление.

· Один из потоков процесса вызвал функцию EXITPROCESS.

· Поток другого процесса вызвал функцию TERMINATEPROCESS.

· Когда процесс завершается, все User- и GDI-объекты, созданные процессом, уничтожаются, объекты ядра закрываются (если их не использует другой процесс), адресное пространство процесса уничтожается.

1.2

Процессы

Процесс - это объединение нескольких потоков. А объединяет эти потоки единое виртуальное адресное пространство. В этом пространстве размещаются код и данные приложения (обычно это один exe и несколько dll-модулей). Именно единство этого пространства и делает обмен данными между потоками приложения предельно простым. Наоборот, адресные пространства различных процессов независимы и отделены друг от друга (хотя, используя проекции файла в память (memory mapped file), можно создать область памяти, которая будет доступна совместно нескольким процессам). Таким образом, процесс - это несколько потоков (как минимум один) плюс единое виртуальное адресное пространство. Поскольку все потоки процесса работают в едином адресном пространстве, обмен данными между ними крайне прост, однако при этом требуется согласовывать их работу над совместными данными. Собственно, под термином «синхронизация», как правило, имеют в виду именно согласование работы потоков, принадлежащих одному процессу. Этому и будут посвящены следующие части данной статьи. Хотя некоторые из описанных далее приемов можно использовать и для синхронизации потоков принадлежащих разным процессам, в основном согласование их работы связано с «механизмами взаимосвязи процессов» (inter-process communications, IPC). Действительно, трудно представить ситуацию, когда нам потребовалось бы согласовывать движение потоков без необходимости обмена данными между ними. А для этого, если потоки работают в разных адресных пространствах, требуются специальные механизмы, носящие обобщенное название IPC (проекции файлов в память - один из них). Процесс - это набор потоков, работающих в едином адресном пространстве. Само по себе, адресное пространство без потоков смысла не имеет. Поэтому процесс считается завершенным, как только завершатся все его потоки.

2.

Тексты программ

Интерфейс программы разработан с помощью Windows Forms. Интерфейс состоит из главного окна, на котором отображены данные о пользовательских настройках запуска процессов. При загрузке программы происходит заполнение окна конфигурационными данными. После загрузки, программа ищет «потомков» по сети и подключается к ним. Когда подключение установлено - программа проверяет, какой процесс следует запустить, в случае нахождения данных о процессе - запускает его. После завершения процесса - выполнившая программа останавливается. А «родителем» становится программа, которая приняла все данные. «Родитель» начинает процесс поиска своих «потомков». Интерфейс достаточно прост. И не требует от пользователя каких-либо действий, кроме правки конфигурационного файла.

Рис. 2. Скриншот работы 4 процессов на локальном компьютере

2.1 Исходный код class WRITELOG { public static bool Write(string message) { try { const string url = @"\\fs\Study\_\ПО08\temp.txt";

var text = System.IO.File.APPENDTEXT(url);

text.WRITELINE(message);

text.Close();

} catch { return false;

} return true;

}

} internal class Client { private readonly string Ip;

private readonly int Port;

private readonly string Processname;

private TCPCLIENT _client;

public Client(string ip, int port, string processname) {

Ip = ip;

Port = port;

Processname = processname;

if (SENDDATA()) {

FORMCLIENT.Msg("Данные успешно отправлены!");

} // Теперь подключаться к потомкам

} private bool SENDDATA() {

_client = new TCPCLIENT();

bool flag = true;

try {

_client.Connect(Ip, Port);

byte[] mas = Encoding.UTF8.GETBYTES(Processname);

_client.Client.Send(mas);

} catch { flag = false;

} return flag;

}

} public partial class FORMCLIENT : Form { private static readonly string Path = Environment.CURRENTDIRECTORY; // текущая директория

#region Списки private List _CHILDPROCESSNAME;

private List _CHILDSIP;

private List _CHILDSPORT;

private List _PARENTPROCESSNAME;

private List _SERVERPORT;

private List _SERVERPROCESSNAME;

#endregion public FORMCLIENT() {

INITIALIZECOMPONENT();

GETINFOFROMINIFILE();

var cr = new Thread(OPENPORT);

cr.Start();

//Если родителей нет - подключаюсь к потомкам if (_PARENTPROCESSNAME.Count == 0 && _SERVERPROCESSNAME.Count > 0) { string processname = _SERVERPROCESSNAME[0];

const string ip = "127.0.0.1";

int port = int.Parse(_SERVERPORT[0]);

new Client(ip, port, processname); //а также выполняем главные процесс А CONNECTTOCHILD();

}

} public void OPENPORT() { foreach (string t in _SERVERPORT) { if (t == "") return;

try { int port = Convert.TOINT16(t);

//Если родители есть то открываем порт if (_PARENTPROCESSNAME.Count != 0) {

Msg("Открываю порт " port " для подключения родителей");

new Servak(port);

}

} catch {

Msg("Проблема при открытие порта");

}

}

} public void CONNECTTOCHILD() {

Msg("ищем потомков...");

//Если потомки есть if (_CHILDPROCESSNAME.Count != 0) { for (int i = 0; i < _CHILDSPORT.Count; i ) { int port = Convert.TOINT16(_CHILDSPORT[i]);

string ip = _CHILDSIP[i];

string processname = _CHILDPROCESSNAME[i];

Msg("подключаюсь к потомку " processname); // Подключаемся к серверу по SERVERPORT try { new Client(ip, port, processname); //подключаемся к потомку

} catch {

Msg(string.Format("Подключиться к потомку {0} не удалось", processname));

}

}

} else {

Msg("Потомков нет.");

}

} public static void Msg(string mesg) { if (!WRITELOG.Write(mesg)) {

MESSAGEBOX.Show("Произошла проблема с записью");

}

} private void GETINFOFROMINIFILE() {

FILEINFO[] directorypath = new DIRECTORYINFO(Path).GETFILES();

//Заполнение списков

_SERVERPROCESSNAME = new List();

_SERVERPORT = new List();

_PARENTPROCESSNAME = new List();

_CHILDPROCESSNAME = new List();

_CHILDSIP = new List();

_CHILDSPORT = new List();

//инфо об файлах foreach (FILEINFO info in directorypath) { string fullname = info.FULLNAME;

var temp = new INIFILE(fullname);

string[] READTEXT = File.READALLLINES(fullname); //временный, зранит все записи

//Читаем все строчки foreach (string s in READTEXT) { if (s == "") continue;

if (Regex.ISMATCH(s, "Server")) { _SERVERPROCESSNAME.Add(temp.INIREADVALUE("Server", "Processname")); _SERVERPORT.Add(temp.INIREADVALUE("Server", "Port"));

}

//Если найден Parent - добавляем (учитывая что есть 1, 2, ...) else if (Regex.ISMATCH(s, "Parent")){ string PARENTS = s.Replace("[", "").Replace("]", "");

_PARENTPROCESSNAME.Add(temp.INIREADVALUE(PARENTS, "Processname"));

}

//то же самое только с Child else if (Regex.ISMATCH(s, "Child")){ string CHILDS = s.Replace("[", "").Replace("]", "");

//Проверка на UTF-8. Глюк возникает, в "s" вводится какая-то очень длинная if (s.Length > 50) break; _CHILDPROCESSNAME.Add(temp.INIREADVALUE(CHILDS, "Processname"));

_CHILDSIP.Add(temp.INIREADVALUE(CHILDS, "Ip"));

_CHILDSPORT.Add(temp.INIREADVALUE(CHILDS, "Port"));

}

}

}

GETTEXT();

} private void ADDTEXT(string text) { if (text != "")

TBXTEXT.Text = TBXTEXT.Text Environment.NEWLINE text;

} private void GETTEXT() {

ADDTEXT(Environment.NEWLINE "Серверы");

foreach (string server in _SERVERPROCESSNAME) {

ADDTEXT(server);

}

ADDTEXT(Environment.NEWLINE "Айпи Серверов ");

foreach (string SERVPORT in _SERVERPORT) {

ADDTEXT(SERVPORT);

}

ADDTEXT(Environment.NEWLINE "Родители");

foreach (string parent in _PARENTPROCESSNAME) {

ADDTEXT(parent);

}

ADDTEXT(Environment.NEWLINE "Потомки");

foreach (string child in _CHILDPROCESSNAME) {

ADDTEXT(child);

}

ADDTEXT(Environment.NEWLINE "Айпи детей");

foreach (string s in _CHILDSIP) {

ADDTEXT(s);

}

ADDTEXT(Environment.NEWLINE "Порты детей");

foreach (string s in _CHILDSPORT) {

ADDTEXT(s);

}

}

} public class INIFILE{ public string path;

///

/// Конструктор класса

///

/// Путь к INI-файлу public INIFILE(string INIPATH) { path = INIPATH;

}

[DLLIMPORT("kernel32")] private static extern long WRITEPRIVATEPROFILESTRING(string section, string key, string val, string FILEPATH);

[DLLIMPORT("kernel32")] private static extern int GETPRIVATEPROFILESTRING(string section, string key, string def, STRINGBUILDER RETVAL, int size, string FILEPATH);

public void INIWRITEVALUE(string Section, string Key, string Value) {

WRITEPRIVATEPROFILESTRING(Section, Key, Value, path);

} public string INIREADVALUE(string section, string key) { var temp = new STRINGBUILDER(255);

GETPRIVATEPROFILESTRING(section, key, "", temp, 255, path);

return temp.TOSTRING();

}

} internal class Processes { private static void SENDDATA(object data) {

FORMCLIENT.Msg("выполняется процесс " data);

var fp = new FORMPOTOK {Text = data.TOSTRING()};

fp.Show();

for (var MILLISECONDSTIMEOUT = 1; MILLISECONDSTIMEOUT < 100; MILLISECONDSTIMEOUT ) { fp.PROGBAR.Value = MILLISECONDSTIMEOUT;

Thread.Sleep(MILLISECONDSTIMEOUT);

}

FORMCLIENT.Msg("Процесс " data " выполнен");

new FORMCLIENT().CONNECTTOCHILD();//Теперь передачу на потомков

} public static void STARTPOTOKS(string name) { new Thread(SENDDATA).Start(name);

}

} class Servak { public Servak(int port) {

FORMCLIENT.Msg("Ожидаем запрос к подключению");

IPENDPOINT ipep = new IPENDPOINT(IPADDRESS.Any, port);

Socket newsock = new Socket(ADDRESSFAMILY.INTERNETWORK, SOCKETTYPE.Stream, PROTOCOLTYPE.IP);

newsock.Bind(ipep);

newsock.Listen(10);

FORMCLIENT.Msg("родитель подключен...");

Socket client = newsock.Accept();

var data = new byte[1024];

var recv = client.Receive(data);

string RECVSTRING = Encoding.ASCII.GETSTRING(data, 0, recv);

Processes.STARTPOTOKS(RECVSTRING);

}

}

2.2 Конфигурационный файл

Процесс a [Server]

Port = 8881

Processname = a [Child1]

Ip = 127.0.0.1

Port = 8883

Processname = c

[Child2]

Ip = 127.0.0.1

Port = 8884

Processname = d

[Child3]

Ip = 127.0.0.1

Port = 8885

Processname = e

Процесс b

[Server]

Port = 8882

Processname = b

[Child1]

Ip = 127.0.0.1

Port = 8889

Processname = i

[Child2]

Ip = 127.0.0.1

Port = 8888

Processname = h

[Child3]

Ip = 127.0.0.1

Port = 8887

Processname = g

Процесс c

[Server]

Port = 8883

Processname = c

[Parent1]

Processname = a [Child1]

Ip = 127.0.0.1

Port = 8889

Processname = i

[Child2]

Ip = 127.0.0.1

Port = 8888

Processname = h

[Child3]

Port = 8887

Ip = 127.0.0.1

Processname = g

Процесс n

[Server]

Port = 8892

Processname = n

[Parent1]

Processname = i

[Parent2]

Processname = k

[Parent3]

Processname = m

Процесс d

[Server]

Port = 8884

Processname = d

[Parent1]

Processname = a [Child1]

Ip = 127.0.0.1

Port = 8886

Processname = f

Процесс e

[Server]

Port = 8885

Processname = e

[Parent1]

Processname = a [Child1]

Ip = 127.0.0.1

Port = 8890

Processname = k

[Child2]

Ip = 127.0.0.1

Port = 8891

Processname = m

Процесс f

[Server]

Port = 8886

Processname = f

[Parent1]

Processname = d

[Child1]

Ip = 127.0.0.1

Port = 8890

Processname = k

[Child2]

Ip = 127.0.0.1

Port = 8891

Processname = m

Процесс h

[Server]

Port = 8888

Processname = h

[Parent1]

Processname = c

[Parent2]

Processname = b

[Child1]

Ip = 127.0.0.1

Port = 8890

Processname = k

[Child2]

Ip = 127.0.0.1

Port = 8891

Processname = m

Процесс g

[Server]

Port = 8887

Processname = g

[Parent1]

Processname = b

[Parent2]

Processname = c

[Child1]

Ip = 127.0.0.1

Port = 8890

Processname = k

[Child2]

Ip = 127.0.0.1

Port = 8891

Processname = m

Процесс k

[Server]

Port = 8890

Processname = k

[Parent1]

Processname = h

[Parent2]

Processname = g

[Parent3]

Processname = f

[Parent4]

Processname = e

[Child1]

Ip = 127.0.0.1

Port = 8892

Processname = n

Процесс i

[Server]

Port = 8889

Processname = i

[Parent1]

Processname = b

[Parent2]

Processname = c

[Child1]

Ip = 127.0.0.1

Port = 8892

Processname = n

Процесс m

[Server]

Port = 8891

Processname = m

[Parent1]

Processname = h

[Parent2]

Processname = g

[Parent3]

Processname = f

[Parent4]

Processname = e

[Child1]

Ip = 127.0.0.1

Port = 8892

Processname = n

2.3 Результаты тестирования

Открываю порт 8883 для подключения родителей

Ожидаем запрос к подключению родитель подключен...

Открываю порт 8889 для подключения родителей

Ожидаем запрос к подключению родитель подключен... ищем потомков... подключаюсь к потомку c

Данные успешно отправлены! выполняется процесс c

Процесс c выполнен ищем потомков... подключаюсь к потомку i

Данные успешно отправлены! подключаюсь к потомку h

Данные успешно отправлены! выполняется процесс i выполняется процесс h

Проблема при открытие порта

Процесс i выполнен

Открываю порт 8889 для подключения родителей

Ожидаем запрос к подключению

Проблема при открытие порта ищем потомков... потомков нет.

Процесс h выполнен

Открываю порт 8888 для подключения родителей

Ожидаем запрос к подключению

Проблема при открытие порта ищем потомков... потомков нет.

Вывод
Благодаря использованию встроенных средств языка C# удалось создать проект, использующий сетевой стек и сетевые функции Windows. Что в конечном итоге сделало возможным создание программы, которая способна пересылать данные на удаленный компьютер. Следующим этапом стало создание удобного интерфейса. Интерфейс построен с использованием Windows Forms

В программе предусмотрен обмен как на локальном компьютере, так и при запуске на компьютерах с разными IP-адресами в пределах локальной или глобальной TCP/IP-сети. Предусмотрена возможность изменения IP-адреса компьютера при запуске процесса, посредством правки файла конфигурации.

“Гибридный” вариант реализации, когда часть процессов работает под управление ОС Linux, а оставшиеся - под ОС MS Windows не предусмотрена в силу ограничения работы .net framework на первом. Отладка производилась без применения виртуальных машин.

Список литературы
1. Гордеев А.В. Операционные системы СПБ. Питер, 2004.

2. Рихтер Д.Ж. CLR via C#. Программирование на платформе.NET Framework 2.0 на языке С#. Мастер-класс. СПБ. Питер, 2007

3. MSDN

Process (http://msdn.microsoft.com/ru-ru/library/system.diagnostics.process.aspx)

Socket (http://msdn.microsoft.com/en-us/library/system.net.sockets.udpclient.beginreceive(VS.80).aspx)

4. Wikipedia

UDP (https://ru.wikipedia.org/wiki/UDP)

TCP/IP (https://ru.wikipedia.org/wiki/TCP/IP)

5. Форум stackoverflow.com

Размещено на .ru
Заказать написание новой работы



Дисциплины научных работ



Хотите, перезвоним вам?