Temat: System zarządzania zadaniami i ich komunikacja (Scheduler, MessageBox).



Podobne dokumenty
Temat: System zarządzania zadaniami i ich komunikacja (Scheduler, MessageBox).

Podstawy programowania komputerów

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

/* dołączenie pliku nagłówkowego zawierającego deklaracje symboli dla wykorzystywanego mikrokontrolera */ #include <aduc834.h>

Informacje wstępne #include <nazwa> - derektywa procesora umożliwiająca włączenie do programu pliku o podanej nazwie. Typy danych: char, signed char

Czym są właściwości. Poprawne projektowanie klas

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Podstawy programowania, Poniedziałek , 8-10 Projekt, część 1

Pętle. Dodał Administrator niedziela, 14 marzec :27

Język C++ zajęcia nr 2

Lista 5 Typy dynamiczne kolejka

Uniwersytet Zielonogórski Instytut Sterowania i Systemów Informatycznych. Ćwiczenie 3 stos Laboratorium Metod i Języków Programowania

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Programowanie strukturalne i obiektowe. Funkcje

Programowanie w języku Python. Grażyna Koba

1 Podstawy c++ w pigułce.

Wstęp do programowania 2

Jak napisać program obliczający pola powierzchni różnych figur płaskich?

- - Ocena wykonaniu zad3. Brak zad3

Kompilator języka C na procesor 8051 RC51 implementacja

4. Procesy pojęcia podstawowe

Programowanie współbieżne Wykład 2. Iwona Kochańska

Wieczorowe Studia Licencjackie Wrocław, Wykład nr 6 (w oparciu o notatki K. Lorysia, z modyfikacjami) Sito Eratostenesa

kiedy znowu uzyska sterowanie, to podejmuje obliczenie od miejsca, w którym poprzednio przerwała, i z dotychczasowymi wartościami zmiennych,

Algorytmy i złożoności. Wykład 3. Listy jednokierunkowe

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Języki i techniki programowania Ćwiczenia 2

Lekcja : Tablice + pętle

Wstęp do programowania INP001213Wcl rok akademicki 2017/18 semestr zimowy. Wykład 6. Karol Tarnowski A-1 p.

Zapisywanie algorytmów w języku programowania

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

Zmienne, stałe i operatory

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

E S - uniwersum struktury stosu

Rozdział 4 KLASY, OBIEKTY, METODY

Wydział Elektryczny. Katedra Telekomunikacji i Aparatury Elektronicznej. Konstrukcje i Technologie w Aparaturze Elektronicznej.

4. Procesy pojęcia podstawowe

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Procesy i wątki. Krzysztof Banaś Obliczenia równoległe 1

1. Liczby i w zapisie zmiennoprzecinkowym przedstawia się następująco

Wprowadzenie do programowania

PMiK Programowanie Mikrokontrolera 8051

Podstawy programowania. Wykład 6 Wskaźniki. Krzysztof Banaś Podstawy programowania 1

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

1 Podstawy c++ w pigułce.

76.Struktura oprogramowania rozproszonego.

Struktury. Przykład W8_1

Od programowania wizualnego do tekstowego

Argumenty wywołania programu, operacje na plikach

Wyjątki. Streszczenie Celem wykładu jest omówienie tematyki wyjątków w Javie. Czas wykładu 45 minut.

2 Przygotował: mgr inż. Maciej Lasota

Laboratorium Informatyka (I) AiR Ćwiczenia z debugowania

Globalne / Lokalne. Wykład 15. Podstawy programowania (język C) Zmienne globalne / lokalne (1) Zmienne globalne / lokalne (2)

Podstawy programowania 2. Przygotował: mgr inż. Tomasz Michno

Część XVII C++ Funkcje. Funkcja bezargumentowa Najprostszym przypadkiem funkcji jest jej wersja bezargumentowa. Spójrzmy na przykład.

Wskaźniki w C. Anna Gogolińska

Poniższe funkcje opisane są w 2 i 3 części pomocy systemowej.

Tworzenie programów równoległych cd. Krzysztof Banaś Obliczenia równoległe 1

Laboratorium 5: Tablice. Wyszukiwanie binarne

5 Przygotował: mgr inż. Maciej Lasota

Rekurencja (rekursja)

Co to jest sterta? Sterta (ang. heap) to obszar pamięci udostępniany przez system operacyjny wszystkim działającym programom (procesom).

