Kiedy przeglądałem jądro Linuksa, znalazłem container_of
makro, które jest zdefiniowane w następujący sposób:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
Rozumiem, co robi container_of, ale nie rozumiem ostatniego zdania, czyli
(type *)( (char *)__mptr - offsetof(type,member) );})
Jeśli użyjemy makra w następujący sposób:
container_of(dev, struct wifi_device, dev);
Odpowiednia część ostatniego zdania brzmiałaby:
(struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);
co wygląda na nic nie robienie. Czy ktoś mógłby tutaj wypełnić tę pustkę?
c
linux-kernel
c-preprocessor
jaeyong
źródło
źródło
rb_node
.Odpowiedzi:
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
dev
w twoim przykładzie odnosi się do nazwy wskaźnika, drugadev
odnosi się do nazwy elementu członkowskiego struktury.Najprawdopodobniej to zamieszanie wywołuje cały ten ból głowy. W rzeczywistości
member
parametr w Twojej ofercie odnosi się do nazwy nadanej temu elementowi w strukturze kontenera.Biorąc na przykład ten pojemnik:
struct container { int some_other_data; int this_data; }
I wskaźnik
int *my_ptr
dothis_data
członka, do którego użyjesz makra, aby uzyskać wskaźnikstruct container *my_container
, używając:struct container *my_container; my_container = container_of(my_ptr, struct container, this_data);
Uwzględnienie przesunięcia
this_data
do początku struktury jest niezbędne do uzyskania prawidłowego położenia wskaźnika.W efekcie wystarczy odjąć przesunięcie członka
this_data
od wskaźnika,my_ptr
aby uzyskać prawidłowe położenie.To jest dokładnie to, co robi ostatni wiersz makra.
źródło
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 jakocontainer_of
.error: initialization from incompatible pointer type
. Czy wynika to zconst
definicji makra zgodnie z tym wątkiem? stackoverflow.com/a/39963179/1256234Ostatnie rzucone zdanie:
wskaźnik do danego
type
. Wskaźnik jest obliczany jako przesunięcie od podanego wskaźnikadev
:( (char *)__mptr - offsetof(type,member) )
Korzystając z
cointainer_of
makra, chcesz pobrać strukturę zawierającą wskaźnik danego pola. Na przykład:struct numbers { int one; int two; int three; } n; int *ptr = &n.two; struct numbers *n_ptr; n_ptr = container_of(ptr, struct numbers, two);
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 polatwo
w 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ą.
źródło
Jest to wykorzystanie rozszerzenia gcc, wyrażeń instrukcji . Jeśli widzisz makro jako coś zwracającego wartość, ostatnia linia wyglądałaby tak:
return (struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);
Zobacz linkowaną stronę, aby uzyskać wyjaśnienie instrukcji złożonych. Oto przykład :
int main(int argc, char**argv) { int b; b = 5; b = ({int a; a = b*b; a;}); printf("b %d\n", b); }
Wynik jest
źródło
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:
struct person { int age; int salary; char *name; } p;
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:
#define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) * __mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); })
Nie bój się wskazówek; po prostu zobacz je w następujący sposób:
Oto elementy poprzedniego fragmentu kodu:
Rozważmy następujący kontener:
struct person { int age; int salary; char *name; };
Rozważmy teraz jedno z jego wystąpień, wraz ze wskaźnikiem do członka wieku:
struct person somebody; [...] int *age_ptr = &somebody.age;
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:
struct person *the_person; the_person = container_of(age_ptr, struct person, age);
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:
(type *)( (char *)__mptr - offsetof(type,member) );
Odnosząc to do prawdziwego przykładu, daje to:
struct family { struct person *father; struct person *mother; int number_of_sons; int family_id; } f; /* * Fill and initialise f somewhere */ [...] /* * pointer to a field of the structure * (could be any (non-pointer) member in the structure) */ int *fam_id_ptr = &f.family_id; struct family *fam_ptr; /* now let us retrieve back its family */ fam_ptr = container_of(fam_id_ptr, struct family, family_id);
Makro container_of jest używane głównie w ogólnych kontenerach jądra.
To wszystko o makrze container_of w jądrze.
źródło
Trochę rzeczywisty kontekst mówi jaśniej, poniżej użyj czerwono-czarnego drzewa jako przykładu , co rozumiem
container_of
.jak
Documentation/rbtree.txt
stwierdza, w kodzie jądra Linuksa, rb_node nie zawiera raczej wprowadzania danychstruct vm_area_struct
(w plikuinclude/linux/mm_types.h:284
) jest taką strukturą,w tym samym pliku znajduje się makro
rb_entry
zdefiniowane jako#define rb_entry(ptr, type, member) container_of(ptr, type, member)
oczywiście
rb_entry
jest taki sam jakcontainer_of
.w
mm/mmap.c:299
definicji funkcji wewnętrznejbrowse_rb
zastosowanorb_entry
:static int browse_rb(struct mm_struct *mm) { /* two line code not matter */ struct rb_node *nd, *pn = NULL; /*nd, first arg, i.e. ptr. */ unsigned long prev = 0, pend = 0; for (nd = rb_first(root); nd; nd = rb_next(nd)) { struct vm_area_struct *vma; vma = rb_entry(nd, struct vm_area_struct, vm_rb); /* -- usage of rb_entry (equivalent to container_of) */ /* more code not matter here */
Teraz jest jasne, w
container_of(ptr, type, member)
,type
jest strukturą kontenerastruct vm_area_struct
member
to nazwa członkatype
instancji, w tymvm_rb
przypadku typurb_node
,ptr
Jest to wskaźnik wskazującymember
otype
przykład tutajrb_node *nd
.co
container_of
to jest, jak w tym przykładzie,obj.member
(tutajobj.vm_rb
), zwróć adresobj
.obj.vm_rb
minusoffset between the struct and member
będzie adresem kontenera.include/linux/kernel.h:858
-- Definicjacontainer_of
include/linux/rbtree.h:51
-- Definicjarb_entry
mm/mmap.c:299
-- użycierb_entry
include/linux/mm_types.h:284
-struct vm_area_struct
Documentation/rbtree.txt:
- Dokumentacja czerwono-czarnego drzewainclude/linux/rbtree.h:36
-- Definicjastruct rb_node
PS
Powyższe pliki są w aktualnej wersji deweloperskiej tj
4.13.0-rc7
.file:k
oznacza k-tą linię wfile
.źródło
Bardzo przydatny link do zrozumienia makra container_of w jądrze Linuksa. https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html
źródło
Najprostsza implementacja makra Container _of jest poniżej. Zmniejsza ona wszelkie skomplikowane sprawdzanie typu i prac
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
ptr poda adres członka i po prostu odejmie różnicę przesunięcia, a otrzymasz adres początkowy.
Przykładowe użycie
struct sample { int mem1; char mem2; int mem3; }; int main(void) { struct sample sample1; printf("Address of Structure sample1 (Normal Method) = %p\n", &sample1); printf("Address of Structure sample1 (container_of Method) = %p\n", container_of(&sample1.mem3, struct sample, mem3)); return 0; }
źródło