to get the file

Transkrypt

to get the file
Systemy Transputerowe
Programowanie transputerów
Transputer Toolset
Rozwój
● PP preprocesor „C”
● TCX kompilator
● TASM Transputer assembler
● TLINK linker/lokator
● TLIB obsługa bibliotek
● MAKE narzędzie do utrzymywania programu
Ładowanie
● LD-ONE – loader do pojedynczego transputera
● LD-NET – loader do sieci transputerów
● CIO – host driver dla „C’ wejścia/wyjścia na transputerze
● TIO – prosty przykład drivera dla standardowego we/wy
*.c ---> *.pp ---> *.tal ---> *.tlr ---> *.tld
Kompilacja
PP plik_wejściowy.c [-opcje]
-o inna_nazwa
-c 0 Nadmiar białych znaków redukowany do
pojedynczego
-c 1 pozwala na podmienienie makra w bloku #pragma
asm
-c 2 pozwala na rekursywne komentarze
-c 3 zagnieżdżone dyrektywy # ##.
-c 4 pozwala na ponowne #define tego samego symbolu
przed #undefine
-c 5 pozwala na rozpoznawanie i tłumaczenie
3znakowego kodu, znaków (tak jak w unixie)
Kompilacja
-d<nazwa_symbolu_makro>[=wartosc_marko] – pozwala
na zdefiniowanie makra prosto z linii poleceń. Jak nie
ma wartości makro to jest ona domyślnie = 1.
Np. pp foo.c -dhi lub pp foo.c –dhi=1
-e nawet gdy wystąpią błędy to nie przerywaj.
-i podajemy inna ścieżkę do includów. Ścieżka musi być
jedna na jedno i.
-l a numeracja lini #<line_number>
-l l numeracja lini #line <line-number>
-l n brak numeracji
-s generuj statystyki
Kompilacja
-t a <string_of_char> dodaj wszystkie te znaki do
preprocesora by wiedział że to są znaki
-t n <string_of_char> to samo tylko odejmij te znaki które
występują w string_of_char
-u <macro_symbol_name> wprowadzenie makro
symbolu
-v tryb „gadatliwy”
-z włączenie trybu debugowania. Tylko dostępny gdy
właczono przy kompilacji w pp.h definicje DEBUG na
TRUE
-? pomoc
Predefiniowane symbole
__LINE__
__FILE__
__DATE__
__TIME__
__NEXT__
__NOW__
__PREV__
- numer lini
- nazwa pliku
- data systemowa
- czas systemowy
- symbole używane do kontrolowania
aktywności generatora liczb unikalnych.
TCX
Cross kompilator tłumaczący na assembler
transputerowy.
Wywołanie:
tcx plik_wejsciowy.pp [-options]
Opcje:
-c skompresuj wyjście przez usuwanie źródłowych
informacji debugujących.
-d włączenie debugowania samego kompilatora.
-e mimo błędów zapisz na wyjściu to co się da
TCX
-f1..2 ustawia precyzję floatów, double i wyników
pośrednich 32b lub 64b
-i kompilator używa plików tymczasowych
-mc<number> ustawia domyślny numer modułu do
zapisu kodu?
-mi<number> ustawia domyślny numer modułu do
zapisu danych zainicjowanych
-mu<number> ustawia domyślny numer modułu do
zapisu danych niezainicjowanych
-o <plik_wyjsciowy> nazwa pliku wyjsciowego
TCX
-p0 generuj kod zawierający tylko instrukcje do 32b
transputerów.
-p2 do transputerów T212/T222
-p25 do transputerów T225
-p4 do T414
-p45 dla T400/T425
-p8 dla T800
-p85 dla T801/T805
-pc typ czysty char zrób signed (domyślnie jest
unsigned)
-ps ??
-q0 wyłącz wewnętrzny post-pass optymalizer.
-q1 -q2 -r -s -ti -to inne opcje optymalizacyjne.
-v wyłącz „gadatliwy” tryb
-w0 –w1 –w2 warning level
TASM
użycie:
tasm plik_wejsciowy [<tymczasowy_katalog>] -[options]
plik wejściowy podajemy bez rozszerzenia, pliki te mają
domyślnie *.tal wiec podajemy np. dla foo.tal:
tasm foo
wynikiem jest *.tlr
opcje:
-c kompresja pliku wynikowego, pozbycie się opcji debugowania
-l wygeneruj listning w assemblerze
-o <plik_wyjsciowy> określenie nazwy pliku wyjsciowego
-q{0|1|2} poziomom optymalizacji
-v włącznik/wyłącznik trybu „gadatliwego”
TLINK
użycie:
tlnk [plik_komend]
najlepiej używać z plikiem komend a w nim
FLAG „falgs (A|a|C|c|S|s) [none]:”
TEMP „Temp file path [.]:”
LIST „Listing file [NUL]:”
INPUT „Input file(s) [.trl]”
ENTRY „Entry point [_main]:”
LIB „Library file(s) [.tld]:”
OUTPUT „Output file [????????.tld]:”
LOAD „Load address [0x????????]:”
STACK „Stack address [0x????????]:”
TLINK
a – kolejność sortowania symboli
c – usuń informacje debugerskie
s – linkuj statycznie
TEMP – ścieżka do pliku tymczasowego
LIST – tworzenie pliku mapującego
INPUT – plik wejściowy
ENTRY – punkt wejścia
LIB – biblioteki
OUTPUT – plik wyjściowy
LOAD – początkowy adres programu
STACK – Adres stosu
Uruchomienie
LD-ONE
Wywołanie:
ld-one <plik_wykonywalny> <host_executable_filename>
przykład
ld-one hello cio
LD-NET
Wywołanie:
ld-net <plik_opisujący_sieć> <argumenty>
przykład
ld-net line
plik opisujący sieć ma rozszerzenie nif
Plik *.nif
1, prog1, R0, 0, 2, , ,
2, prog1, R1, 1, 3, , ,
3, prog2, R2, 2, 4,4 , ,
4, prog2, R2, 3, , 3, ,
2
2
2
2
0 T1 1
0 T1 1
0 T1 1
0 T1 1
3
3
3
3
Biblioteka CONC
Współbieżność w języku ansi C osiągnięta została za
pomocą funkcji bibliotecznych wykonujących następujące
operacje:
tworzenie, szeregowanie, uruchamianie procesów
● komunikacja za pośrednictwem kanałów
● wyboru gotowego wejścia
● semaforowe
●
Biblioteka CONC
Oraz 3 typy danych
Process – struktura opisująca każdy zadeklarowany
proces <process.h>
● Channel – wskaźnik danej typu void wykorzystywany
do tworzenia kanału <channel.h>
● Semaphor – struktura opisująca informacje o
semforach <semaphor.h>
●
Proces
Procesy są zdefiniowane tak samo jak zwykłe funkcje
języka C, z tym że wymagają stałego, pierwszego
parametru. Musi nim być jej własny wskaźnik typu
process.
#include <process.h>
void proc(Process *p, int a , int b)
{
p=p; /* tylko po to by nie było komunikatu
„unused pointer...” */
/* tresc funkcji */
}
Proces
Przed pierwszym wywołaniem procesu musi nastąpić
przydział pamięci oraz zainicjowanie struktury Process.
● Dokonuje się tego za pomocą funkcji ProcAlloc
● Procesy mogą działać synchronicznie lub
asynchronicznie
● Te drugie działają nie zależnie od procesu który je
wywołał i mogą działać nawet po zakończeniu
rodziców.
● W trybie synchronicznym sterowanie powraca do
rodzica w momencie zakończenia wszystkich
procesów z wywołanej grupy
● Istnieje specjalny kanał przeznaczony do informowania
o stanie procesów
● Stan procesów można zmieniać tyko za pośrednictwem
kanałów.
●
Proces
●
Przydział pamięci i inicjacja odbywa się za pomocą
Process *ProcAlloc(int (*func)(),int sp, int nparam,...);
func - wskaźnik funkcji o wyniku int, z której zostanie
utworzony równoległy proces,
sp - rozmiar stosu w bajtach 4KB dla 32 bitowego i
1KB dla 16b.
nparam – liczba słów wymaganych do przekazania
parametrów funkcji;
W przypadku niepowodzenia wartość zwrotna = NULL
W przypadku sukcesu zwracany jest wskaźnik do
struktury Process
Proces
Jeżeli chcemy wykorzystać już przydzieloną pamięć i
chcemy tyko zainicjować to wołamy funkcję ProcInit.
#include <con.h>
ProcInit(p, func, ws, wssize, nparam, p1,
p2, ..., pn)
Process *p;
int (*func)();
char *ws;
int wssize;
int nparam;
parametry jej podobne ale ma jeszcze wskaźnik do
procesu p oraz wskaźnik stosu ws i rozmiar wssize
Proces
#include <process.h>
#include <stdlib.h>
#define SIZE 4096
Process *p;
char *ws;
int nparam=2;
if ((p=Process *)malloc(sizeof(Process)))
== NULL) abort();
if (( ws = (int*)malloc(SIZE))==NULL)
abort();
if (ProcInit(p,proc,ws,SIZE,nparam,1,2))
abort();
gdy funkcja się uda zwracane jest 0
Proces
Do zmiany parametrów procesu z zarezerwowaną
pamięcią służy ProcParam
#include <process.h>
void ProcParam(Process *p,...);
liczba parametrów określonych ma być taka sama jak
podana w deklaracji procesu.
Proces
Struktura Process wygląda następująco:
typedef struct
{
Process *link; /* lista procesów */
int wsret; /* położenie końca obszaru roboczego */
int *wbase; /* wskaźnik stosu */
int wsize; /* rozmiar stosu */
int *wp; /* wskaźnik początku obszaru roboczego */
int psize; /*liczba słów zajmowanych przez
parametry */
void (*func)(); /* wskaźnik funkcji (process) */
} Process;
Proces
Zwalnianie pamięci
void ProcAllocCean(Process *p);
void ProcInitClean(Process *p);
proces zainicjowany przez ProcAlloc musi być zwalniany
tylko przez ProcAllocClean
proces zainicjowany przez ProcInit musi być zwalniany
tylko przez ProcInitClean a następnie przez np. zwykłe
free jak był przydzielany przez malloc.
Proces
Wywoływanie procesów
void ProcPar(Process *p1, Process
*p2,...,NULL);
void ProcParList(Process **plist);
Obie te funkcje służą do wywoływania procesów
synchronicznie
void ProcPriPar(Process *phigh, Process
*plow);
wywołuję parę procesów pierwszy o priorytecie wysokim
drugi o priorytecie niskim
Proces
void ProcRun(Process *p); uruchamia proces
asynchronicznie z priorytetem taki jak rodzic
void ProcRunHigh(Process *p); uruchamia proces
asynchronicznie z priorytetem wyższym
void ProcRunLow(Process *p); uruchamia proces
asynchronicznie z priorytetem niższym
Proces
Zarządzanie procesami
void ProcAfter(int czas); opóźnienie w
wykonywaniu do czasu nadejścia chwili określonej
ilością kwantów czasu wskazywaną przez rejestr
Timer0 lub Timer1 czyli (1uS lub 64uS)
void ProcWait(int czas); Zawieszenie procesu na
zadany ilością kwantów czas.
void ProcReschedule(void); Przesunięcie
aktywnego procesu na koniec kolejki procesów.
Umożliwia to reazlicację operacji aktywnego
czekania.
Proces
void ProcStop(void); Zatrzymuje bieżący proces
przez ciągłe ustawianie go na końcu kolejki.
Zatrzymany w ten sposób proces nie może zostać
wznowiony.
int ProcGetPriority(void); zwraca priorytet
procesu, PROC_HIGH = 1 lub PROC_LOW = 0.
Proces
Pomiar czasu
int ProcTime(); służy do określania czasu, zwraca
bieżącą wartość zegara transputera
int ProcTimePlus(const int czas1, const int
czas2);
int ProcTimeMinus(const int czas1, const int
czas2);
Funkcje te zwracają wynik dodawania i różnicy modulo
dla argumentów wyznaczanych z funkcji ProcTime();
int ProcTimeAfter(const int czas1, const int
czas2); zwraca 1 gdy moment czas1 nastąpił po czas2 lub 0
w przeciwnym razie.
Kanały
Przykład:
#include <channel.h>
#include <stdlib.h>
Channel c1, *c2;
ChanInit(&c1);
ChanInit(c2=(channel*)malloc(sizeof(channel)));
Kanały
Wejście wyjście przez kanał
void ChanOut(Channel *ch, void *cp, int count);
zapisuje do kanału ch dane z cp o ilości count
void ChanIn(Channel *ch, void *cp, int count );
Odczytuje dane z kanału dane do cp o ilości count.
Kanały
void ChanOutChar(Channel *ch, char c);
zapisuje do kanału ch znak c.
char ChaninChar(Channel *ch);
odczytuje znak z kanału ch i zwraca jako wartość;
void ChanOutInt(Channel *ch, int n);
zapisuje wartość int n do kanału ch
int ChanInInt(Channel *ch);
odczytuje wartość int z kanału ch i zwraca jako wartość.
Kanały
Adresy kanałów są zdefiniowane następująco:
#define
#define
#define
#define
#define
#define
#define
#define
LINK0OUT
LINK1OUT
LINK2OUT
LINK3OUT
LINK0IN
LINK1IN
LINK2IN
LINK3IN
((Channel*)
((Channel*)
((Channel*)
((Channel*)
((Channel*)
((Channel*)
((Channel*)
((Channel*)
0x80000000)
0x80000004)
0x80000008)
0x8000000C)
0x80000010)
0x80000014)
0x80000018)
0x8000001C)
Kanały
#include
#include
#include
#include
#include
<stdio.h>
<string.h>
<stdlib.h>
<process.h>
<channel.h>
void we(Process *p, Channel *kan)
{
char inf[21];
p=p;
printf(„\nWprowadz tekst (maks 20 znakow):”);
gets(inf);
ChanOut(kan,inf,21);
}
void wysw(Process *p,Channel *kan)
{
char nazwa[21];
p=p;
ChanIn(kan,nazwa,21);
printf(„\n%s”,nazwa);
}
Kanały
int main()
{
process *p1, *p2;
Channel *kan;
if ((kan=ChanAlloc())) == NULL)
exit(1);
if ((p1=ProcAlloc(we,1024,1,kan)) == NULL)
exit(1);
if ((p2=ProcAlloc(wysw,1024,1,kan)) == NULL)
exit(1);
ProcPar(p1,p2,NULL);
exit(0);
}
Niezawodne protokoły
Omówione wcześniej funkcje nie gwarantują
rozpoznania uszkodzenia linku.
Osiągnięto to przez parametr time-out w grupie
funkcji TimeFail, lub specjalnego dodatkowego kanału
monitorującego obsługiwanego przez grupę ChanFail.
Niezawodne protokoły
int ChanOutTimeFail(Channel *ch, void *cp, int count,
int time);
Jeżeli w czasie time nie uda się wysłać danych zwróć błąd
int ChanOutChanFail(Channel *ch, void *cp, int count,
Channel *failc);
Jeżeli w czasie time nie uda się wysłać danych wyślij do kanału
failc
int ChanInFail(Channel *ch, void *cp, int count, int
time);
Jeżeli w czasie time nie uda się odebrać danych zwróć błąd
int ChanInChanFail(Channel *ch, void *cp, int count,
Channel *failc);
Jeżeli w czasie time nie uda się odebrać danych wyślij do
kanału failc.
Niezawodne protokoły
cp to wskaźnik obszaru gdzie dane zostaną umieszczone
bądź skąd zostaną pobrane.
● ilość danych określa count
● time określa czas po którego upływie w razie braku
komunikacji zostaje wykonywane przerwanie.
● failc jest wskaźnikiem kanału z procesu monitorującego
sprawność połączenia przez link.
● wartość 0 zwracana przez powyższe funkcje to pomyślne
zakończenie w przeciwnym wypadku –1.
●
Wybór gotowego wejścia
intProcAlt(Channel *c1,Channel
*c2,...,NULL);
intProcAltList(Channel **chlist);
Służą do wybrania z podanej listy kanału gotowego do
czytania.
● Zawieszają dany proces do momentu aż któryś z kanałów
nie będzie gotowy.
● Jak już będzie to zwracany jest indeks z podanej listy
kanałów licząc od 0.
●
Wybór gotowego wejścia
int ProcSkipAlt(Channel *c1, Channel
*c2,...,NULL);
int ProcSkipAltList(Channel **chanlist);
●
Podobnie jak poprzednie z tym że tu nie czekają, jak nie
ma to zwracane jest –1.
int ProcTimerAlt(int timer, Channel *c1,
Channel *c2,...,NULL);
int ProcTimerAltList(int timer, Channel
**chanlist);
●
Tu czekają tylko określoną ilość czasu jak nie to
zwracana jest -1
Semafory
Mechanizm semaforów gwarantuje niepodzielny dostęp do
zasobów. Rozwiązanie zaproponowane przez
Holenderskiego matematyka Dijkstrę w 1965 r.
Definicja typu semaforowego
Semafor jest zmienną całkowitoliczbową
● przyjmuje wartości nieujemne
● Jedyne operacje, za wyjątkiem inicjacji, to
● WAIT, P(holenderskie słowo passeren
przejść, proberen próbować), opuszczanie
● SIGNAL, V(vrijmaken – zwolnić, verhogen zwiększyć), podnoszenie.
● Operacje te są nie podzielne.
● Inicjacja jest dokonywana poza procesami,
które z niego korzystają
● Na semaforach nie wykonujemy operacji
sprawdzania.
●
Semafor ogólny
P(S): jeśli S>0 to S:= S-1, w przeciwnym
przypadku wstrzymaj działanie procesu
wykonującego tę operację.
V(S): jeśli są procesy wstrzymane w wyniku P(S),
to wznów jeden z nich, w przeciwnym przypadku
S:=S+1.
Operacje Semaforowe
Rozpoczęcie wykonywania operacji semaforowych musi być
poprzedzone deklaracją i inicjowaniem semafora przez jedną z
dwóch dostępnych po dołączeniu zbioru semaphor.h funkcji:
Semaphore *SemAlloc(int wartosc);
zainicjuje i przydzieli pamięć
void SemInit(Semaphore *sem,int wartosc);
tylko zainicjuje więc pamięć przydzielona już musi być.
Operacje Semaforowe
void SemWait(Semaphore *sem);
(P) opuszczenie semafora
void SemSignal(Semaphore *sem);
(V) podniesienie semafora
Operacje semaforowe
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<process.h>
<semaphor.h>
double x=10;
Semaphore *sek_kryt, *sek_gl;
void proc_1(Process *p)
{
SemWait(sek_kryt);
p=p;
x=2*x;
printf(„%g”,x);
SemSignal(sek_kryt);
SemSignal(sek_gl);
}
int main(void)
{
Process *p1;
sek_kryt = SemAlloc(1);
sek_gl=SemAlloc(0);
if ((p1=ProcAlloc(proc_1,0,0)
== NULL)
exit(1);
ProcRun(p1);
Sem(Wait(sek_gl);
exit(0);
}