Języki C i C++ Wykład: 2. Wstęp Instrukcje sterujące. dr Artur Bartoszewski - Języki C i C++, sem. 1I- WYKŁAD

Wskaźniki. Przemysław Gawroński D-10, p marca Wykład 2. (Wykład 2) Wskaźniki 8 marca / 17

Pytania sprawdzające wiedzę z programowania C++

Programowanie obiektowe

Cwiczenie nr 1 Pierwszy program w języku C na mikrokontroler AVR

Laboratorium nr 12. Temat: Struktury, klasy. Zakres laboratorium:

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

Podstawy programowania 2. Temat: Funkcje i procedury rekurencyjne. Przygotował: mgr inż. Tomasz Michno

Instrukcje sterujące. wer. 11 z drobnymi modyfikacjami! Wojciech Myszka :53:

REKURENCJA W JĘZYKU HASKELL. Autor: Walczak Michał

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 4 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 44

ZASADY PROGRAMOWANIA KOMPUTERÓW

Programowanie - wykład 4

Podstawy programowania. Wykład: 5. Instrukcje sterujące c.d. Stałe, Typy zmiennych c.d. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Język JAVA podstawy. Wykład 3, część 3. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

6 Przygotował: mgr inż. Maciej Lasota

Warto też w tym miejscu powiedzieć, że w C zero jest rozpoznawane jako fałsz, a wszystkie pozostałe wartości jako prawda.

4. Procesy pojęcia podstawowe

Instrukcja do ćwiczeń nr 4 typy i rodzaje zmiennych w języku C dla AVR, oraz ich deklarowanie, oraz podstawowe operatory

Język C, tablice i funkcje (laboratorium, EE1-DI)

Język ludzki kod maszynowy

Widoczność zmiennych Czy wartości każdej zmiennej można zmieniać w dowolnym miejscu kodu? Czy można zadeklarować dwie zmienne o takich samych nazwach?

Stronicowanie w systemie pamięci wirtualnej

Konwersje napis <-> liczba Struktury, unie Scanf / printf Wskaźniki

Jak napisać listę jednokierunkową?

Struktura programu. Projekty złożone składają się zwykłe z różnych plików. Zawartość każdego pliku programista wyznacza zgodnie z jego przeznaczeniem.

Projektowanie oprogramowania systemów PROCESY I ZARZĄDZANIE PROCESAMI

Lab 10. Funkcje w argumentach funkcji metoda Newtona. Synonimy nazw typów danych. Struktury. Tablice struktur.

Podstawy programowania

Wstęp do programowania

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 5 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 41

Tworzenie aplikacji rozproszonej w Sun RPC

Typy wyliczeniowe Konwersje napis <-> liczba Struktury, unie Scanf / printf Wskaźniki

Wstęp do programowania

Niezwykłe tablice Poznane typy danych pozwalają przechowywać pojedyncze liczby. Dzięki tablicom zgromadzimy wiele wartości w jednym miejscu.

Transkrypt:

