2013-04-05 2 views
45

Quando stavo dando un'occhiata al kernel di Linux, ho trovato una macro container_of, che è definito come segue:intesa macro container_of nel kernel di Linux

#define container_of(ptr, type, member) ({      \ 
     const typeof(((type *)0)->member) *__mptr = (ptr); \ 
     (type *)((char *)__mptr - offsetof(type,member));}) 

capisco cosa fa container_of fare, ma quello che non capisco è l'ultima frase, che è

(type *)((char *)__mptr - offsetof(type,member));}) 

Se usiamo la macro come segue:

container_of(dev, struct wifi_device, dev); 

La parte corrispondente dell'ultima frase sarebbe:

(struct wifi_device *)((char *)__mptr - offset(struct wifi_device, dev); 

che sembra non fare nulla. Qualcuno potrebbe riempire il vuoto qui?

+0

[questa risposta] (https://stackoverflow.com/a/45923596/3625404) ha ** un esempio reale e intuitivo ** utilizzando albero rosso-nero 'rb_node'. – qeatzy

risposta

56

L'esempio di utilizzo container_of(dev, struct wifi_device, dev); potrebbe essere un po 'fuorviante poiché si stanno mescolando due spazi dei nomi.

Mentre il primo dev nell'esempio fa riferimento al nome del puntatore, il secondo dev fa riferimento al nome di un membro della struttura.

Molto probabilmente questo mix sta provocando tutto quel mal di testa. Infatti il ​​parametro member nella tua citazione si riferisce al nome dato a quel membro nella struttura del contenitore.

Prendendo questo contenitore per esempio:

struct container { 
    int some_other_data; 
    int this_data; 
} 

e un puntatore int *my_ptr al membro this_data devi usare la macro per ottenere un puntatore a struct container *my_container utilizzando:

struct container *my_container; 
my_container = container_of(my_ptr, struct container, this_data); 

Prendendo l'offset di this_data all'inizio della struttura in considerazione è essenziale per ottenere la posizione del puntatore corretta.

In pratica è sufficiente sottrarre l'offset del membro this_data dal puntatore my_ptr per ottenere la posizione corretta.

Questo è esattamente ciò che fa l'ultima riga della macro.

+3

Per quelli di voi che hanno bisogno di una spiegazione più dettagliata: [Radek Pazdera] (https://twitter.com/radekpazdera) [spiegato] (http://radek.io/2012/11/10/magical-container_of-macro/) [container_of] (http://lxr.free-electrons.com/ident?i=container_of) macro ('include/linux/kernel.h') molto chiaramente sul [suo blog] (http://radek.io /). [BTW] (http://www.tux.org/lkml/#contributors): [list_entry] (http://lxr.free-electrons.com/ident?i=list_entry) macro ('include/linux/list .h') usato per essere definito [in modo molto simile] (http://stackoverflow.com/questions/5550404/list-entry-in-linux), ma ora è definito come 'container_of'. –

+1

Anche questo blogpost di Greg Kroah-Hartman può essere utile: http://www.kroah.com/log/linux/container_of.html – EFraim

14

La frase ultima Cast:

(type *)(...) 

un puntatore ad un dato type. Il puntatore è calcolato come offset da un dato puntatore dev:

((char *)__mptr - offsetof(type,member)) 

Quando si utilizza la cointainer_of macro, si desidera recuperare la struttura che contiene il puntatore di un dato campo. Per esempio:

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

Si dispone di un puntatore che punta nel mezzo di una struttura (e si sa che è un puntatore al two depositato [il nome del campo nella struttura]), ma si vuole recuperare l'intera struttura (numbers). Quindi, si calcola l'offset del depositata two nella struttura:

offsetof(type,member) 

e sottrarre questo offset dal puntatore data. Il risultato è il puntatore all'inizio della struttura. Infine, lanci questo puntatore al tipo di struttura per avere una variabile valida.

6

È un utilizzo di un'estensione gcc, statements expressions. Se si vede la macro come qualcosa di restituire un valore, allora l'ultima riga sarebbe:

return (struct wifi_device *)((char *)__mptr - offset(struct wifi_device, dev); 

Vedere la pagina collegata per una spiegazione di istruzioni composte. Ecco un esempio:

int main(int argc, char**argv) 
{ 
    int b; 
    b = 5; 
    b = ({int a; 
      a = b*b; 
      a;}); 
    printf("b %d\n", b); 
} 

L'uscita è

b 25

0

Un po 'di contesto reale dice più chiaro, sotto uso albero rosso-nero, come ad esempio, che è la modo che capisco container_of.

come Documentation/rbtree.txt stati, nel codice del kernel di Linux, non è rb_node contengono dati entrata, piuttosto

nodi di dati in un albero di rbtree sono strutture che contengono un membro rb_node struct .

struct vm_area_struct (nel file di include/linux/mm_types.h:284) è una tale struttura,

nello stesso file , esiste una macro rb_entry che è definita come

#define rb_entry(ptr, type, member) container_of(ptr, type, member) 

chiaramente, rb_entry è stessa container_of.

a mm/mmap.c:299 all'interno definizione di funzione browse_rb, v'è un uso di rb_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 */ 

Ora è chiaro, in container_of(ptr, type, member),

  • type è la struct contenitore, qui struct vm_area_struct
  • member è il nome di un membro di type esempio, qui vm_rb, che è di tipo rb_node,
  • ptr è un puntatore che indica member di type esempio, qui rb_node *nd.

ciò container_of fare è, come in questo esempio,

  • dato indirizzo di obj.member (qui obj.vm_rb), restituirà l'indirizzo di obj.
  • dal momento che una struct è un blocco di memoria contigua, indirizzo del obj.vm_rb meno offset between the struct and member sarà l'indirizzo del contenitore.

include/linux/kernel.h:858 - definizione di container_of

include/linux/rbtree.h:51 - definizione di rb_entry

mm/mmap.c:299 - utilizzo di rb_entry

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

Documentation/rbtree.txt: - Documentazione di rosso-nero albero

include/linux/rbtree.h:36 - definizione di struct rb_node

P.S.

I file sopra indicati sono in versione di sviluppo corrente, ad esempio 4.13.0-rc7.

file:k mean kth line in file.