Byê moûe obiîo Ci sië juû to pojëcie o uszy. Byê moûe nawet wiesz, czym sâ porty i jak sië miëdzy nimi porozumiewaê - w takim razie nie musisz czytaê tego artykuîu. Jednakûe jeôli port kojarzy Ci sië wyîâcznie z zacumowanymi do nadgniîych sîupów i obijajâcymi sië o burty kutrami, gdzie powietrze wypeînione jest sîonym zapachem morza i bardziej juû przykrym odorem nadgniîych ryb, to gorâco polecam Ci poniûszâ lekturë, jako ûe idea portów jest po prostu genialna, a dodatkowo - jak to zwykle w takich przypadkach bywa - wzruszajâca wrëcz w swojej prostocie.
Port - ten zwykîy, gdzie moûna znaleúê snujâcych sië marynarzy i gotowe na wszystko kobiety - istnieje po to, ûeby mogîy do niego zawijaê statki oraz podobne z niego odchodziê. Identycznie rzecz sië ma wzglëdem portów z Execa. Jedynâ róûnicâ jest brak zepsutego powietrza i sîonej wody, zaô statki sâ zastâpione wpîywajâcymi (lub wychodzâcymi) wiadomoôciami. Port - teraz juû ten execowy - to po prostu kawaîek pamiëci, a precyzujâc pewna struktura. Jeôli teraz znamy adres poczâtku tej struktury w pamiëci, czyli innymi sîowy adres tego portu, moûemy wysîaê do niego wiadomoôê (dalej Message). Moûemy teû zatrzymaê nasz program i poczekaê, aû do naszego portu wpîynie jakiô komunikat (czyli Message). Kiedy taki Message przybëdzie, naleûy go odebraê, odczytaê, odpowiednio zinterpretowaê, wykonaê ewentualnie jakieô dziaîanie i odpowiedzieê (czësto koniecznie!) nadawcy, ûe juû uporaliômy sië z komunikatem. Na ogóî mechanizm czekania na wiadomoôê, jej odbierania, przetwarzania itd. jest taki sam. Tak jak wyûej wspomniaîem - najpierw korzystamy z procedury, która zatrzymuje nasz program i czeka na wiadomoôê. Kiedy komunikat dotrze do portu, procedura zwróci adres pierwszego dostëpnego komunikatu (patrz poniûej opis struktury Message). Wtedy my odczytujemy wiadomoôê i na podstawie zawartych w niej danych wykonujemy odpowiednie dziaîanie. Kiedy skoïczymy, odpowiadamy nadawcy, który wysîaî nam wiadomoôê (w strukturze Message na ogóî znajduje sië adres portu nadajâcego komunikat). Czasami moûna zmieniê kolejnoôê tych dziaîaï, tzn. najpierw odpowiedzieê nadawcy, a dopiero potem zabraê sië za odczytywanie wiadomoôci. W tym przypadku jednak musimy byê caîkowicie pewni, ûe taka zamiana jest dozwolona (wyjaônienie póúniej; nie zawsze moûna tej zamiany dokonaê). Natomisat jeûeli zamierzamy wysîaê komunikat, musimy znaê adres portu, do którego Message ma dotrzeê; powinniômy takûe umieôciê w naszej wiadomoôci adres naszego portu (choê to nie zawsze jest konieczne), ûeby odbiorca wiedziaî, komu odpowiedzieê. Na dobrâ sprawë, to mniej wiëcej wîaônie tak wyglâdajâ porty i komunikacja miëdzy nimi - oczekiwanie na wiadomoôê oraz ich wysyîanie.
Tamten akapit byî bardzo ogólnym wprowadzeniem. W tym znajdziesz wiëcej szczegóîów. Do obsîugi portów oraz systemu komunikacji przewidzianych jest zaledwie kilka procedur (plus dwie nowe w systemach +2.0) z biblioteki Exec. Sâ to:
_LVOWait Equ -318 _LVOAllocSignal Equ -330 _LVOFreeSignal Equ -336 _LVOAddPort Equ -354 _LVORemPort Equ -360 _LVOPutMsg Equ -366 _LVOGetMsg Equ -372 _LVOReplyMsg Equ -378 _LVOWaitPort Equ -384 _LVOFindPort Equ -390 _LVOCreateMsgPort Equ -666 ; V36 _LVODeleteMsgPort Equ -672 ; V36
Z pewnych powodów trzy pierwsze funkcje opiszë na samym koïcu. Zanim jednak zacznë opisywaê dwie nastëpne procedury, muszë wspomnieê o dwóch rodzajach portów, a mianowicie o portach publicznych (public ports) i prywatnych. Port publiczny to taki, którego adres zostaje umieszczony na systemowej liôcie (nawiasem mówiâc dwukierunkowej, czyli tzw. double-linked) portów publicznych. Mogâ mieê do niego zatem dostëp takûe inne niû nasz programy, czyli mogâ do niego wysyîaê komuniakty i na dobrâ sprawë czekaê, aû do tego portu jakiô komunkiat wpîynie (czego nie wolno robiê); czyniâc port publicznym, trzeba mieê naprawdë waûny do tego powód, gdyû w takim przypadku naleûy byê przygotowanym na praktycznie wszystko. No i - jak juû sië domyôlasz - port prywatny to taki, którego adres jest znany tylko nam. Skoro zaô o tym wspomniaîem przed opisaniem tych dwóch procedur, îatwo jest sië domyôleê, ûe sâ one wîaônie z typami tych portów zwiâzane. Struktura portu publicznego absolutnie niczym sië nie róûni od struktury portu prywatnego - w kaûdej chwili port prywatny moûesz wpisaê na listë portów publicznych - do tego wîaônie sîuûy procedura_LVOAddPort(). _LVORemPort() uûywasz natomiast wtedy, gdy chcesz jakiô port z tej listy usunâê. Dziëki _LVOPutMsg() moûesz wysîaê wiadomoôê do jakiegoô portu, zaô przy pomocy_LVOGetMsg() odebraê jâ, gdy do naszego portu przybëdzie. _LVOWaitPort() zatrzymuje dziaîanie naszego programu do momentu, kiedy do portu podanego jako argument dla powyûszej procedury wpîynie wiadomoôê._LVOFindPort() znajdzie nam adres portu o podanej nazwie z listy portów publicznych (oczywiôcie, jeôli taki istnieje). Dwie ostatnie procedury - obecne w ROM-ie od systemów 2.0 w górë zakîadajâ (_LVOCreateMsgPort()) nowy port i kasujâ go (_LVODeleteMsgPort()) z pamiëci, czyli alokujâ (rezerwujâ) pamiëê potrzebnâ na strukturë naszego portu i wypeîniajâ pewnymi danymi, bez których port nie bëdzie dziaîaî oraz tâ pamiëê dealokujâ (zwalniajâ). Poniewaû - jak juû wspomniaîem - procedury te dostëpne sâ jedynie od systemów +2.0, na niûszych systemach musimy sië sami zatroszczyê o prawidîowe zbudowanie struktury portu. Jak taka struktura wyglâda? Ano tak:
NT_MSGPORT Equ 4
MsgPort: dc.l 0 ; mp_MsgNode....ln_Succ
dc.l 0 ; ln_Pred
dc.b NT_MSGPORT ; ln_Type
dc.b 0 ; ln_Pri
dc.l PortName ; ..............ln_Name
dc.b 0 ; mp_Flags
dc.b 0 ; mp_SigBit
dc.l 0 ; mp_SigTask/mp_SoftInt
dc.l 0 ; mp_MsgList....lh_Head
dc.l 0 ; lh_Tail
dc.l 0 ; lh_TailPred
dc.b 0 ; lh_Type
dc.b 0 ; ..............lh_pad
PortName: dc.b "Amsterdam",0
Jak widaê nasz port skîada sië z trzech pomniejszych struktur: ze struktry Node, z pól charakterystycznych dla naszego portu i ze struktury List. Struktura Node (piëê pierwszych pól) ma swoje zasîuûone miejsce w systemie Amigi. Jest ona doîâczana do wielu innych struktur, co wskazuje na jej uniwersalnoôê. I rzeczywiôcie. Dwa pierwsze pola moûemy spokojnie wyzerowaê. Sâ one uûywane przez system, jeôli dopisaê nasz MsgPort do listy portów publicznych (w pierwszym bedzie adres nastëpnego portu - lub zero jeôli nasz port jest ostatni na liôcie, w drugim poprzedniego - lub zero jeôi nasz port jest pierwszy na liôcie; nie powinno sië tych pól ani modyfikowaê, ani odczytywaê - sâ one do uûytku przez system). Trzecie pole - ln_Type - mówi nam o tym, do czego struktura Node jest doczepiona. Poniewaû jest to port, ustawiamy wartoôê tego pola na NT_MSGPORT (czyli 4). Nastëpny bajt - ln_Pri - to priorytet naszego portu. Jeôli nasz program (port) bëdzie czësto wyszukiwany z listy systemowej, warto ustawiê tâ wartoôê na wiëkszâ od zera - ulokuje to nasz port bliûej poczâtku listy (lub moûe nawet na samym jej poczâtku), przez co zostanie szybciej znaleziony. Jeôli mamy ôwiadomoôê, ûe port bëdzie odszukany raz na tysiâc lat, moûna ustawiê priorytet na mniejszy od zera - wtedy port zostanie dodany bliûej koïca listy. Najczëôciej jednak ustawia sië to pole na zero. Ostatnie pole struktury Node to wskaúnik na nazwë naszego portu. Jest ona dowolna i musi byê zakoïczona zerem. Wszystkie te pola mogâ byê wyzerowane, jeôli nie bëdziemy dodawaê naszego portu do listy (uwaga: mogâ byê one wyzerowane nawet jeôli dodamy MsgPort do listy, ale w przypadku, gdy ln_Name bëdzie równe zero, nie licz na to, ûe znajdziesz adres tego portu uûywajâc _LVOFindPort()...). A co jeôli port o nazwie jakâ nadaliômy naszemu portowi juû istnieje? Tu jest maîy problem. Procedura _LVOAddPort() nie zwraca nam ûadnej informacji o tym, czy port, który chcemy dodaê nie nazywa sië przypadkiem tak samo, jak jeden z listy. Ona po prostu dodaje nam nasz port do listy i nie przewiduje ûadnych kîoptów. Tak wiëc moûe wystâpiê sytuacja, gdy na liôcie bëdzie kilka portów o takiej samej nazwie (uûywajâc _LVOFindPort() dostaniesz adres portu znajdujâcego sië najwyûej na liôcie o danej nazwie). Dlatego aby uniknâê tej sytuacji, przed dodaniem naszej struktury MsgPort do listy publicznej, warto jest uûyê _LVOFindPort() aby przekonaê sië o tym, czy port o takiej nazwie juû istnieje. Jeôli tak, to moûemy wyjôê z programu, wysîaê do portu jakiô komunikat (np. o tym, ûe wystartowano drugi taki sam program) i/albo zmieniê nazwë naszego portu (i oczywiôcie jeszcze raz uûyê _LVOFindPort(), aû do skutku...). Trzy kolejne pola - mp_Flags, mp_SigBit i mp_SigTask (lub mp_SoftInt; wyjaônienie dalej) wystëpujâ tylko w strukturze MsgPort. Najpierw jednak opiszë mp_SigTask. W polu tym najczëôciej znajduje sië adres naszego Tasku (zadania), czyli mówiâc po ludzku adres struktury opisujâcej nasz program. Tak naprawdë, to uruchomienie kaûdego programu w systemie îâczy sië ze stworzeniem przez system dodatkowej struktury - wîaônie Task (lub czasem Process) - w której zawarte sâ pewne informacje dotyczâce naszego programu i otoczenia w jakim zostaî on uruchomiony, np. tzw. current dir, nazwa naszego programu etc. Poniewaû z pustego i "Salamon" nie naleje, zatem adres naszego tasku - rzecz jasna - musimy sami znaleúê i w tym polu umieôciê. Uûywamy do tego procedury _LVOFindTask() (takûe z exec.library; zauwaû, ûe funkcja ta dziaîa identycznie jak _LVOFindPort(), tyle ûe wyszukuje taski, a nie porty), zaô jako parametr (wskaúnik na nazwë zadania) podajemy zero - oznacza to, ûe mamy znaleúê adres struktury bieûâcego tasku, a nie jakiegoô tam z systemowej listy tasków. Po zwróceniu adresu w d0, kopiujemy go wîaônie do pola mp_SigTask. Ale i jeszcze wtedy nasz port nie bëdzie gotowy do dziaîania (o tym póúniej). Po co tam adres naszego tasku? Otóû multitasking jest zrealizowany - najproôciej mówiâc - wîaônie dziëki istnieniu listy z taskami, które sâ co chwilâ "po kawaîku" wykonywane (tzn. struktura Task zawiera informacje o ostatnio wykonanym rozkazie programu, który opisuje i to ten program a nie Task jest wykonywany). Kiedy czekamy na komunikat, nasz program nie robi nic, po co wiëc marnowaê czas procesora na niepotrzebne pëtle? Istnieje wiëc druga lista - lista tasków czekajâcych, gdzie znajdujâ sië taski przeniesione z tej pierwszej listy dziëki uûyciu procedury _LVOWaitPort(). Skoro wiëc przenosimy Task do innej listy, musimy znaê jego adres, zatem stâd wynika potrzeba wpisania do struktury MsgPort adresu naszego Tasku. Po drugie zaô, kiedy port otrzyma komunikat, w ogólnoôci nasz task przenoszony jest z powrotem na listë tasków gotowych do wykonania, czyli znowu potrzebny jest jego adres. Chyba wiëc jasne, ûe jeôli to pole bëdzie wyzerowane, to nie ma co liczyê na poprawne dziaîanie, a w zasadzie na jakiekolwiek dziaîanie funkcji _LVOWaitPort()? Jeôli tak, to jedziemy dalej. Pole mp_SigTask nazwa sië teû mp_SoftInt. Dlaczego? Wynika to z tego, ûe w zaleûnoôci od ustawienia flag w polu mp_Flags, nasz program moûe sië róûnie zachowywaê. Jeôli wiëc ustawimy odpowiedniâ flagë, nasz port moûe po otrzymaniu komunikatu spowodowaê wywoîanie przez system przerwania (_LVOCause()), którego adres struktury znajduje sië wîaônie w polu mp_SoftInt. Dlatego pole to ma dwie nazwy. W polu mp_Flags ustawiamy flagi (bity) maskowane przez PF_ACTION, które mówiâ nam, jak sië ma zachowaê nasz program po dotarciu do portu komunikatu. Flagi te to:
PF_ACTION Equ 3 ; Maska
PA_SIGNAL Equ 0 ; Zasygnalizuj Task (zadanie) w polu
; mp_SigTask (najczëôciej uûywane)
PA_SOFTINT Equ 1 ; Zasygnalizuj SoftInt (przerwanie)
; w polu mp_SoftInt
PA_IGNORE Equ 2 ; Zignoruj przybycie wiadomoôci
Flaga PA_SIGNAL jest uûywana - jak mi sië zdaje - najczëôciej i taka teû jest uûyta w doîâczonym przykîadzie. PA_SOFTINT, to wîaônie ta flaga, która powoduje wywoîanie przez system przerwania, której wskaúnik do struktury jest umieszczony w mp_SoftInt (czyli mp_SigTask). Trzecia, ostatnia flaga - PA_IGNORE - powoduje zatrzymanie wprawdzie wiadomoôci w porcie, która do niego przybywa, ale oprócz tego nic sië nie dzieje - Task nie jest sygnalizowany, zresztâ tak samo jak ewentualne przerwanie. Zostaîo jeszcze pole mp_SigBit, jednak tak jak procedury _LVOWait(), _LVOAllocSignal() oraz _LVOFreeSignal() zostanâ one opisane na koïcu, gdyû do najprostszej komunikacji miëdzy portami nie sâ potrzebne, chociaû dziëki nim moûna efektywniej programowaê. Pamiëtaj jednak, o wpisaniu do mp_SigTask/mp_SoftInt adresu Tasku/struktury SoftInt. Zostaîo jeszcze piëê pól, a w zasadzie cztery, które tworzâ strukturë List. W tym miejscu bëdâ umieszczane wskaúniki do napîywajâcych komunikatów. Poniewaû struktura Message (opisana poniûej) takûe - jak MsgPort - zawiera w sobie strukturë Node, moûe byê wiëc elementem listy dwukierunkowej. Tak teû sië dzieje. W strukturze mp_MsgList moûna znaleúê wskaúnik do pierwszego komunikatu, jaki przybyî, w pierwszym komunikacie adres do nastënego itd. Ûeby sië zbytnio nie mëczyê, do odczytywania komunikatów uûywamy procedury _LVOGetMsg(), która zwróci nam w d0 adres pierwszego dostëpnego komunikatu i dodatkowo usunie go z listy komunikatów oczekujâcych w porcie. Aby jednak nasz MsgPort moûna byîo w koïcu uûyê, musimy jeszcze wypeîniê owâ strukturë List (jeôli nasz port stanie sië publicznym, procedura _LVOAddPort() zrobi to juû za nas). Moûna do tego celu uûyê - jeôli korzystasz z includów - makra NEWLIST, zaô jeôli masz awersjë do tego rodzaju "uîatwieï" moûesz skorzystaê z przykîadu (który zresztâ zostaî ûywcem przepisany z w/w makra... 8). Pierwsze trzy pola to nagîówek listy - w polu lh_Head musi znaleúê sië adres pola lh_Tail, lh_Tail musi zostaê wyzerowane (to pole zawsze bëdzie/musi byê równe zero), zaô do lh_TailPred naleûy wpisaê adres pola lh_Head. Przeôledúcie zresztâ przykîad, a dojdziecie sami w czym rzecz. Pole lh_Type zerujë - okreôla ono zapewne typ nagîówka, ale nigdzie w materiaîach nie spotkaîem sië z wpîywem wartoôci tego pola na dziaîanie portu. Po prostu wyzeruj. No i ostatnie quasi-pole to wyrównanie do parzystego adresu - nie sâ tam zapisywane ûadne informacje, istnieje ono tylko po to, ûeby ewentualna kolejna struktura czy kod znajdujâce sië po strukturze MsgPort zaczynaî sie od parzystego adresu.
Tak wyglâda struktura MsgPort, ale co z Message? Otóû struktura ta skîada sië ze swego rodzaju nagîówka i... absolutnie wszystkiego, czego sobie zaûyczysz. Od nas bëdzie zaleûaîo, jakie komunikaty (i jakiej treôci) bëdziemy wysyîaê. Nagîówek zawiera - jak wiele struktur w systemie - Node oraz informacje n/t portu, skâd wiadomoôê przyszîa oraz jaka jest dîugoôê naszej wiadomoôci:
NT_MESSAGE Equ 5
Message: dc.l 0 ; mn_Node.......ln_Succ
dc.l 0 ; ln_Pred
dc.b NT_MESSAGE ; ln_Type
dc.b 0 ; ln_Pri
dc.l 0 ; ..............ln_Name
dc.l MsgPort ; mn_ReplyPort
dc.w 20+n ; mn_Length
;--------------------------------------
blk.b n ; nasze dane,
; czyli tzw. "message body"
W tym przypadku musimy sië zatroszczyê o wartoôê pola ln_Type (NT_MESSAGE), o mn_ReplyPort - jest adres struktury MsgPort (np. tej zdefiniwanej powyûej) oraz o mn_Length, gdzie wpisujemy dîugoôê struktury Message (20 bajtów) plus dîugoôê bloku naszych danych. Naleûy pamiëtaê, ûe ze wzglëdu na to, iû pole mn_Length jest sîowem, maxymalna dîugoôê Message nie moûe przekroczyê 64 kB (oczywiôcie moûna to zignorowaê i wysyîaê dîuûsze komunikaty, tyle, ûe sami gdzieô w treôci Message powinniômy okreôliê jego dîugoôê - jeôli nam na tym zaleûy...). Przykîadowa struktura Message moûe wyglâdaê nastëpujâco:
MyAge: Equ 21 ; to juû tyle???
HerAge: Equ 20 ; prawie...
Weight: Equ 77 ; schudîem!!! 8)
Height: Equ 179 ; niestety juû nie rosnë..
Message: dc.l 0
dc.l 0
dc.b NT_MESSAGE
dc.b 0
dc.l 0
dc.l MsgPort
dc.w 20+16 ; dîugoôê struktury
; Message + Message Body
;---------------------------- tu zaczynajâ sië nasze
; dane...
dc.l Nick ; 4 bajty
dc.l Group ; 8 bajtów
dc.l MyGirl ; 12 bajtów
dc.b MyAge ; 13 bajtów
dc.b HerAge ; 14 bajtów
dc.b Weight ; 15 bajtów
dc.b Height ; 16 bajtów (dîugoôê
; naszego "message body")
;---------------------------- a tu sië koïczâ.
Nick: dc.b "Cromax",0
Group: dc.b "Alchemy",0
MyGirl: dc.b "Olimpia",0
Jak widaê, nasza wiadomoôê zawiera takûe wskaúniki do nazw, co jest jednak dozwolone - w ten sposób moûna ominâê "limit" 64 kB. Uwaga: jeûeli wysyîamy do jakiegoô portu Message zawierajâcy wskaúniki (jak w powyûszym przykîadzie), to do czasu kiedy tamten Task nie odpowie, nie moûemy zmieniaê/dealokowaê pamiëci, których adresy wysîaliômy; w przeciwnym wypadku trzeba liczyê sië z niepoprawnym dziaîaniem programu.
Kiedy juû wyjaôniliômy sobie budowë struktur i znaczenie pól, moûemy przejôê do szczegóîowego omówienia procedur. Sâ to wîasnorëcznie (ta taa!) tîumaczone opisy procedur z tzw. AutoDoc-ów:
Jedynie te porty, które bëdâ wyszukiwane dziëki funkcji FindPort() wymagajâ dodania ich do systemowej listy. Jeôli port bëdzie w ogóle wyszukiwany, to pole priorytetu powinno byê równe co najmniej 1 (aby uniknâê z reguîy duûej liczby nieaktywnych portów o priorytecie zero). Jeûeli natomiast port bëdzie czësto wyszukiwany, warto ustawiê priorytet w zakresie od 50 do 100 (wtedy znajdzie sië przed innymi, rzadziej uûywanymi portami).
Kiedy juû port zostanie dodany do listy, musisz uwaûaê, aby usunâê go stamtâd (przez funkcjë RemPort) zanim zwolnisz zaalokowanâ dla niego pamiëê.
Nie wykonuj funkcji AddPort() na aktywnym porcie (tzn. po wykonaniu na nim funkcji _LVOWait() czy _LVOWaitPort() - przyp. tîum.)
Komunikaty mogâ byê doîâczane tylko do jednego portu naraz. Rozmiar czy forma wiadomoôci moûe byê zupeînie dowolna. Poniewaû komunikat nie jest kopiowany, wspóîdziaîajâce zadania dzielâ tâ samâ pamiëê zajmowanâ przez wiadomoôê. Zadanie, które wysîaîo komunikat, nie moûe zwolniê pamiëci zajëtej przez treôê komunikatu, dopóki odbiorca nie wyôle odpowiedzi. Oczywiôcie zaleûy to od traktowania przychodzâcych wiadomoôci oraz konwencji przyjëtych przez zwiâzane ze sobâ zadania. Jeûeli pole ReplyPort jest róûne od zera, to kiedy obiorca odpowie na komunikat, zostanie on wysîany z powrotem do portu. którego adres znajduje sië w powyûszym polu.
Gdy zostanie wysîana wiadomoôê, moûliwe jest wykonanie jednej z poniûszych czynnoôci:
Wykonanie czynnoôci zaleûy od wartoôci znajdujâcej sië w polu mp_Flags portu docelowego.
Opisywana funkcja nie czeka na przybycie komunikatu do portu. Jeôli w porcie nie ma ûadnej wiadomoôci, funkcja zwróci zero. Jeôli program musi czekaê na komunikat, naleûy uûyê fukcji Wait() czekajâcej na sygnaî dla portu lub funkcji WaitPort(). Na dany port moûe czekaê tylko jedno zadanie (dlatego nie moûna czekaê na porty z listy systemowej, bo jakiô Task moûe juû na niego czekaê - przyp. tîum.).
Pobranie komunikatu nie jest jednoznaczne dla zadania wysyîajâcego, ûe komunikat juû jest wolny i ûe moûna np. zwolniê pamiëê przez niego zajmowanâ. Kiedy odbiorca skoïczy analizowaê wiadomoôê, moûe jâ odesîaê z powrotem do nadawcy przy pomocy funkcji ReplyMsg().
Pobranie sygnaîu NIE ZAWSZE równa sië otrzymaniu komunikatu. Do portu moûe przybyê wiëcej niû jedna wiadomoôê per sygnaî, zaô sygnaîy mogâ sië ukazywaê bez komunikatów. Najczëôciej musisz zapëtliê GetMsg() do momentu, kiedy zwróci zero, a dopiero potem znowu uûyê Wait() czy WaitPort().
Funkcjë moûna wywoîaê z przerwaï.
Aby czekaê na wiëcej niû jeden port, naleûy poîâczyê bity sygnaîowe z kaûdego portu do jednego wywoîania funkcji Wait(), potem uûywajâc GetMsg() w pëtli wydobyê wszystkie komunikaty. Moûe sië zdarzyê, ûe otrzymasz sygnaî dla portu BEZ komunikatu. Naleûy to przewidzieê.
Uff, trochë juû chyba wiemy, przyszîa wiëc pora, aby sprawdziê nasze wiadomoôci. Jeszcze tylko jedno sîówko o funkcjach _LVOForbid()/_LVOPermit(). Pierwsza z nich zatrzymuje multitasking, tzn. powstrzymuje system przed przeîâczaniem obiektów w listach systemowych, usuwaniem ich etc. Druga, przywraca normalny stan. Po co nam te dwie funkcje? Wyobraúmy sobie, ûe uûywamy _LVOFindPort(). Zaraz po tym, gdy znaleúliômy poszukiwany port, program, który go zaîoûyî, usuwa go z listy. Czyli w sumie mamy adres do nieistniejâcego juû portu. Hmm... zatem jeôli chcemy wysîaê jakikolwiek komunikat do portu publicznego, naleûy: 1). uûyê _LVOForbid() 2). nastëpnie _LVOFindPort() 3). wysîaê komunikat _LVOPutMsg() 4). przywróciê multitasking _LVOPermit().
No, teraz juû do roboty! Mamy tu listingi dwóch programów. Pierwszy z nich zakîada publiczny port o nazwie "Amsterdam" i czeka na wiadomoôê. [plik WaitPort.s] Drugi szuka na liôcie portów publicznych w/w portu i jeôli go znajduje, wysyîa mu komunikat. [plik Put.s] Najlepiej najpierw je przepisz - nie sâ one specjalnie dîugie - i uruchom, a potem wróê ewentualnie do lektury. Zanim jedak je wykonasz, otwórz dwa okienka Shell (czy CLI). Teraz dopiero w jednym uruchom pierwszy program (Wait), zaô w drugim - program Put, najlepiej z parametrami (dowolnymi), np: 2> Put Józek ma drewniany wózek
Po zapoznaniu sië z dziaîaniem obu programów oraz przyswojeniem sobie wiedzy na temat funkcjonowania procedur uûytych w przykîadach, sugerujë poszerzenie wiedzy dziëki lekturze poniûszego materiaîu, który traktuje o czekaniu na komunikaty... jednoczeônie w kilku portach na raz! A co, Amiga rules!
W powyûszych opisach juû zetknâîeô sië z takimi wyrazami jak "signal", "mp_SigBit", "_LVOWait()" etc. Wróêmy zatem znowu do naszego morskiego portu, gdzie w powietrzu unoszâ sië mewy, zaô bosmani klnâc, zachrypniëtym gîosem poganiajâ majtków... Zaczynasz sië rozglâdaê i nagle spostrzegasz latarnië morskâ. Na jej czubku siedzi facet i ciâgle obserwuje otoczenie. Kiedy widzi, ûe do portu przybija statek albo ûe zbiera sië na burzë, czy teû spostrzega jeszcze coô innego, wychyla sië przez barierkë i wrzeszczy jak opëtany. Krótko mówiâc, daje sygnaî. Jak pamiëtasz, przybicie statku do portu, byîo dotarciem komunikatu. Natomiast ten krzyk niezrównowaûonego latarnika jest niczym innym, jak sygnalizowaniem. Cóû to jest, to tajemnicze sygnalizowanie? No wiëc wróêmy jeszcze na chwilë, do naszego krzykacza... Powiedziano mu, ûe jeôli zaobserwuje zawijajâcy statek, to ma krzyczeê STATEK W PORCIE! Dwa - jeôli zobaczy burzë, to ma krzyczeê IDZIE BURZA! Jeôli natomiast zobaczy ûóîtâ îódú podwodnâ, to niech krzyczy YELLOW SUBMARINE! Ale to jest wszystko, o czym potrafi zasygnalizowaê. Jeôli mu nie powiemy, ûe gdy zobaczy ufo, to ma krzyczeê UFO!, to nawet gdyby doszîo do inwazji, facet bëdzie milczaî wypatrujâc statków, burzy czy îodzi podwodnych. Przekîadajâc to na bardziej komputerowy sposób rozumowania - jeôli zajdzie zdarzenie A, to daj sygnaî nr 1, gdy dojdzie do wydarzenia B, zasygnalizuj 2 etc. Otóû dotarcie wiadomoôci do portu jest pewnym zdarzeniem. Jeôli teraz w polu mp_SigBit bëdzie znajdowaîa sië prawidîowo (o tym póúniej) okreôlona wartoôê, to zostanie ona zasygnalizowana (moûna powiedzieê, ûe ta wartoôê jest treôciâ komunikatu, jakâ ma wykrzyczeê latarnik). Czyli powiedzmy ten facet wstanie i krzyknie STATEK! Jak pamiëtasz, funkcja _LVOWaitPort() czekaîa, aû w porcie pojawi sië komunikat. Natomiast _LVOWait() czeka na okreôlony sygnaî. Wyobraú sobie, ûe pod latarniâ poîoûyîo sië spaê dwóch marynarzy. Jeden z nich budzi sië na dúwiëk sîowa STATEK W PORCIE A!, drugi zaô STATEK W PORCIE B!. Tych dwoje marynarzy, to nasza funkcja _LVOWait(). Zauwaû, ûe jeôli latarnik krzyknie YELLOW SUBMARINE!, to ûaden z nich sië nie obudzi (chyba, ûe poîoûymy tam jeszcze jednego marynarza reagujâcego na ten okrzyk). Poîâczmy to teraz z portami. Niech - tak jak powyûej - zawiniëcie statku do portu A bëdzie zdarzeniem A, zaô sîowo STATEK W PORCIE A! bëdzie sygnaîem nr 1 itd. Weúmy teraz dwa róûne porty (A i B). Do jednego, do pola mp_SigBit wpisujemy Sygnaî nr 1 (tzn, ûe jak wpîynie do niego Message, to zasygnalizowany bëdzie Sygnaî nr 1, czyli okrzyk STATEK W PORCIE A!), zaô do drugiego - Sygnaî nr 2. Oczywiôcie w polu mp_SigBit moûe znajdowaê sië tylko jeden sygnaî. Teraz z pól mp_SigBit bierzemy oba sygnaîy, podajemy je jako argument do _LVOWait(), nasz program zatrzymuje sië i... i jeûeli do jednego z nich wpîynie Message, to nasz program pójdzie dalej! Krótko mówiâc - _LVOWaitPort() umoûliwiaîa nam oczekiwanie na komunikat, ale tylko w jednym porcie, natomiast _LVOWait() umoûliwa czekanie na komunikaty w jednym i wiëcej portach na raz! No, tak naprawdë, to _LVOWait() czeka na okreôlone sygnaîy, które jeôli sâ ustawione w porcie, bëdâ zasygnalizowane gdy przybëdzie wiadomoôê (lub gdy sami procedurâ _LVOSignal() taki sygnaî wyôlemy do okreôlonego Tasku). Oczywiôcie, jeôli mimo tego, ûe ustawimy w mp_SigBit numer sygnaîu, ale nie podamy go do _LVOWait(), to choêby wpîynëîo tam tysiâc komunikatów, nie bëdziemy o tym informowani.
Skoro - jak mam nadziejë - ogólna zasada dziaîania sygnaîów zostaîa wyjaôniona, moûna przejôê do czëôci bardziej ôcisîej. Otóû dla kaûdego tasku przewidziane jest 32 róûnych sygnaîów, na które moûe oczekiwaê _LVOWait(). Szesnaôcie z nich jest przeznaczone dla sygnaîów sytemowych, np. sygnaî jest wysyîany po naciôniëciu klaiwszy Ctrl-C, -D, -E, -F oraz z powodu innych zdarzeï. Pozostaîe 16 sygnaîów moûemy juû dowolnie wykorzystaê. Piszâc nasz program, moûemy takûe czekaê na sygnaîy systemowe i po ich otrzymaniu stosownie sië do nich zachowaê (np. przerwaê wykonywanie programu). Sygnaîy sâ reprezentowane przez bity (jeden ustwiony bit, to jeden sygnaî), przy czym systemowe sygnaîy, to 16 mîodszych bitów. Reszta, to bity (sygnaîy), które moûemy sobie wykorzystywaê wedle naszych potrzeb. Ûeby móc wykorzystaê gdzieô sygnaî, trzeba go sobie zaalokowaê (czyli przydzieliê). Sîuûy do tego funkcja _LVOAllocSignal(). Podaje sië jej jako argument numer bitu, który ma byê przydzielony nam jako sygnaî (od 0 do 31). Jeûeli nie ma juû wolnych sygnaîów albo numer, który podaliômy jest juû w uûyciu, zostanie nam zwrócone -1. Najrozsâdniejsze wydaje sië alokowanie kolejnego wolnego sygnaîu (bitu). W tym celu zamiast konkretnego numeru bitu na wejôciu podajemy po prostu -1. Na wyjôciu dostaniemy albo numer zaalokowanego sygnaîu, albo -1 w przypadku bîëdu. Numer sygnaîu jest zapisany w bajcie i bajt równieû wynosi dîugoôê pola mp_SigBit, gdzie wpisujemy nasz poprawnie zaalokowany sygnaî. Jeôli korzystamy z kilku portów, powtarzamy wyûej opisane czynnoôci. Jako parametr dla _LVOWait() podajemy nie numery bitów, ale dîugie sîowo, gdzie bity o zaalokowanych numerach musimy sami ustawiê. Dajmy na to, ûe zaalokowaliômy sygnaîy $14, $15 i $16. musimy zatem ustawiê te bity w rejestrze d0 przed wywoîaniem _LVOWait(), czyli binarnie d0 wyglâdaîoby nastëpujâco: d0 = %00000000.01110000.00000000.00000000. Teraz dopiero moûemy wywoîaê _LVOWait(). W wyniku dostajemy ustawienie bitów, które zostaîy zasygnalizowane (moûe sië zdarzyê, ûe pojawi sie kilka sygnaîów na raz; naleûy o tym pamiëtaê). Naleûy teraz sprawdziê, czy numer bitu z kaûdego portu jest ustawiony na wyjôciu i przedsiëwziâê odpowieniâ akcjë.
Przykîad wykorzystania procedury _LVOWait() znajduje sië poniûej. [plik Wait.s] Ûeby wyjôê z programu moûemy nacisnâê przy aktywnym okienku klawisz Esc, gadûet WindowClose lub programem Put z pierwszego przykîadu wysîaê polecenie "quit" (maîe litery). Kaûdy inny wyraz zostanie zapisany (podobnie jak w przykîadzie WaitPort) do output (czyli najczëôciej na okno Shell). Dodatkowo naciskajâê klawisze kursora przy aktywnym oknie, moûemy nim przesuwaê. Poniewaû ten przykîad takûe zakîada port "Amsterdam", zatem nie da sië uruchomiê WaitPort jak i Wait równoczeônie.
Choê przekracza to materiaî artykuîu, to jednak naleûy wspomnieê jeszcze o procedurach _LVOSetSignal() oraz _LVOSignal(), które pozwalajâ: pierwsza na ustawianie lub odczytywanie sygnaîów tasku, druga na wysîanie do okreôlonego tasku sygnaîów (i przerwanie czekania _LVOWait(), jeôli sygnalizowany task akurat byî uôpiony). Opis jednakûe tych procedur zarówno jak i systemu komunikacji przy pomocy sygnaîów nie jest czëôciâ tego artykuîu.
maskowanie - jest to ustawienie tylko tych bitów (wszystkich, czëôci, jednego lub ûadnego), które pokrywajâ sië z ustawionymi bitami maski; przykîadowo niech nasza maska (tj. zwykîa liczba) ma wartoôê 3; binarnie (w systemie dwójkowym) zapisujemy to nastëpujâco: #3 = %11; jeôli wiëc mamy coô zamaskowaê wartoôciâ 3, musimy innymi sîowy wykonaê rozkaz AND maski z polem, które chcemy zamaskowaê; e.g. w polu znajduje sië wartoôê (binarnie) %10010110; po zamaskowaniu tego pola, otrzymamy w nim wartosê %00000010, gdyû tylko dwa pierwsze bity mogîy byê ustawione, pozostaîe zaô naleûaîo wyzerowaê; tu kilka przykîadów maskowania: maska = %00011011, pole = %11111111, wynik = %00011011; maska = %10101010, pole = %11001100, wynik = 10001000 etc.
cromax@hpux.wcss.wroc.pl