Klausur Juli 2013

Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.

Klausur Juli 2013
So, da sich noch keiner der letzten Klausur gewidmet hat, hier mein Lösungsvorschlag zu den SC/MC-Fragen:

EDIT - sollte jetzt alles richtig sein:

1.1
a) 4
b) 2
c) 3
d) 1
e) 1
f) 3
g) 3
h) 1
i) 1
j) 2
k) 4

1.2
a) 2+5+6+7+8
b) 1+2+4+5

Anregungen/Verbesserungsvorschläge erwünscht, da ich mir insbesondere bei den MC Fragen nicht ganz sicher bin.


1.1
a) 4
b) 1
c) 3
d) 1
e) 1
f) 3
g) -
h) 1
i) 1
j) 2
k) 4

1.2
a) 5,6,7,8
b) blub (jetzt weiß ich was ich noch lernen muss :-))

Aufgabe 2 im Anhang

Attachment:
tesa.c: https://fsi.cs.fau.de/unb-attachments/post_127914/tesa.c

1 Like

4 ist falsch, [m]exit(2)[/m] überführt den Prozess in den Zustand beendet. 1 ist richtig, ein Prozess kann sich nur selbst blockieren (z.B. durch Disk-I/O, Zugriff auf ausgelagerten Speicher (Pagefault), etc.).

4 ist falsch. Für den Scheduler ist es nicht intransparent, der kann ja den Prozess beliebig schedulen ohne dass eine Prioritätsverletzung auftritt. 1 ist richtig, CAS geht nicht in C99 (wird haben ja das GCC-Builtin benötigt).

2 stimmt, nur die Rechte des Verzeichnis sind relevant um eine Datei zu löschen.
3 ist falsch, sobald die Datei weg ist, kannst du keinen Hardlink mehr anlegen (denn dann war der Hardlink-Count ja schon 0 und das Dateisystem hat die Inode inklusive Dateiinhalt entfernt).
6 stimmt auch. „foo“ und „bar/foo“ (zweimal Dateiname „foo“) ist natürlich möglich.

2 stimmt auch, z.B. beim Schreiben in eine read-only Seite gibt es trotzdem einen Page-Fault.
6 stimmt nicht: ab | cd (wobei ab und cd Buddies sind), b und c kannst du jedoch nicht verschmelzen.

Bitte korrigiere deine Lösungen, damit zukünftige Leser des Threads es einfacher haben.

2 Likes

Wo sind die Aufgaben für diese Klausur?
Hier ist die letzte Klausur für 02.2012 https://www4.cs.fau.de/Lehre/SS12/V_SP2/Pruefung/klausuren.shtml


Wenn du ein aktuelles Semester in deine URL packst bekommst du auch aktuelle Klausuren.

Also hier: https://www4.cs.fau.de/Lehre/WS13/V_SP2/Pruefung/klausuren.shtml

1 Like

Vielen Dank kronos für deine Lösung zur tesa :slight_smile:
Kannst du mir erklären, warum bei [m]kill(0, SIGTERM)[/m] bzw. [m]kill(0, SIGKILL)[/m] genau alle Kinder sterben? Das Hauptprogramm soll ja weiterlaufen.
Ich hab’s mir versucht so zu erklären, dass bei fork() 0 als pid für das Kind zurückgegeben wird. Hat das was damit zu tun?

Die runTestsuite-Methode sollte man am Ende um ein printSummary ergänzen oder? :wink:

// Signalbehandlung SIGALRM
static void steven(int signum) {
	if(childProcessCounter <= 0) {
		return;
	}	
	
	kill(0, SIGKILL);
}

// Signalbehandlung SIGINT
static void nsa(int signum) {
	kill(0, SIGTERM);
	alarm(TESTCASE_TIMEOUT);
}

Allgemeine Frage: Ist es richtig, dass wenn man eine Signalbehandlungsfunktion besipielsweise für SIGALRM setzt und dann fork() aufruft, sowohl Vater als auch Kind zur Signalbehandlung kommen könnten, jedoch letztendlich nur der schnellere es tut?
Beispiel:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>