Temat: System zarządzania zadaniami i ich komunikacja (Scheduler, MessageBox). Ćwiczenie 3. (sd 2) 1.Uruchamianie zadań / przełączanie zadań. Praca wielozadaniowa (multitasking), daje programiście aplikacyjnemu możliwość tworzenia oprogramowania (zadań) działającego równolegle z innymi. Teoria i sposoby realizacji wykraczają poza ramy tego dokumentu, poruszone zatem zostaną tylko najprostsze i najważniejsze zagadnienia. Generalnie upraszczając, systemy wielozadaniowe można podzielić na: Kooperujące - Programista musi zrobić wszystko pisząc aplikacje, aby jego zadanie było kończone najszybciej jak to jest tylko możliwe. Jeżeli jest to niemożliwe musi być przekazane sterowanie do zadania przełączającego (pozwala innym zadaniom coś wykonać), a po następnym wywołaniu podjąć przerwane zadania (np. długotrwałe, skomplikowane obliczenia, oczekiwanie na interakcje z użytkownikiem lub wolnymi urządzeniami peryferyjnymi). Z reguły takie działanie jest akceptowalne w małych i prostych systemach, wymaga od programisty dużo uwagi nad projektowaniem aplikacji. Wywłaszczające - Programista pisze aplikacje nie wiedząc nic o tym kiedy i jak przekazywane jest sterowanie między zadaniami. Takie podejście powoduje że nie wiadomo kiedy zadanie zostanie przerwane, istnieje zatem konieczność dbania o spójność danych wykorzystywanych przez wiele procesów, wprowadzenie operacji atomowych (nie przerywalnych, wykluczających wyścig między procesami), natomiast system przełączania jest bardziej skomplikowany, gdyż konieczne jest zachowywanie kontekstu przerwanego zadania, a w odpowiednim momencie jego odtworzenie. Proces przełączania zadań (Scheduler), dba o odpowiednie wywoływanie procesów zgodnie z powyższymi modelami. Kolejność wywoływania zadań może być realizowana według algorytmu "wszystkim po równo" (algorytm prosty w implementacji, lecz mało efektywny), lub z wykorzystaniem priorytetowania zadań. 2.Implementacja przełączania zadań Podstawowym zadaniem (w ramach tego ćwiczenia) może być funkcja o prototypie: void funcx(void); Zakładając że zbiór zadań jest ponumerowany od 0 do N, mamy następujące funkcje o podobnych prototypach: func1, func2, func3,, funcn. Dla takiego zestawu można utworzyć w obszarze pamięci kodu (Code) tablicę: code void (*code f[])(void)={ func1, func2, func3, ; Wywołanie dowolnej tak zadeklarowanej funkcji sprowadzałoby się do wywołania: (*f[ task ])(); Gdzie zmienna task jest odpowiednikiem numeru funkcji aktualnie wywoływanej. Wartość task nie może wskazywać (indeksować) obszaru poza danymi zapisanymi w tablicy f. Nieprzestrzeganie tego założenia może doprowadzić do zawieszenia systemu a w ogólności powodować błędną pracę. Przy pomocy prostego przełącznika (schedulera) można po zakończeniu jednego zadania wywołać inne. Ważne jest aby funkcje były tak zapisane by trwać jak najkrócej. W szczególności nie wykonywały pętli nieskończonych lub bardzo długich (dłuższych niż czas dopuszczalny dla zadania), bez przekazywania obsługi przełącznikowi cyklicznie. Poniższy przykład ilustruje jak unikać tworzenia pętli nieskończonych:

void funcn(void){ Oryginalny kod z pętlą nieskończoną-długą long l; //zmienna lokalna funkcji!!! for(l=0; l<2147483647; l++){ //zadanie do //wykonania wewnątrz pętli Zmieniona wersja kodu bez pętli nieskończonej-długiej long l=0; //zmienna globalna!!! void funcn(void){ if(l<2147483647){ //zadanie do wykonania //wewnątrz pętli l++; Ja widać zadanie wykonywane w funkcji: funcn, będzie wykonywane tyle samo razy, różnica polegać będzie na sposobie wykonywania samej pętli. W przypadku złym (lewa strona tabeli) funkcja raz wywołana będzie realizować wszystkie iteracje pętli w drugim dobrym (prawa strona tabeli) funkcja wykona tylko jedną iterację pętli, resztę iteracji wykonane będzie przy następnych wywołaniach zadania przełącznika. W sumie obie wykonają tyle samo iteracji ale rozwiązanie dobre pozwoli innym zadaniom się wykonywać w tle. Na uwagę zasługuje zmienna l, jest ona używana dla sterowania przebiegiem działania pętli, lecz w większości przypadków używa się struktur które przechowują stan przerwanego zadania (stan nie musi być przechowywany w jednej zmiennej), dzięki czemu następne wejście w zadanie odtwarza stan z poprzedniego wywołania. 3.Modularyzacja Tworzenie zadań w przypadku rozbudowanych systemów może być powierzone niezależnym zespołom. W takich przypadkach, a także ze względu na przejrzystość kodu, każde z zadań dobrze jest umieszczać w innym pliku (module). Sam podział na pliki nie miałby większego sensu gdyby nie separacja obiektów. Jednym z założeń systemów wielozadaniowych jest separacja zadań ich komunikacja jest możliwa ale realizowana jest w ściśle kontrolowany sposób (patrz pkt. 5.Komunikacja miedzy zadaniami). W uroszczeniu separacje można osiągnąć przez umieszczenie zadań (tutaj funkcji) w różnych plikach: Plik: taska.c Plik: taska.h int ita,jta; void TaskA(void){ Plik: taskb.c int itb,jtb; void TaskB(void){ Plik: taskc.c void TaskA(void); Plik: taskb.h void TaskB(void); Plik: taskc.h int itc,jtc; void TaskC(void){ void TaskC(void); W tak podanym zestawie plików mamy trzy zadania: TaskA, TaskB, TaskC. Pliki nagłówkowe będą użyteczne wyłącznie przełącznikowi zadań, opisany następująco: #include taska.h #include taskb.h #include taskc.h #define MAX_TASK 3 code void (*code f[])(void)={ TaskA, TaskB, TaskC ;

void main(void){ char task=0; for(;;){ (*f[task])(); task++; if(task==max_task) task=0; Nazwy zmiennych w plikach taskc dobrano nie przypadkowo, sugeruje iż stanowią własność modułu deklarującego i nie wolno innym modułom korzystania z nich, dodatkowo nazwy pomagają wyśledzić ewentualne odstępstwa od tej reguły. 4.Przłącznik zadań z kolejka W poprzednim punkcie zamieszczono opis trywialnego przełącznika. Działanie jego polega na ciągłym wywoływaniu zadań (tutaj funkcji) z tablicy f. Taka praca przełącznika zakłada iż każde z zadań chce się wykonywać w nieskończoność. Rozwiązanie takie jest mało efektywne, zwłaszcza gdy zadanie nie ma więcej nic do zrobienia, lub trzeba powołać inne zadanie do życia. Najlepszym rozwiązaniem powyższych problemów jest utworzenie kolejki zadań. Powołanie zadania do życia polegało by na włożeniu go do kolejki, np.: post(taska); Funkcja post jest typu: char post(void (*tp) ()); gdzie parametr *tp to wskaźnik do zadania wkładanego do kolejki. Zadaniem systemu przełącznika jest ciągłe sprawdzanie stanu kolejki i uruchamianie zadania na jej szczycie, pętla główna takiego procesu mogła by wyglądać tak: while(1) { run_task(); gdzie właściwe wywoływanie zadań miało by miejsce w funkcji: run_task(); Aby zapewnić sprawną kolejkę zadań wystarczy zadeklarować tablice: sched_entry_t queue[max_tasks]; gdzie elementem tej tablicy jest: typedef struct { void (*tp) (); sched_entry_t; I dzięki takim deklaracjom możliwe będzie włożenie do MAX_TASK zadań do wykonania (za pomocą funkcji post). Dla prawidłowego działania przełącznika wymagane są jeszcze dwie zmienne: unsigned char sched_full; unsigned char sched_free; gdzie sched_full będzie wskazywać czy jest jeszcze miejsce w kolejce, a sched_free wskazywać będzie na zadanie do wykonania (korzystać z niej będzie funkcja run_task). Nasunąć się może pytanie a co się stanie w przypadku gdy kolejka się opróżni. Do takiej sytuacji nie powinno się dopuścić jeżeli system ma działać nieskończenie, w większości systemów zawsze działa jedno zadanie np.: IDLE czyli zadanie które sprawdza czy nie istnieją warunki do rozpoczęcia innego zadania, sytuacjami takimi może być interakcja z użytkownikiem (wydanie polecenia uruchom zadanie XXX), lub zdarzenie zewnętrzne które mogło by być obsługiwane przez nowe zadanie. Implementacja zadania IDLE (dalej nazywanego TaskA), mogła by wyglądać tak:

void TaskA(void){ if(sprawdzeni_warunków_wywołania_taskb()==0) post(taskb); if(sprawdzeni_warunków_wywołania_taskc()==0) post(taskc); post(taska); Przy tak napisanym zadaniu, wystarczy aby system przełącznika przed uruchomieniem pętli sprawdzającej kolejkę włożył zadanie A na jej początek, np.: void main(void){ sched_init(); post(zadaniea); while(1) { run_task(); Gdzie funkcja sched_init() przygotowywałaby kolejkę do pracy przełącznika. Zastanawiający może być zapis w kodzie zadania TaskA, wpisanie na jego końcu post(taska), jest to niezbędne aby zadanie działało w nieskończoność. Działanie systemu przełącznika powinna tak być realizowane aby włożenie zadania A trafiało na koniec kolejki tak aby dać szansę zadaniom B i C się wykonać. Bardziej zaawansowane systemy realizują też priorytetowanie kolejki, 5.Komunikacja miedzy zadaniami Idea systemów wielozadaniowych zakłada możliwość przesyłania informacji między procesami (po to by np. synchronizować zadania, dzielić się danymi). Adresowanie zadań nie jest ogólnie znormalizowane. W najprostszym podejściu zakłada się że każde zadanie ma swój numer - adres. Każde z zadań ma swoją kolejkę (w prostszych systemach kolejka może mieć długość 1), do której inne zadania (za pomocą systemu operacyjnego) będą wkładać wiadomości. Podstawowe funkcje mogą być następujące: SendMSG ReciveMSG WaitReciveMSG -Wyślij wiadomość (wstawienie do kolejki zadania adresata), argumentem musi być adres procesu i wiadomość. -Obierz wiadomość z kolejki (aktualnego zadania), jeżeli jest ona pusta nie czekaj, zwróć kod błędu (np.: QUEUE_EMPTY). -Czekaj aż w kolejce (aktualnego zadania) pojawi się wiadomość i pobierz ja, zakłada się wystąpienie sytuacji w której możliwe jest czekanie w nieskończoność (aż wiadomość się pojawi). Powyższe funkcje są implementowane przez system operacyjny, co z tym się wiąże podczas np. wykonywania funkcji WaitReciveMSG system może nie przekazać sterowania do funkcji wywołującej aż do otrzymania wiadomości dla wywołującego procesu (po to by nie wywoływać zadania czekającego - powodowałoby to marnotrawienie czasu procesora). Przykładowa struktura wiadomości może mieć strukturę: struct MSG{ t_adres sender; t_msg text; ; //kto wysłał wiadomość //wiadomość właściwa Gdzie typy: t_adres -typ reprezentujący adres, np.: numer procesu (dla małych systemów np. takie które są oparte o procesor 80C51 warto ograniczyć liczbę procesów do 255, wtedy można podstawić za t_adres typ unsigned char, np.: #define t_adres unsigned char

t_msg -typ zmiennej trzymający informacje o adresie (wskaźnik) wiadomości lub samą wiadomość (tablica), wybór zależy od konkretnej aplikacji - mocno zależy od ilości pamięci dostępnej w systemie i od długości wiadomości, np.: #define t_msg unsigned int Dla uproszczenia można stosować jedną kolejkę dla wszystkich zadań, bardziej skomplikowane staną się wtedy procedury ReciveMSG, WaitReciveMSG, kosztem uproszczenia SendMSG. Jedną z wiadomości może być informacja END_PROCESS (przekazywaną w polu text), po otrzymaniu której proces ma w możliwe krótkim czasie zwolnić wszelkie zasoby i wywołać funkcje exit() - czyli przekazać sterowanie programowi przełączającemu (scheduler) bez ponownego wywoływania. 6.Uwagi końcowe W prostych systemach należy ograniczać ilość procesów głównie z powodu problemu wielkości stosu i obszaru zmiennych. Projektując system przełączania zadań z wywłaszczaniem, nie wolno zapomnieć o czasie przełączania. Im ten czas będzie większy (w porównaniu z czasem działania zadań), tym efektywność systemu mocno spadnie. Podobnie im czas w jakim zadanie może się wykonywać będzie za długi - inne zadania będą miały wrażenie "zawieszania" się systemu. W przypadku systemów kooperujących sytuacja jest jeszcze bardziej skomplikowana - każde zadanie musi samo oszacować co może zrobić w ogólnie przyjętym przedziale czasu (nie istnieje arbiter przełączający niezależny od zaintersowanych). Wszystko zatem zależy od "wzajemnej uprzejmości" zadań, od wyobraźni i przezorności programisty piszącego dane zadanie. Konstruowanie przełącznika z wywłaszczaniem wymaga wnikania w zagadnienia wewnętrznej budowy procesora po to aby efektywnie (liczy się czas przełączania) i poprawnie (zapamiętanie stanu musi dotyczyć też obiektów nie dostępnych z poziomu języka C ). Bardzo często takie przełączanie może dość mocno ograniczyć liczbę procesów mogących działać równocześnie w systemie. W systemach o małych zasobach liczba procesów może być ograniczana do dziesiątek a nawet jedności (np.: 4-6). W systemach wielozadaniowych istnieje także problem dzielenia się pamięcią. Nie możliwa jest poprawna praca wielu procesów bez wyraźnego podzielenia zasobów dostępnych dla każdego z nich. Większe systemy dysponują specjalnymi mechanizmami wspieranymi sprzętowo do kontroli uprawnień dostępu do zasobów.