Klausur Systemprogrammierung1, März 2005

paar Fragen :wink:

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 Systemprogrammierung1, März 2005
jo hi,
hab mir die Klausur mal angeschaut, und hätte n paar Fragen dazu. Wäre nett, wenn mir da jemand von euch weiterhelfen könnte :wink:

Multiple Choice Teil:
hier mal meine(manchmal geratenen → ?) Antworten. Wenn da was falsches dabei ist, bitte schreibt nicht nur die richtige Antwort, sondern auch waurm das die richtige Antwort ist :wink:

a)3?
b)2
c)3
d)1
e)3
f)3
g)1?
h)4
i)2, obwohl bei 3 der prozess doch auch nicht in im bereit-Zustand sein sollte oder ?
j)3
k)2
l)3
m)3
n)1

dann noch zur Programmieraufgabe:
ich schilder hier mal so grob, wie ichs machen würde, verbessert mich bitte, wenn euch was nicht passt :wink: :
also in der Main erstmal wieder einen Socket erstellen, binden, dann listen, und dann in der serverschleife accept ausführen.
nach dem accept einen Kindprozess mit fork erzeugen… jetzt hab ich zwei Fragen:
1)im kindprozess muss ja dann die serveFunktion ausgeführt werden. in der Übung mussten wir mit execvp dann das auszuführende Programm in den neu erzeugten Prozess reinladen. Muss man das hier auch machen ? wenn ja, wie sieht dann der aufruf dazu aus ?
2) was mache ich im Vaterprozess ? Der soll ja neue Verbindungen annehmen. mach ich dann sowas wie continue? damit wäre ich dann wieder am anfang der Schleife und das nächste accept würde ausgeführt werden.

so dann in der serve-Funktion den socket öffnen, Zeilenweise die Anfragen lesen und die dann mit der prattr-Funktion bearbeiten.
hier hätte ich auch ne Frage: wenn ich in der serve-Funktion im Fehlerfall exit aufrufe, schieße ich damit meinen ganzen Server ab, oder nur den Kindprozess (eigentlich nur den Kindprozess oder ?)

nagut, dann noch in der prattr-Funktion mit lstat den Status der Datei holen, und dann je nachdem was das jetzt für ein Dateityp ist entsprechende Ausgaben machen. Sollen diese Ausgaben eigentlich auf die Standardausgabe? Und wohin sollte die Ausgabe, für den Fall, dass die Datei nicht existiert ? stderr oder stdout ?

So und dann sollen die Zombies noch aufgeräumt werden. Ich hätte das mit waitpid gemacht, war mir aber nicht merh sicher wie waitpid genau funktioniert ;). Wenn das nen Zombieprozess findet, wird der dann automatisch aufgeräumt, oder muss ich das dann noch explizit machen?

soo das wars… wäre nett, wenn ihr mir da weiterhelfen könntet :slight_smile:
mfg

Aufgabe 1
Ohne Musterlösung und deshalb ohne Gewähr. :slight_smile:

Der Satz “Dabei können keine Daten verloren gehen” ist nicht korrekt. Es können zwar Inkonsistenzen in den Speicherungsstrukturen erkannt und ausgebügelt werden, aber dabei gehen unter Umständen Daten verloren, weil Änderungsoperationen auf der Platte nicht transaktionsbasiert sind wie bei Journaling-Dateisystemen. Das heißt es wird unter Umständen beim Reparieren des Dateisystems eine vergangene Schreiboperation teilweise wiederholt oder teilweise zurückgesetzt.
Richtig wäre meiner Meinung nach Antwort 2.

“Das System überprüft vor dem Freigeben?! von Betriebsmitteln, ob ein unsicherer Zustand eintreten würde.”
→ Korrekt müsste Antwort 3 sein.

Klingt gut. :slight_smile:

Darüber bin ich auch gestolpert. Meiner Meinung nach sind Aussage 2 und 3 beide falsch, denn wenn ein Seitenfehler bei einer ausgelagerten Seite auftritt, wird auch eine I/O-Operation angestoßen und somit der Prozess in den Zustand blockiert überführt.

Passt.

“(…) dass nur auf gültige Speicheradressen erfolgreich zugegriffen werden kann.” → Das ist nicht verkehrt.
Mehr Speicher zuzuordnen als physikalisch vorhanden ist funktioniert nur mit einem virtuellen Adressraum, deswegen ist Aussage 3 falsch.


danke für die antworten :wink:
zu i) hätte ich noch ne frage: ist hier jetzt antwort 2 richtig ?
und zu n) was grundlegendes zu Segmentierung: kann man mit Segmentierung nicht auch einen virtuellen Asressraum realisieren, wenn man die Segmente nochmal in Seiten unterteilt ?
achja: soll ich lieber für jede Klausur nen neuen thread aufmachen, oder alles hier reinschreiben ? wird denk ich mal ein bischen unübersichtlich…
ciao


schau mal ins wiki


hab ich, aber iwie find ich da nur links zu alten klausuren. gibts da evtl auch lösungen ?


https://fsi.informatik.uni-erlangen.de/dw/jahrgaenge/2006/loesungen/softwaresysteme_1/vordiplom#vordiplomsklausuren


Meiner unqualifizierten Meinung nach schon.

Kann man. Aber bei reiner Segmentierung funktioniert das nicht.

Lieber einen separaten Thread pro Klausur, sonst blickt spätestens nach fünf Beiträgen keiner mehr durch. :slight_smile:

Im Wiki finde ich für März 2005 nur die SoS1-Klausur, aber nicht die SP1-Klausur. Wobei SoS1 (trotz des Namens) für euch relevanter sein dürfte aus SP1.

