Klausur Februar 2012, Aufgabe msgpipe

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 Februar 2012, Aufgabe msgpipe
Hi,

ich versuche mich gerade an der Programmieraufgabe und bin ein wenig irritiert, weil der bbuffer, der vorgegeben ist, folgendermaßen aufgebaut ist:

typedef struct BNDBUF{
  void **data;
  int size;
  volatile int readPos;
  volatile int writePos;
  SEM *full;
  SEM *free;
  SEM *readLock;
  SEM *writeLock;
}

Meine Fragen wären jetzt

  1. Da data nur ein ** ist, reicht es dann, wenn ich in der Funktion init malloc (sizeof (BNDBUF*)); aufrufe, oder muss ich malloc(sizeof(BNDBUF*) + size * sizeof(void *)); oder so aufrufen?
  2. wozu brauche ich denn die zwei Semaphore readLock und writeLock? Reichen full und free nicht aus? Mit welchen Werten müsste ich die dann initialisieren?

also irgendwie finde ich die aufgabe nicht - die von Feb 2012 ist doch ohne bndbuf.


Haha kein Wunder. Der Teil ist von der Juli 2012. Die hab ich gleichzeitig ausgedruckt und da hat sich eine falsche Seite eingeschlichen :smiley:
Ich hab mich schon gewundert, dass der Puffer nirgends in der Aufgabenstellung auftaucht facepalm
OK, also Juli 2012, die Fragen bleiben aber gleich.


full und free sind für den BNDBUF um zu blockieren wenn er voll bzw. leer ist.
readLock und writeLock schützen den Zugriff auf readPos bzw. writePos. In der Jbuffer hat man das über das gcc atomic builtin gelöst hier wirds halt mit Semaphoren gemacht.

Die bb_init sieht so aus:

BNDBUF *bb_init(size_t size) {
	BNDBUF* buf = (BNDBUF*) malloc(sizeof(BNDBUF));
	if(!buf) {
		return NULL;
	}

	buf->size = size;
	buf->readPos = 0;
	buf->writePos = 0;

	buf->full = sem_init(0);
	if(!buf->full) return NULL;

	buf->free = sem_init(size);
	if(!buf->free) {
		semDestroy(buf->full);
		return NULL;
	}

	buf->readLock = sem_init(1);
	if(!buf->readLock) {
		semDestroy(buf->full);
		semDestroy(buf->free);
		return NULL;
	}

	buf->writeLock = sem_init(1);
	if(!buf->writeLock) {
		semDestroy(buf->full);
		semDestroy(buf->free);
		semDestroy(buf->readLock);
		return NULL;
	}
	
	return buf;
}

Ach ja, stimmt. Ich hab jetzt dummerweise angenommen, dass volatile atomaren Zugriff ermöglicht. Das heißt, ich baue einfach P und V um readPos und writePos herum, wenn ich den Wert verändere, richtig?
In deinem Code ist aber schon ein Fehler, oder? Du fragst ab dem 2. mal immer ab (!buf->free). Das macht doch keinen Sinn, oder?
Vielen Dank!


Oh copy paste :slight_smile: Ja die ifs sollten das jeweils vorhergehende sem_init prüfen.


Dachte ich mir :slight_smile:


Hab mal das Repo public gestellt unter: https://git.informatik.uni-erlangen.de/?p=sp-klausur;a=summary

Wer Fehler findet darf gerne Patches bereitstellen.


a) Nein, denn dann ist [m]data[/m] ein uninitialisierter Zeiger, der ins Nichts zeigt. Das wird in aller Regel gleich beim ersten Zugriff einen saftigen Segfault geben.
b) Nein, denn auch dann ist [m]data[/m] uninitialisiert. Dieses Draufaddieren beim [m]malloc()[/m] nützt nur was, wenn [m]data[/m] als flexible array member deklariert ist, also als Array mit unbestimmter Größe ganz am Ende des Struktur.

→ Für das Array, auf das der [m]data[/m]-Zeiger verweisen soll, braucht man noch zusätzlich ein separates [m]malloc()[/m].


Hier leakst du Speicher, falls [m]sem_init()[/m] fehlschlägt, da fehlt ein [m]free(3)[/m]. Selbes Problem auch weiter unten.

Auch fehlt hier noch das [m]malloc(3)[/m] für [m]buf->data[/m] (wenn du ganz korrekt sein willst, musst du hier auch noch einen Integer-Overflow abfangen: [m]size * sizeof(*data)[/m] ist ggf. größer als [m]size_t[/m], was du in malloc packst).

Ich weiß auch nicht wie es aktuell ist, aber früher bekam [m]sem_init()[/m] als Parameter ein [m]int[/m], was somit das Maximum für [m]size[/m] darstellt. Das könnte man auch noch abfangen.

Die beiden extra Semaphoren schützen den Buffer vor gleichzeitigen Zugriffen und müssen mit 1 initialisiert werden, um den kritischen Abschnitt (get und put) zu schützen.

[m]volatile[/m] in C sorgt nur dafür, dass der Compiler den Wert jedes Mal aus dem Speicher laden muss und dass er Zugriffe zwischen mehreren [m]volatile[/m] Variablen nicht umsortieren darf (andere Zugriffe darf er beliebig umsortieren). Das Lesen/Schreiben selbst muss dabei nicht zwingend atomar sein! Bei x86 ist das für alignte CPU-Worte ([m]sizeof(void *)[/m], z.B. [m]int[/m] oder Pointer) aber gegeben.