Zrozumienie makra container_of w jądrze Linuksa

84

Kiedy przeglądałem jądro Linuksa, znalazłem container_ofmakro, które jest zdefiniowane w następujący sposób:

Rozumiem, co robi container_of, ale nie rozumiem ostatniego zdania, czyli

Jeśli użyjemy makra w następujący sposób:

Odpowiednia część ostatniego zdania brzmiałaby:

co wygląda na nic nie robienie. Czy ktoś mógłby tutaj wypełnić tę pustkę?

jaeyong
źródło
1
ta odpowiedź ma prawdziwy i intuicyjny przykład użycia czerwono-czarnego drzewa rb_node.
qeatzy

Odpowiedzi:

87

Twój przykład użycia container_of(dev, struct wifi_device, dev);może być nieco mylący, ponieważ mieszasz tam dwie przestrzenie nazw.

Podczas gdy pierwsza devw twoim przykładzie odnosi się do nazwy wskaźnika, druga devodnosi się do nazwy elementu członkowskiego struktury.

Najprawdopodobniej to zamieszanie wywołuje cały ten ból głowy. W rzeczywistości memberparametr w Twojej ofercie odnosi się do nazwy nadanej temu elementowi w strukturze kontenera.

Biorąc na przykład ten pojemnik:

I wskaźnik int *my_ptrdo this_dataczłonka, do którego użyjesz makra, aby uzyskać wskaźnik struct container *my_container, używając:

Uwzględnienie przesunięcia this_datado początku struktury jest niezbędne do uzyskania prawidłowego położenia wskaźnika.

W efekcie wystarczy odjąć przesunięcie członka this_dataod wskaźnika, my_ptraby uzyskać prawidłowe położenie.

To jest dokładnie to, co robi ostatni wiersz makra.

mikyra
źródło
5
Dla tych z Was, którzy potrzebują bardziej szczegółowego wyjaśnienia: Radek Pazdera bardzo jasno wyjaśnił container_of macro ( include/linux/kernel.h) na swoim blogu . BTW : makro list_entry ( include/linux/list.h) było kiedyś definiowane bardzo podobnie , ale teraz jest zdefiniowane jako container_of.
patryk.beza
1
Przydatny może być również ten post na blogu Grega Kroah-Hartmana: kroah.com/log/linux/container_of.html
EFraim,
1
W zasadzie to fajna odpowiedź, tyle że nie odpowiada ona na znaczną część pytania? Np. Co osiąga ostatnia linia kodu i jak?
Michael Beer
Kiedy podążam za tym przykładem, używając GCC otrzymuję error: initialization from incompatible pointer type. Czy wynika to z constdefinicji makra zgodnie z tym wątkiem? stackoverflow.com/a/39963179/1256234
Andy J
18

Ostatnie rzucone zdanie:

wskaźnik do danego type. Wskaźnik jest obliczany jako przesunięcie od podanego wskaźnika dev:

Korzystając z cointainer_ofmakra, chcesz pobrać strukturę zawierającą wskaźnik danego pola. Na przykład:

Masz wskaźnik, który wskazuje środek struktury (i wiesz, że jest to wskaźnik do pola two[ nazwa pola w strukturze ]), ale chcesz pobrać całą strukturę ( numbers). Więc obliczasz przesunięcie pola twow strukturze:

i odejmij to przesunięcie od podanego wskaźnika. Rezultatem jest wskaźnik do początku struktury. Na koniec rzutujesz ten wskaźnik na typ struktury, aby mieć prawidłową zmienną.

Federico
źródło
10

Jest to wykorzystanie rozszerzenia gcc, wyrażeń instrukcji . Jeśli widzisz makro jako coś zwracającego wartość, ostatnia linia wyglądałaby tak:

Zobacz linkowaną stronę, aby uzyskać wyjaśnienie instrukcji złożonych. Oto przykład :

Wynik jest

b 25

shodanex
źródło
7

makro conatainer_of () w jądrze Linuksa -

