2013-01-17 10 views
9

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:

  1. Da dieser Code explizit beschäftigt sich mit Speicherordnungs und Unteilbarkeit, was der Punkt des spin_lock ist() ?
  2. Bis jetzt ist mein Verständnis, dass ACCESS_ONCE Compiler-Neuordnung stoppt, wahr?
  3. Gibt produce_item (item) einfach alle Schreibvorgänge aus, die mit dem Element verknüpft sind?
  4. Ich glaube, smp_wmb() garantiert, dass alle Schreibvorgänge in produce_item (Element) vor dem "publishing" schreiben, die darauf folgen. wahr?
  5. 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":

  1. 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!
  2. Ist die smp_mb() dort zu garantieren, dass alle Lesevorgänge vor dem Abschluss vor dem Schreibvorgang abgeschlossen sind?

Antwort

7

Für den Hersteller:

  1. Die spin_lock() hier zwei Produzenten, um zu verhindern versuchen, die Warteschlange zur gleichen Zeit zu ändern.
  2. ACCESS_ONCE verhindert die Neuordnung, es verhindert auch, dass der Compiler den Wert später erneut lädt. (Es gibt an article about ACCESS_ONCE on LWN, die dies weiter ausdehnt)
  3. Richtig.
  4. Auch korrekt.
  5. Die (implizierte) Schreibsperre wird hier benötigt, bevor der Verbraucher geweckt wird, da der Verbraucher sonst möglicherweise den aktualisierten Wert head nicht sehen kann.

Consumer:

  1. smp_read_barrier_depends() ist eine Datenabhängigkeitsbarriere, die eine schwächere Form einer Lesesperre (siehe 2). Der Effekt ist in diesem Fall sicherzustellen, dass buffer->tail gelesen wird, bevor Sie es als Array-Index in buffer[tail] verwenden.
  2. smp_mb() Hier ist eine vollständige Speicherbarriere, die dafür sorgt, dass alle Lese- und Schreibvorgänge zu diesem Zeitpunkt ausgeführt werden.

Weitere Referenzen:

(Anmerkung: Ich bin nicht ganz sicher über meine Antworten für 5 in dem Hersteller und 1 für die Verbraucher, aber ich glaube, sie‘ Das ist eine faire Annäherung an den Sachverhalt. Ich empfehle dringend, die Dokumentationsseite über Speicherbarrieren zu lesen, da sie umfassender ist als alles, was ich hier schreiben könnte.)