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:
- Ponieważ kod ten wyraźnie dotyczy porządkowania pamięci i atomowości co jest punktem spin_lock() ?
- Do tej pory rozumiem, że ACCESS_ONCE przerywa porządkowanie kompilatorów, prawda?
- Czy produce_item (item) po prostu wydaje wszystkie zapisy związane z produktem?
- Wierzę, że smp_wmb() gwarantuje, że wszystkie zapisy w production_item (element) kompletne PRZED napisanie "publikowania", który następuje po nim. prawdziwe?
- 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":
- 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!
- Czy jest tam smp_mb(), aby zagwarantować, że wszystkie odczyty, zanim zostaną ukończone, przed zapisaniem po nim?