2009/08 – zmiana linku do kodu źródłowego z dropboxa na github
Na Programowaniu Systemowym zostało nam zadane kolejne zadanie, jako że oddałem je jako jeden z pierwszych – a wydaje mi się że wśród kolegów z grupy będzie na niego popyt – postanowiłem że je udostępnię tutaj. Przede wszystkim dlatego by poinformować że jeszcze żyje i aktualizuje tego bloga.
Na sam początek zadanie:
Utworzyć 3 procesy pomiędzy którymi będzie zachodziła komunikacja (łącza nazwane). Pierwszy proces jest interfejsem użytkownika. Użytkownik podaje w nim dwie liczby i wybiera działanie arytmetyczne. Te dane przesyłane są do procesu obliczeniowego w którym obliczana jest wartość wyrażenia. Wynik jest przesyłany do trzeciego procesu, który wyświetla wynik. Każdy proces używa osobnego terminala.
Pierwszym krokiem który należy zrobić, to dowiedzieć co to są te łącza nazwane. W informatyce stosuje się przede wszystkim terminy angielskie, wypadało by więc dowiedzieć się co miał polski tłumacz na myśli. Pomaga tutaj wyszukiwarka Google i Wikipedia, które naprowadzają nas na angielską nazwę: Named pipe jak i na funkcje która tworzy taki potok w linuksach: mkfifo.
Zanim zaczniemy jednak pisać odpowiednie programy, wypada dowiedzieć się jak ta funkcja działa/jakie potrzebuje parametry. W systemie Windows moglibyśmy skorzystać z Microsoft Developer Network, a w Linuksie można skorzystać z wbudowanego podręcznika: man
. By otworzyć odpowiednią stronę manuala należy wydać polecenie: man 3 mkfifo
. Czyli otwórz mi sekcje trzecią podręcznika o mkfifo. Oczywiście w internecie są odpowiednie strony które pozwalają na dotarcie do stron podręcznika bez potrzeby uruchamiania Linuksa czy wywoływania polecenia man.
Jeśli ktoś uważnie przeczytał stronę podręcznika poświęconą mkfifo, to pewnie zauważył że w manualu jest też strona poświęcona fifo znajdująca się w sekcji 7 (man 7 fifo
). Na tej stronie znajduje się informacja jak nazwany potok jest zaimplementowany w kernelu Linuksa.
Drugim krokiem który wypada zrobić, to odpowiednio zaprojektować komunikacje między trzema programami. Ja postanowiłem że skorzystamy z dwóch potoków nazwanych – pierwszy będzie przesyłał informacje z interfejsu do logiki, a drugi z logiki do wyjścia. Jest to dość logiczne rozwiązanie, usuwające nam przy okazji konieczność dodawania do wiadomości które będziemy przesyłać informacji o tym od kogo albo też do kogo jest skierowana ta wiadomość.
Oprócz tego należy zastanowić się Co będziemy przesyłać. Ja w swoich programach postanowiłem przesyłać dane binarnie (czyli tak jak są zapisane w pamięci komputera), wynika to z tego że w języku C nie ma strumieni (cout
/cin
) i wczytywanie liczb zapisanych dziesiętnie może być irytujące (patrz: p2_interfejs.c:45-89). Jeśli jednak prześlemy dane w oryginalnej postaci, wystarczy tylko je rzutować.
Jako że w filozofii linuksa wszystko jest plikiem, to i gniazda nazwane można tak traktować. Oznacza to że można używać do nich najprostszych funkcji do obsługi plików – fopen
/fread
/fwrite
/fclose
, czy też strumieni w C++.
Dane przesyłane między programami zapisane są w dwóch różnych formatach:
- Między interfejsem a logiką – dwa inty i dwa chary. Gdzie inty odpowiadają za liczby, pierwszy char za zapisany w kodzie ascii znak wyrażenia, a drugi miał odpowiadać za wyrównanie – później stwierdziłem że jest to zbędne, ale i tak to zostało w kodzie. Przy czym jeśli interfejs wyśle same zera, to będzie oznaczało że użytkownik chce wyjść z programu.
- Między logiką a wyjściem – jeden char i jeden int. Gdzie char odpowiada za status, a int za wynik działania. Gdy status jest równy zero to należy wyjść z programu.
To chyba tyle jeśli chodzi o wstęp teoretyczny, całą resztę można sobie wyczytać z kodu źródłowego programów. Tutaj jeszcze chciałbym zaznaczyć, że swoje programy napisałem w C, czyli nie korzystałem z std::string
, strumieni – które IMO uprościły by kod, i innych funkcjonalności tego języka.