Aufgabe 2

Nein. Ein [m]exec*()[/m] braucht man nur, wenn man ein anderes Programm starten will. In diesem Fall soll das laufende Binary “weiterverwendet” werden.
Leider gab es heuer keine Übungsaufgabe, in der ein [m]fork()[/m]-basiertes Serverprogramm zu implementieren war, deswegen kann ich verstehen, dass es diesbezüglich noch ein paar Unklarheiten gibt. Hoffentlich macht mein untiger Codeauschnitt klar, wie es funktioniert.

Richtig.
Was man außerdem nicht vergessen darf: Der Vaterprozess muss den Client-Socket, den er mit [m]accept()[/m] erhalten hat, wieder schließen. Sonst gehen ihm nach einer Weile die Dateideskriptoren aus, weil er sämtliche Client-Verbindungen unnötigerweise offen hält.
Ein [m]close()[/m] wirkt sich wohlgemerkt nur auf den aufrufenden Prozess aus. Der Kindprozess kann also noch mit dem Client-Socket weiterarbeiten.

Nur den Kindprozess.

Gute Frage. Eigentlich hätte ich erwartet, dass die Ausgabe zurück auf den Socket geschrieben werden soll, aber das ist bei dieser Aufgabe offenbar nicht verlangt. Also einfach normale Ausgabe nach [m]stdout[/m] und Fehlermeldungen nach [m]stderr[/m].
Wenn’s einem Spaß macht, kann man diese beiden Kanäle ja mit [m]dup2()[/m] auf den Socket umleiten. :slight_smile:

[m]waitpid()[/m] erledigt das Aufräumen. Es muss in einer Schleife aufgerufen werden, weil eventuell mehrere Zombies gleichzeitig vorliegen.

while (waitpid(-1, NULL, WNOHANG) > 0);

Diese Zeile kann entweder in der Hauptschleife des Programms oder in einem Signalhandler für [m]SIGCHLD[/m] platziert werden. Letzteres hat den Vorteil, dass alle Zombies sofort aufgeräumt werden und nicht erst nach dem Annehmen der nächsten Client-Verbindung.


Die Hauptschleife müsste in etwa so aussehen:

int listenSock; /* Socket, auf dem gelauscht wird */

/* ... */

/* Server-Schleife */
for (;;) {

    int   clientSock;
    pid_t pid;

    /* Verbindung annehmen */
    clientSock = accept(listenSock, NULL, NULL);
    if (clientSock == -1) die("accept()");

    /* Prozess zur Kommandoausfuehrung erzeugen */
    pid = fork();

    /* Fehler bei Prozesserzeugung */
    if (pid == -1) {

        die("fork()");

    /* Sohnprozess */
    } else if (pid == 0) {

        if (close(listenSock) == -1) die("close()");
        serve(clientSock);
        return EXIT_SUCCESS;

    /* Vaterprozess */
    } else {

        if (close(clientSock) == -1) die("close()");
    }
}

Mit einer kleinen Hilfsfunktion, die einem (nicht nur in der Klausur) das Leben stark vereinfachen kann.

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

Noch zwei kleine Anmerkungen zu meinem Code:

  • Das [m]return[/m] im Kindprozess ist verdammt wichtig! Falls man es vergisst, hat man eine wunderschöne Forkbombe. :smiley:
  • Ob man bei den [m]close()[/m]-Aufrufen eine Fehlerabfrage braucht, lässt sich diskutieren. In der Klausur sollte man aber auf Nummer sicher gehen.

close(2):


Ein [m]write()[/m] vor dem [m]close()[/m] kann es in dieser Situation allerdings nicht geben. :slight_smile:


jo danke schonmal :wink: ist schon einiges klarer


Könnte mir jemand die Aufgabe 4 erklären? Bin irgendwie ratlos was und wie ich das hinschreiben soll.

Grüße odj


Aktivitätsträger bedeutet dasselbe wie Kontrollfluss, Kontrollfaden, Thread, …
Ich schätze, bei der Aufgabe geht es um die Klassifizierung in schwer-, leicht- und federgewichtige Threads.


Shame on me! Hab nicht gesehn, dass es 2 Klausuren im März 05 gab, meinte die SOS1-Klausur mit der Semaphoren-Aufgabe.
Trotzdem vielen Dank Airhardt, mir wäre bei der Aufgabe eben so wenig klar gewesen, was zu tun ist.


Einen Semaphor haben wir doch im Wintersemester in der letzten Aufgabe programmiert (Folie U10.17).


in etwa so…


struct SEM
{
	pthread_mutex_t lockme;
	pthread_cond_t cond;
	volatile int count;
};

void V(SEM *sem) {

	if(sem == NULL) return;

	if(pthread_mutex_lock(&sem->lockme) != 0)
		exit(EXIT_FAILURE);

	sem->count++;

	if(pthread_cond_broadcast(&sem->cond) != 0)
		exit(EXIT_FAILURE);

	if(pthread_mutex_unlock(&sem->lockme) != 0)
		exit(EXIT_FAILURE);
}

void P(SEM *sem) {
	if(sem == NULL) return;

	if(pthread_mutex_lock(&sem->lockme) != 0)
		exit(EXIT_FAILURE);

	while(sem->count == 0) {
		if(pthread_cond_wait(&sem->cond, &sem->lockme) != 0)
			exit(EXIT_FAILURE);
	}

	sem->count--;

	if(pthread_mutex_unlock(&sem->lockme)!= 0)
		exit(EXIT_FAILURE);
}