static void die(const char* message) {
	perror(message);
	exit(EXIT_FAILURE);
}

static void sigAlrmHandler(int signum) {
	printf("Alarm was received by %d\n", getpid());
}

static void sigChldHandler(int signum) {
	printf("Child died.\n");
}

int main(int argc, char* argv[]) {

	// Apply signal handling
	struct sigaction action;
	if(-1 == sigemptyset(&action.sa_mask))
		die("sigemptyset");
	action.sa_flags = SA_RESTART;
	action.sa_handler = sigAlrmHandler;
	if (-1 == sigaction(SIGALRM, &action, NULL))
		die("sigaction");
		
	struct sigaction childHandler;
	if(-1 == sigemptyset(&childHandler.sa_mask))
		die("sigemptyset");
	childHandler.sa_flags = SA_RESTART | SA_NOCLDSTOP | SA_NOCLDWAIT;
	childHandler.sa_handler = sigChldHandler;
	if (-1 == sigaction(SIGCHLD, &childHandler, NULL))
		die("sigaction");
		
	pid_t pid = fork();
	switch(pid) {
		case -1:
			die("fork");
		case 0:
			sleep(1);
			sleep(1);
			break;
		default:
			alarm(1);
			printf("Parent called alarm(1). Child's id is %d\n", pid);
			printf("Wait until the child has died and then press enter to leave...\n");
			char c = getchar();
			break;
	}
	
	return EXIT_SUCCESS;
}

Als Output habe ich bisher immer bekommen, dass der Vater die Signalbehandlung durchlaufen hat. Das Kind nie, obwohl es zu dem Zeitpunkt noch lebt.


Nein, hat nichts damit zu tun. Zitat aus [m]man 2 kill[/m]:

In [m]man 2 fork[/m] steht allerdings:

Ich vermute also, dass das so nicht funktioniert, sondern einfach der Prozess stirbt, den das Signal erreicht. (Was immer der Vater sein sollte, da die Kinder ja andere PGIDs haben.)

Alles ohne Gewähr. Ich hab’s nur schnell nachgeschaut und das klingt relativ logisch für mich. :wink:


Ich war der Ansicht, dass das funktionieren sollte :slight_smile:
Ich habs mal auf eine Schleife geändert …

Die printSummary stimmt nicht mit der Signatur auf dem Aufgabenblatt überein. Wozu soll die testsuite ein Array sein?

Attachment:
tesa.c: https://fsi.cs.fau.de/unb-attachments/post_128071/tesa.c


Richtig.

„process group“ != „process ID“. Prozessgruppen sind etwas anderes, für diese Konzepte fehlt aber in SP die Zeit. [m]man setpgid[/m] und [m]man tcsetpgrp[/m] für mehr Informationen. Prozessgruppen erlauben es (unter anderem) mehrere Prozesse zusammenzufassen und Signale an alle Prozesse innerhalb eine Gruppe zu schicken. Das ist z.B. für Pipes („grep foo | sort | tail“) praktisch. Man will ja alle Prozesse beenden, wenn man Ctrl-C drückt. Auch für Hintergrund-Prozess ist das wichtig.


Nein. Nur der Prozess, der das [m]alarm()[/m] aufruft, bekommt das [m]SIGALRM[/m].

Du brauchst keine Fehlerbehandlung für [m]sigemptyset(3)[/m]. Bei [m]sigaction(3)[/m], brauchst du ebenfalls keine, wenn die Parameter fest sind (wie hier).


Okay, danke.
Heißt also [m]kill(0, SIGKILL)[/m] funktioniert nicht, solange man nicht in allen Kindern [m]setpgid()[/m] aufruft?


Nein, die Sache mit den Prozessgruppen funktioniert nochmal ein bisschen anders.

