2013-01-17 10 views
9

Istnieje artykuł pod adresem: http://lwn.net/Articles/378262/ opisujący implementację bufora kołowego jądra systemu Linux. Mam kilka pytań:Omówienie bufora okrągłego jądra systemu Linux

Oto „producent”:

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); 

pytania:

  1. Ponieważ kod ten wyraźnie dotyczy porządkowania pamięci i atomowości co jest punktem spin_lock() ?
  2. Do tej pory rozumiem, że ACCESS_ONCE przerywa porządkowanie kompilatorów, prawda?
  3. Czy produce_item (item) po prostu wydaje wszystkie zapisy związane z produktem?
  4. Wierzę, że smp_wmb() gwarantuje, że wszystkie zapisy w production_item (element) kompletne PRZED napisanie "publikowania", który następuje po nim. prawdziwe?
  5. Komentarz na stronie, na której dostałem ten kod, wydaje się sugerować, że smp_wmb() byłby normalnie potrzebny po aktualizacji indeksu głównego, ale wake_up (konsument) robi to, więc nie jest to konieczne. Czy to prawda? Jeśli tak, dlaczego?

Oto "konsument":

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); 

pytania specyficzne dla "konsument":

  1. Co smp_read_barrier_depends() zrobić? Z niektórych komentarzy na forum wydaje się, że mógłbyś wydać smp_rmb() tutaj, ale na niektórych architekturach jest to niepotrzebne (x86) i zbyt kosztowne, więc smp_read_barrier_depends() został stworzony, aby zrobić to opcjonalnie ... To powiedziawszy, I tak naprawdę nie rozumiem, dlaczego smp_rmb() jest zawsze potrzebny!
  2. Czy jest tam smp_mb(), aby zagwarantować, że wszystkie odczyty, zanim zostaną ukończone, przed zapisaniem po nim?

Odpowiedz

7

Dla producenta:

  1. spin_lock() tutaj jest zapobieganie dwóch producentów od prób modyfikowania kolejkę w tym samym czasie.
  2. ACCESS_ONCE zapobiega ponownemu porządkowaniu, zapobiega także ponownemu wczytaniu wartości przez kompilator później. (Jest an article about ACCESS_ONCE on LWN, który rozwija się dalej)
  3. Prawidłowo.
  4. Także poprawne.
  5. Bariera zapisu (domniemana) jest tutaj potrzebna przed przebudzeniem konsumenta, ponieważ w przeciwnym razie konsument może nie zobaczyć zaktualizowanej wartości head.

konsumentów:

  1. smp_read_barrier_depends() bariera zależność danych, która jest słabsza postać bariery odczytu (patrz 2). Efektem w tym przypadku jest odczytanie buffer->tail przed użyciem go jako indeksu tablicy w buffer[tail].
  2. tutaj jest pełna bariera pamięci, zapewniając, że wszystkie odczyty i zapisy są popełniane przez ten punkt.

Dodatkowe informacje:

(Uwaga: Nie jestem do końca pewien, o moich odpowiedzi dla 5 w 1 dla producenta i konsumenta, ale wierzę, że oni Odpowiednie przybliżenie faktów. Gorąco polecam przeczytanie strony z dokumentacją o barierach pamięci, ponieważ jest bardziej wszechstronna niż wszystko, co mogłem tutaj napisać.)

Powiązane problemy