Aufgabe 1 - Homepage von Clemens Mayer

Werbung
Aufgabe 1: Petri-Netze
Geben Sie für die beiden angegebenen Petri-Netze jeweils an, ob sie deadlock-frei sind und begründen Sie Ihre Antwort.
a)
b)
Aufgabe 2: Socket-Kommunikation
Gegeben sei ein Client, der mit Hilfe der Socket-Kommunikation mit UDPProtokoll eine mit 0 beendete Folge von int-Zahlen an den Port 7777 eines
Servers schickt.
Schreiben Sie den UDP-Server, der in einer Schleife alle Zahlen (bis zum
Wert 0) einliest, dabei deren Summe berechnet und dann das Ergebnis an den Client
zurückschickt.
include- Anweisungen können Sie dabei weglassen und auf das Abfangen von
Fehlersituationen können Sie verzichten.
1
Aufgabe 3: RPC-Programmierung in C++
In dieser Aufgabe geht es um ein Client-Server System, bei dem der Server
dem Client folgende Operationen in RNS-Arithmetik zur Verfügung stellt.
base:
assign:
add:
mult:
incr:
Festlegung der RNS-Basis durch einen Vektor (m0,m1,…,m9)
Zuweisung an einen Akkumulator: accu=(X0,X1,…,x9)
RNS-Addition: accu+=(X0,X1,…,x9)
RNS-Multiplikation: accu*=(X0,X1,…,X9)
RNS-Incrementieren: accu+=1
Für die RNS-Zahldarstellung soll folgender Typ feld verwendet werden.
const rns_dimension=10;
struct feld { int v[rns_dimension]; };
Um eine echte Nebenläufigkeit zwischen Client und Server zu erreichen, sollen
die RPC-Prozeduren für die obigen Operationen keine Ergebnisse an den Client zurückliefern, sondern sie sollen sich nach der Parameterübergabe sofort
beenden und die eigentliche Abarbeitung einem parallel laufenden Berechnungsthread überlassen.
Zur Rückübertragung der erzielten Ergebnisses soll eine RPC-Funktion get
benutzt werden, die kein Argument besitzt, die Beendigung der bisher veranlassten Berechnungen abwartet und dann den Inhalt des Akkumulators akku
zurückgibt.
Zur Illustration ist in Tabelle 1 ein Ausschnitt aus einem Client-Programm
angegeben. Die darin aufgerufenen RPC-Prozeduren start und stop dienen
lediglich dazu, die im Server benötigte Threads und Semaphoren zu erzeugen,
bzw. zu löschen.
In den Tabellen 2 und 3 ist die Server-Implementierung ausschnittsweise angegeben. Die RPC-Routinen für base, assign, add, mult und incr tragen
mit der Hilfsprozedur command_to_queue (und einem Thread queue_thread)
jeweils den Operator command und gegebenenfalls das Argument parameter
in eine Warteschlange queue der Länge 20 ein. Diese Warteschlage wird durch
ein zyklisch verwendetes Feld implementiert. Die Verfügbarkeit freier Einträge wird über einen Semaphore empty_queue_cells verwaltet und die Anzahl
bereits gefüllter Einträge wird durch den Semaphore full_queue_cells dargestellt.
Für die Abarbeitung der in der Warteschlange gespeicherten Kommandos
ist der Thread computation_thread zuständig. Er wartet in einer Schleife
auf die Verfügbarkeit eines queue-Elements, führt dann die entsprechende
Berechnung durch und gibt das queue-Element wieder frei.
2
int main (int arge, char* argvD)
{CLIENT* cl=clnt_create(argv[l], RNSPROG, RNSVERS,
feld
feld
feld
feld
"TCP");
m={l7, 19, 23, 29, 31, 37, 41, 43, 47, 53}; // RNS-Basis
a={10, 10, 12, 12, 8, 3, 0, 0, 0, 0};
b={13, 14, 15, 16, 17, 18, 19, 20, 21, 22};
c={ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
feld* y;
void* result;
result= start_l(NULL, cl);
result= base_l(fcni, cl);
// Server aktivieren
// RNS-Basis festlegen
// Rechnung: y = (a+b)*c +1 in RNS-Arithmetik
result= assign_1(&a, cl);
result= add_1(&b, cl);
result= mult_1(&c, cl);
result= incr_1(NULL,cl);
y= get_1(NULL,cl);
cout << "y=";
// Ergebnisausgabe
for (int i=0; i< rns_dimension; i++)
cout << (*y).v[i] << “ “;
cout << endl;
result= stop_1(NULL, cl);
// Server deaktivieren }
Tabelle 1: Ausschnitt aus einem Client-Program, zum RNS-Server
a) Schreiben Sie die Datei rns.x für das Client-Server-System.
b) Beschreiben Sie das Verhalten des in Tabelle 2 und 3 dargestellten
Servers durch ein Petri-Netz. Das Petri-Netz sollte "Client-Anfragen",
RPC-Routinen, die Threads queue_thread
und computation_thread,
sowie alle verwendeten Semaphoren beinhalten.
c) Geben Sie eine Implementierung der Server-Routine start_1_svc an,
die sämtliche benötigen Semaphoren initialisiert und die Threads star
tet.
d) Geben Sie eine Implementierung der Server-Routine stop_1_svc an,
die invers zu start_1_svc arbeitet.
e) Schreiben Sie die Server-Routine get_1_svc, die den Inhalt der glo
balen Variablen accu an den Client zurückliefert. Dazu muss zunächst
auf die Abarbeitung aller Befehle aus der queue gewartet werden.
3
// RNS-Server
...
#indude "rns.h"
typedef void* zeiger;
int base[ras.dimension];
// Basis-Vektor für RNS-Arithmetik
feld accu;
// RNS-Arbeitsregister
// Aufzählungstyp für Kommandos, die im Hintergrund bearbeitet werden
enum cmd {base.cmd, assign_cmd, add_cmd, mult.cmd, incr_cmd, decr.cmd};
struct commands {cmd c; int v[rns_dimension];};
const int queue_size=20;
commands queue [queue_size];
int read_cmd=0;
int write_cmd=O;
//
//
//
//
Größe der queue für Kommandos
Puffer für Kommandos
Index zum Lesen aus queue
Index zum Schreiben in queue
cmd command; int parameter[rns.dimension]; int
ok=0;
// Threads
pthread_t computation_thread;
pthread_t queue_thread;
// Berechnungsthread
// Kommandos in queue eintragen
// Semaphoren
sem_t insert_cmd, insert_cmd_done;
sem_t empty_queue_cells, full_queue_cells;
void command_to_queue(cmd c, int* v)
{command=c;
if (v)
for (int i=0; i<rns_dimension;
parameter [i]= v[i];
sem_post(&insert_cmd);
sem_wait(&insert_cmd_done);
}
// Hilfsprozedur
i++)
Tabelle 2: Globale Deklarationen und Prozedur command_to_queue des RNS-Servers
4
// RPC-Prozeduren
void* base_1_svc(feld *p, svc_req* cl)
{command_to_queue(base_cmd,(*p).v); return &ok; }
void* assign_1_svc(feld *p, svc_req* cl)
{command_to_queue(assign_cmd,(*p).v); return &ok; }
void* add_1_svc(feld *p, svc_req* cl)
{command_to_queue(add_cmd,(*p).v); return &ok; }
void* mult_1_svc(feld *p, svc_req* cl)
{command_to_queue(mult_cmd,(*p).v); return &ok; }
void* incr_1_svc(void *p, svc_req* cl)
{command_to_queue(incr_cmd,NULL); return &ok; }
// Vom Thread "command_thread" auszuführende Prozedur
void* f_computation(void* p)
{while(1)
{sem_wait(&full_queue_cells); // Warten auf Kommando in queue[read_cmd]
cmd c=queue[read_cmd].c;
// Auszuführendes Kommando
if (c==base_cmd)
for (int i=0; i<rns_dimension; i++)
base[i] =queue[read_cmd].v[i];
else
...
read_cmd=(read_cmd+1)%queue_size; // Element in queue wieder freigeben
sem_post(&empty_queue_cells);
}
}
// Vom Thread "queue_thread" auszuführende Prozedur
void* f_command_to_queue(void* p)
{while(1)
{sem_wait(&insert_cmd);
// Warten auf Vorliegen eines Kommandos
sem_wait(&empty_queue_cells); // Warten auf freies Feld in queue
queue[write_cmd].c=command;
// Kommando in queue eintragen
for (int i=0; i<rns_dimension; i++)
// Argument in queue eintragen
queue[write_cmd].v[i]=parameter[i]; write_cmd=(write_cmd+l)*/.queue_size;
sem_post(&insert_cmd_done) ;
// Eintrag wurde vorgenommen
sem_post(&full_queue_cells);
// neues Kommando in queue
}
}
Tabelle 3: Programmteile des RNS-Servers
5
Aufgabe 4: Threads
Um eine herkömmliche for-Schleife der Art
for (int i=0; i<10; i++)
y[i]= f(x[i]);
mit einer rechenzeitintensiven Funktion int f (int) nebenläufig statt se
quentiell durchzuführen, kann man wie im folgenden dargestellt, 10 separate
Berechnungsthreads verwenden:
void* fthread(void* x)
{ (int local_x=(int) (*x);
int y=f(local_x);
pthread_exit(&y);
}
...
main()
{...
pthread p[10];
int param;
int* result;
for (int i=0; i<10; i++)
{
param=x[i];
pthread_create(&(pthread[i]), NULL, fthread, ftparam);
}
for (int i=0; i<10; i++)
{
pthread_join(pthread[i],
(void**)(&result));
pthread_detach(pthread[i]);
y[i]= *result;
}
...
a) Erklären Sie kurz, warum man auch auf einem Rechner mit nur einer
CPU eine Chance hat, dass die pseudo-parallele Ausführung schneller
ist, als die sequentielle Schleifenabarbeitung.
b) Nennen Sie einige Gründe, warum die pseudo-parallele Ausführung
eventuell langsamer ist, als die sequentielle Ausführung der for-Schleife.
c) Im obigen nebenläufigen Programm gibt es Fehler, die zu falschen Er
gebnissen führen können. Erklären Sie kurz, was falsch gemacht wurde.
6
Aufgabe 5: RNS-Arithmetik
Für die folgenden Prägen soll jeweils die RNS-Basis (5,9,11) verwendet werden.
a) Welcher Zahlbereich kann mit der angegebenen RNS-Basis dargestellt
werden?
b) Stellen Sie die Werte 10 und 20 in RNS-Notation dar und berechnen Sie
dann im RNS-System das Produkt (10 • 20). Sie brauchen das Ergebnis
nicht zurücktransformieren.
c) Die Zahl 2 hat die RNS-Darstellung (2,2,2). Um im RNS-Zahlsystem
durch 2 zu dividieren, braucht man ein "inverses Element" (x1,x2,x3),
so dass die RNS-Multiplikation ((x1,x2,x3)-(2,2,2)) das Ergebnis (1,1,1)
liefert. Ermitteln Sie dieses "inverse Element". Demonstrieren Sie die
Richtigkeit anschließend anhand der Division 50/2 in RNS-Arithmetik.
Aufgabe 6: Allgemeines
a) Der fork-Befehl kann in C und C++ benutzt werden, um Prozesse zu
erzeugen. Welche Bedeutung hat sein Rückgabewert?
b) Was ist bei der Socket-Kommunikation der wesentliche Unterschied zwi
schen dem UDP- und dem TCP-Protokoll
c) Wann spricht man bei der nebenläufigen Programmierung von "kriti
schen Bereichen"?
d) Welche Bedeutung hat das Kommando rmiregistry bei der RMIProgrammierung?
e) Welche Bedeutung hat das Kommando rpcgen bei der RPC-Programmierung?
Aufgabe 7: JAVA-RMI
Tabelle 4 zeigt einen in JAVA geschriebenen RMI-Server. Seine Schnittstellenbeschreibung stellt nur eine Methode int getO zur Verfügung und lau
tet:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface DelphiInterface extends Remote
{int getO throvs RemoteException;
}
Geben Sie an, welche Ausgabe der in Tabelle 5 dargestellte Client erzeugt.
7
import
import
import
import
import
java.rmi.RemoteException;
java.rmi.Server.UnicastRemoteObject;
java.rmi.RMISe curityManager;
java.rmi.Naming;
Delphiinterface;
public class DelphiServer extends UnicastRemoteObject implements
DelphiInterface
{ int a,b;
DelphiServer() throws RemoteException {
super();
a=1;
b=1;
}
public int get() throws RemoteException {
int c=a+b;
a=b;
b=c;
return c;
}
public static void main(String args[]) {
try {
System.setSecurityManager(new RMISecurityManager());
DelphiServer server=new DelphiServer();
Naming.rebind("Delphi-Server", server);
}
catch (java.net.MalformedURLException me) {
System.out .println("Malformed URL :" + me.toString());
}
catch (RemoteException re) {
System.out.println("RemoteException :" + re.toString());
}
}
}
Tabelle 4: JAVA-Implementeirung eines RMI-Servers
8
import
import
import
import
import
import
java.rmi.Remote;
java.rmi.RemoteException;
java.rmi.RMISecurityManager;
java.rmi.Naming;
java.rmi.NotBoundException;
java.net.MalformedURLException;
import DelphiInterface;
public class DelphiClient /* extends Remote */
{
public static void main(String argv[])
{
if (System.getSecurityManager()=null)
System.setSecurityManager(new RMISecurityManager());
try {
String url= "//localhost/Delphi-Server";
DelphiInterface Delphi1= (DelphiInterface)Naming.lookup(url);
for (int i=1; i<5; i++)
System.out.println(Delphi1.get());
DelphiInterface Delphi2= (DelphiInterface)Naming.lookup(url);
for (int i=1; i<5; i++)
System.out.println(Delphi2.get());
}
catch (java.rmi.NotBoundException exe) {
System.out.println("ERROR NotBoundException()" + exc.toString());
}
catch (java.net.MalformedURLException exe) {
System.out.println("ERROR MalformedURLException " + exc.toString());
}
catch (java.rmi.RemoteException exe) {
System.out.println("ERROR RemoteException " + exc.toString());
}
}
}
Tabelle 5: JAVA Client-Implementierung
9
Herunterladen