Was bei dieser Aufgabe gedacht ist: Iteriere über das Array von Testfall-Strukturen, das zu der Testsuite gehört, und sende an jede der gespeicherten PIDs das Signal (sofern dieser Testfall nicht schon terminiert ist).


Stimmt hier tatsaechlich die 8?
Welche zwei hardlinks wuerden denn auf die root-Directory(„/“) verweisen?


. und …


Das kannst du auch leicht selbst nachschauen: [m]ls -lai / | head[/m] und [m]ls -lai /etc | head[/m], unter der Annahme, dass [m]/etc[/m] im selben Dateisystem wie [m]/[/m] ist ([m]-i[/m] zeigt die Inode an).


Zur Aufgabe 4:
Wir haben fuer 4 a und 4 b eine Loesung zusammengestellt, wissen aber nicht wie wir bei der 4 c herangehen sollen.

Unsere Antwort:

  • Auswirkung des Verfahrens auf nur einen Prozess (einseitige S.) oder mehrere Prozesse (mehrseitige S.)
  • Reihenfolge der Prozesse ist klar (einseitige S.) oder unvorhersehbar (mehrseitige S.)

Unsere Antwort:

  • Wiederverwendbare Betriebsmittel: Betriebsmittel, die von mehreren Prozessen genutzt werden koennen und dabei nicht aufgebraucht werden. Diese BM koennen nur begrenzt erstellt werden. Eine weitere Unterteilung dieser BM ist in {un,}teilbare BM.
  • CPU (HW)
  • Peripherie-Geraete (HW)
  • Speicher (HW)
  • Dateien (SW)
  • I/O-Buffer (SW)
  • Seitenrahmen (SW)
  • Deskriptoren (SW)
  • Konsumierbare Betriebsmittel: Betriebsmittel, die nur von einem Prozess genutzt werden koennen und dabei aufgebraucht werden. Diese BM koennen unendlich oft produziert werden.
  • Signale (HW)
  • Nachrichten (SW)
  • Signale (SW)

Bei der 4c ist unklar, was denn ein Zugriffsmuster ist. Ist dies nur die Reihenfolge von V/P Aktionen?

Und einseitige/mehrseitige Synchronisation wird ja dann wird ja anhand der Kategorie des Betriebsmittels festgestellt, ist also eine quasi Wiederholung der Frage in 4b?

Hat da jemand was dazu bzw. kann einen Tipp/eine Loesung vorschlagen?


Jain.
Man soll beides anhand jeweils eines Semaphoren-Beispiels erläutern.
simples, zusammengeschusterte Beispiele:

//einseitig, producer vs. consumer
void *magicResult;

ThreadA(){
  magicResult = createMagic();
  V();
}

ThreadB(){
  P();
  void *magic = magicResult;
}
//mehrseitig, gegenseitiger Ausschluss
ThreadA(){
  P();
  doMagic();
  V();
}

ThreadB(){
  P();
  doFancy();
  V();
}
1 Like

Ich hätte hier noch erwähnt, dass bei einseitiger Synchronisation eine binäre Semaphore benötigt wird und bei mehrseitiger eine zählende Semaphore.

Ich bin mir auch nicht ganz sicher, ob deine Antworten:

  • Reihenfolge ist klar und
  • Auswirkungen nur auf einen Prozess/mehrere Prozesse
    stimmen.

Wenn ich das richtig kapiert habe, dann geht es bei einseitiger Synchro doch darum, dass ein Thread/Prozess quasi „durchläuft“ und andere sich daran synchronisieren müssen.

Und bei mehrseitiger dann eben darum, dass es einen kritischen Abschnitt zu schützen gilt.

Ich bin mir aber wie gesagt selbst nicht ganz sicher, daher bitte Korrekturen / Bestätigungen :wink:
Danke!

4.c hätte ich ähnlich wie Cauca beantwortet.


Genau :slight_smile: Hier ist das Beispiel aus der Vorlesung (Folien 23 + 24).

1 Like