Es gibt einen Artikel unter: http://lwn.net/Articles/378262/, der die Linux Kernel-Ringpuffer-Implementierung beschreibt. Ich habe einige Fragen:Grundlegendes zu Linux Kernel Circular Buffer
Hier ist der „Erzeuger“:
spin_lock(&producer_lock);
unsigned long head = buffer->head;
unsigned long tail = ACCESS_ONCE(buffer->tail);
if (CIRC_SPACE(head, tail, buffer->size) >= 1) {
/* insert one item into the buffer */
struct item *item = buffer[head];
produce_item(item);
smp_wmb(); /* commit the item before incrementing the head */
buffer->head = (head + 1) & (buffer->size - 1);
/* wake_up() will make sure that the head is committed before
* waking anyone up */
wake_up(consumer);
}
spin_unlock(&producer_lock);
Fragen:
- Da dieser Code explizit beschäftigt sich mit Speicherordnungs und Unteilbarkeit, was der Punkt des spin_lock ist() ?
- Bis jetzt ist mein Verständnis, dass ACCESS_ONCE Compiler-Neuordnung stoppt, wahr?
- Gibt produce_item (item) einfach alle Schreibvorgänge aus, die mit dem Element verknüpft sind?
- Ich glaube, smp_wmb() garantiert, dass alle Schreibvorgänge in produce_item (Element) vor dem "publishing" schreiben, die darauf folgen. wahr?
- Der Kommentar auf der Seite, wo ich diesen Code bekam, scheint zu implizieren, dass ein smp_wmb() normalerweise nach der Aktualisierung des Kopfindex benötigt wird, aber wake_up (Consumer) tut dies, also ist es nicht notwendig. Ist das wahr? Wenn ja warum?
Hier ist die "Verbraucher":
spin_lock(&consumer_lock);
unsigned long head = ACCESS_ONCE(buffer->head);
unsigned long tail = buffer->tail;
if (CIRC_CNT(head, tail, buffer->size) >= 1) {
/* read index before reading contents at that index */
smp_read_barrier_depends();
/* extract one item from the buffer */
struct item *item = buffer[tail];
consume_item(item);
smp_mb(); /* finish reading descriptor before incrementing tail */
buffer->tail = (tail + 1) & (buffer->size - 1);
}
spin_unlock(&consumer_lock);
Fragen spezifisch für "Verbraucher":
- Was bedeutet smp_read_barrier_depends() tun? Von einigen Kommentaren in einem Forum scheint es, als hättest du hier eine smp_rmb() ausgeben können, aber auf einigen Architekturen ist das unnötig (x86) und zu teuer, also wurde smp_read_barrier_depends() dafür optional erstellt ... Das heißt, ich verstehe nicht wirklich warum smp_rmb() jemals notwendig ist!
- Ist die smp_mb() dort zu garantieren, dass alle Lesevorgänge vor dem Abschluss vor dem Schreibvorgang abgeschlossen sind?