exec.library ------------ PORTY Moûe to pojëcie obiîo Ci sië juû o uszy. Moûe nawet wiesz, czym sâ porty i jak z nich korzystaê? Jeôli tak, to 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 jest wypeînione sîonym zapachem morza i bardziej juû przykrym odorem nadgniîych ryb, to gorâco Ci polecam lekturë poniûszego tekstu. Cromax/Alchemy Port -- ten zwykîy, gdzie moûna znaleúê snujâcych sië marynarzy i gotowe na wszystko kobiety, istnieje, ûeby mogîy do niego zawijaê i odpîywaê z niego statki. Identycznie rzecz sië ma z portami z Execa. Jedynâ róûnicâ jest brak zepsutego powietrza i sîonej wody, statki zaô sâ zastâpione wpîywajâcymi (lub wychodzâcymi) wiadomoôciami (a gdzie te kobiety? -- red.). 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 bëdë uûywaî angielskiego okreôlenia message). Moûemy teû zatrzymaê nasz program i poczekaê, aû wpîynie do niego jakiô komunikat (czyli po angielsku message). Kiedy taki message przybëdzie, naleûy go odebraê, odczytaê, odpowiednio zinterpretowaê, ewentualnie wykonaê 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 standardowy. 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ë do odczytywania wiadomoôci. W tym wypadku jednak musimy byê caîkowicie pewni, ûe taka zamiana jest dozwolona (wyjaônienie póúniej). Nie zawsze moûna jej dokonaê. Natomiast 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ôci oraz ich wysyîanie. Poprzedni 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.library. Sâ to: _LVOWait Equ -318 _LVOAllocSignal Equ -330 _LVOFreeSignal Eqi -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 (2.0) _LVODeleteMsgPort Equ -672 ; V36 (2.0) 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ê komunikaty i czekaê (czego nie powinny robiê), aû do portu wpîynie jakiô komunikat; czyniâc port publicznym, trzeba mieê naprawdë do tego powód, bo wtedy naleûy byê przygotowanym wîaôciwie na 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ôliê, û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 pomocâ zaô _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 dealokujâ të pamiëê (zwalniajâ). Poniewaû -- jak juû wspomniaîem -- procedury te dostëpne sâ jedynie od systemu 2.0, na niûszych systemach musimy sië sami zatroszczyê o poprawne 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 struktury 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 bëdzie adres nastëpnego portu -- lub zero, jeôli nasz port jest ostatni na liôcie, w drugim poprzedniego lub zero, jeôli 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 wypadku, 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îopotów. Moûe wiëc 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, warto uûyê _LVOFindPort() przed dodaniem naszej struktury MsgPort do listy publicznej, aby przekonaê sië, 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 uruchomiono 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 proôciej 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 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), jako parametr zaô (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 wtedy nasz port nie bëdzie jeszcze 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 chwila "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 pierwszej listy dziëki uûyciu procedury _LVOWaitPort(). Skoro wiëc przenosimy task do innej listy, musimy znaê jego adres, stâd zatem 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. To tyle w tym odcinku, za miesiâc zajmë sië polem mp_Flags.