Jeśli chodzi o zarządzanie kilkoma strukturami danych w kodzie, prawie zawsze będziesz musiał osadzić jedną strukturę w innej i odzyskać je w dowolnym momencie bez zadawania pytań o przesunięcia lub granice pamięci. Powiedzmy, że masz osobę struct, jak zdefiniowano tutaj:

Mając tylko wskaźnik na wiek lub wynagrodzenie, możesz pobrać całą strukturę opakowującą (zawierającą) ten wskaźnik. Jak sama nazwa wskazuje, makro container_of służy do wyszukiwania kontenera danego pola struktury. Makro jest zdefiniowane w include / linux / kernel.h i wygląda następująco:

Nie bój się wskazówek; po prostu zobacz je w następujący sposób:

Oto elementy poprzedniego fragmentu kodu:

  • pointer: To jest wskaźnik do pola w strukturze
  • typ_kontenera: jest to typ zawijania struktury (zawierającej) wskaźnik
  • container_field: jest to nazwa pola, na które wskaźnik wskazuje wewnątrz struktury

Rozważmy następujący kontener:

Rozważmy teraz jedno z jego wystąpień, wraz ze wskaźnikiem do członka wieku:

Wraz ze wskaźnikiem do elementu członkowskiego imienia (age_ptr) możesz użyć makra container_of, aby uzyskać wskaźnik do całej struktury (kontenera), który otacza ten element członkowski, używając następującego polecenia:

container_of bierze pod uwagę przesunięcie wieku na początku struktury, aby uzyskać poprawną lokalizację wskaźnika. Jeśli odejmiesz przesunięcie wieku pola od wskaźnika age_ptr, otrzymasz poprawną lokalizację. Oto, co robi ostatnia linia makra:

Odnosząc to do prawdziwego przykładu, daje to:

Makro container_of jest używane głównie w ogólnych kontenerach jądra.

To wszystko o makrze container_of w jądrze.

Spyder
źródło
3

Trochę rzeczywisty kontekst mówi jaśniej, poniżej użyj czerwono-czarnego drzewa jako przykładu , co rozumiem container_of.

jak Documentation/rbtree.txtstwierdza, w kodzie jądra Linuksa, rb_node nie zawiera raczej wprowadzania danych

Węzły danych w drzewie rbtree są strukturami zawierającymi element struktury rb_node.

struct vm_area_struct(w pliku include/linux/mm_types.h:284) jest taką strukturą,

w tym samym pliku znajduje się makro rb_entryzdefiniowane jako

oczywiście rb_entryjest taki sam jak container_of.

w mm/mmap.c:299definicji funkcji wewnętrznej browse_rbzastosowano rb_entry:

Teraz jest jasne, w container_of(ptr, type, member),

  • type jest strukturą kontenera struct vm_area_struct
  • memberto nazwa członka typeinstancji, w tym vm_rbprzypadku typu rb_node,
  • ptrJest to wskaźnik wskazujący membero typeprzykład tutaj rb_node *nd.

co container_ofto jest, jak w tym przykładzie,

  • podany adres obj.member(tutaj obj.vm_rb), zwróć adres obj.
  • ponieważ struktura jest blokiem ciągłej pamięci, adres obj.vm_rbminus offset between the struct and memberbędzie adresem kontenera.

include/linux/kernel.h:858 -- Definicja container_of

include/linux/rbtree.h:51 -- Definicja rb_entry

mm/mmap.c:299 -- użycie rb_entry

include/linux/mm_types.h:284 - struct vm_area_struct

Documentation/rbtree.txt: - Dokumentacja czerwono-czarnego drzewa

include/linux/rbtree.h:36 -- Definicja struct rb_node

PS

Powyższe pliki są w aktualnej wersji deweloperskiej tj 4.13.0-rc7.

file:koznacza k-tą linię w file.

qeatzy
źródło
0

Najprostsza implementacja makra Container _of jest poniżej. Zmniejsza ona wszelkie skomplikowane sprawdzanie typu i prac

ptr poda adres członka i po prostu odejmie różnicę przesunięcia, a otrzymasz adres początkowy.

Przykładowe użycie

Alok Prasad
źródło