проФорт

Форт и всё такое

Простой сканер портов - 3

,

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

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

Посмотрим как это работает на практике. Реализация самой модели в ~ygrek/lib/multi/msg.f (либа сырая, но для показательного примера вполне сгодится). Слова интерфейса :
  • ltreceive ( -- msg ) вызывается внутри потока для получения очередного сообщения из очереди (блокирует если сообщений нет).
  • msg.data ( msg -- a u ) извлекает текст сообщения (все данные передаваемые между потоками должны сериализоваться в строку, конечно я не могу запретить передавать указатели, но это сводит на нет все преимущества этого подхода - независимость потоков и унификацию средств общения).
  • msg.type ( msg -- type ) возвращает тип сообщения (на разные собщения можно реагировать по разному).
  • msg.sender ( msg -- lt ) возвращает идентификатор потока который послал это сообщение (для обратной связи).
  • ltsend ( a u type lt -- ) посылает сообщение указанному потоку и возвращает управление немедленно, не ожидая обработки получателем (сообщение ставится в очередь получателя).

В нашем сканере работают потоки трёх типов - port-supplier, scanner и registrator.



port-supplier заведует ресурсом непросканированных портов и выдаёт их по запросу тому кто присылает любое сообщение, вот его цикл работы :
 [COLOR=darkblue]:NONAME[/COLOR] [COLOR=darkblue]{[/COLOR] [COLOR=darkblue]|[/COLOR][COLOR=limegreen] m range_low range_hi[/COLOR][COLOR=darkblue] }[/COLOR]
[COLOR=chocolate] \ some initialization here[/COLOR]
 [COLOR=deeppink]BEGIN[/COLOR]
   ltreceive [COLOR=darkblue]->[/COLOR] m
   range_low range_hi [COLOR=darkblue]=[/COLOR] [COLOR=deeppink]IF[/COLOR] [COLOR=limegreen]0[/COLOR] [COLOR=deeppink]ELSE[/COLOR] range_low [COLOR=darkblue]DUP[/COLOR] [COLOR=darkblue]1+[/COLOR] [COLOR=darkblue]->[/COLOR] range_low [COLOR=deeppink]THEN[/COLOR]
  [COLOR=darkblue] " [/COLOR][COLOR=blue]{n}[/COLOR][COLOR=darkblue]"[/COLOR] [COLOR=limegreen]0[/COLOR] m msg.sender STRltsend
   m FREE-MSG
 [COLOR=deeppink]AGAIN[/COLOR][COLOR=darkblue] ;[/COLOR] [COLOR=darkblue]VALUE[/COLOR] [COLOR=darkblue]<port-supplier>[/COLOR]


Слово STRltsend ( s n lt -- ) это очевидная обёртка над ltsend.

Потребители этого ресурса - потоки-сканеры, в цикле запрашивают порт и сканируют его, докладывая о результате в поток-регистратор сообщением MSG_REGISTER. При завершении работы (когда supplier не даёт больше новых портов) регистратору посылается сообщение MSG_FINISHED чтобы он мог отследить момент когда все сканеры завершатся :

[COLOR=darkblue]:NONAME[/COLOR]
 [COLOR=chocolate] \ some initialization here[/COLOR]
  [COLOR=deeppink]BEGIN[/COLOR]
   [COLOR=darkblue]S" [/COLOR][COLOR=darkblue]"[/COLOR] [COLOR=limegreen]0[/COLOR] port-supplier ltsend
   ltreceive [COLOR=darkblue]->[/COLOR] m
   m msg.data NUMBER [COLOR=darkblue]0=[/COLOR] [COLOR=darkblue]ABORT" [/COLOR][COLOR=blue]Supplier is mad![/COLOR][COLOR=darkblue]"[/COLOR] [COLOR=darkblue]->[/COLOR] port
   port[COLOR=chocolate] \ пока есть порты для проверки [/COLOR]
  [COLOR=deeppink]WHILE[/COLOR]
   hostname STR@ port tryConnect [COLOR=deeppink]IF[/COLOR] port[COLOR=darkblue] " [/COLOR][COLOR=blue]{n}[/COLOR][COLOR=darkblue]"[/COLOR] MSG_REGISTER registrator STRltsend [COLOR=deeppink]THEN[/COLOR]
  [COLOR=deeppink]REPEAT[/COLOR]
  [COLOR=darkblue]S" [/COLOR][COLOR=darkblue]"[/COLOR] MSG_FINISHED registrator ltsend
[COLOR=darkblue];[/COLOR] [COLOR=darkblue]VALUE[/COLOR] [COLOR=darkblue]<scanner>[/COLOR]



Как только регистратор получает MSG_FINISHED от всех запущенных сканеров, он отчитывается о зарегистрированных портах и завершает работу программы.

Полный текст программы

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

Cама идея была скопирована из erlang'а, точнее из прочитанных описаний smile

Ссылки по теме :

Конечные автоматы и таблицы решений на SPFSummary 2007

Comments

acayc Tuesday, January 15, 2008 3:36:10 PM

Вместо ~ygrek/lib/multi/msg.f можно использовать ~pinka/lib/multi/ack_messages.f , тогда действительно "становятся невозможными дедлоки, т.к. нет самого понятия локов" (в игрековой либе локи есть). Или пул рабочих потоков, принимающих работу "сообщениями" - ~ac/lib/win/thread/pool.f

exs Wednesday, January 16, 2008 8:16:28 PM

Я эту либу Рувима обнаружил уже после того как. Но там ограничение на размер передаваемых данных - только поля структуры MSG. Можно использовать как транспортную среду для msg.f, очевидно побыстрее будет.
Насчёт "невоможности дедлоков" - тут дело не в том есть ли в явном виде мутексы в коде или нет, а в том что они скрыты за таким интерфейсом который не позволит задедлочиться ни в каком случае. Наверняка ведь и в реализации KERNEL32::GetMessage есть внутри мутексы.

How to use Quote function:

  1. Select some text
  2. Click on the Quote link

Write a comment

Comment
(BBcode and HTML is turned off for anonymous user comments.)

If you can't read the words, press the small reload icon.


Smilies