Cuando estaba navegando por el kernel de Linux, encontré una container_of
macro que se define de la siguiente manera:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
Entiendo lo que hace container_of, pero lo que no entiendo es la última frase, que es
(type *)( (char *)__mptr - offsetof(type,member) );})
Si usamos la macro de la siguiente manera:
container_of(dev, struct wifi_device, dev);
La parte correspondiente de la última oración sería:
(struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);
que parece no hacer nada. ¿Alguien podría llenar el vacío aquí?
c
linux-kernel
c-preprocessor
jaeyong
fuente
fuente
rb_node
.Respuestas:
Su ejemplo de uso
container_of(dev, struct wifi_device, dev);
puede ser un poco engañoso ya que está mezclando dos espacios de nombres allí.Mientras que el primero
dev
en su ejemplo se refiere al nombre del puntero, el segundo sedev
refiere al nombre de un miembro de la estructura.Lo más probable es que esta confusión esté provocando todo ese dolor de cabeza. De hecho, el
member
parámetro en su cotización se refiere al nombre dado a ese miembro en la estructura del contenedor.Tomando este contenedor por ejemplo:
struct container { int some_other_data; int this_data; }
Y un puntero
int *my_ptr
althis_data
miembro que usaría la macro para obtener un punterostruct container *my_container
usando:struct container *my_container; my_container = container_of(my_ptr, struct container, this_data);
Tener en cuenta el desplazamiento de
this_data
al principio de la estructura es esencial para obtener la ubicación correcta del puntero.Efectivamente, solo tiene que restar el desplazamiento del miembro
this_data
de su punteromy_ptr
para obtener la ubicación correcta.Eso es exactamente lo que hace la última línea de la macro.
fuente
include/linux/kernel.h
) muy claramente en su blog . Por cierto : list_entry macro (include/linux/list.h
) solía definirse de manera muy similar , pero ahora se define comocontainer_of
.error: initialization from incompatible pointer type
. ¿Esto se debe a laconst
definición de macro según este hilo? stackoverflow.com/a/39963179/1256234La última frase emitida:
un puntero a un dado
type
. El puntero se calcula como desplazamiento de un puntero dadodev
:( (char *)__mptr - offsetof(type,member) )
Cuando usa la
cointainer_of
macro, desea recuperar la estructura que contiene el puntero de un campo determinado. Por ejemplo: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);
Tiene un puntero que apunta al medio de una estructura (y sabe que es un puntero al campo
two
[ el nombre del campo en la estructura ]), pero desea recuperar la estructura completa (numbers
). Entonces, calcula el desplazamiento del archivotwo
en la estructura:y reste este desplazamiento del puntero dado. El resultado es el puntero al inicio de la estructura. Finalmente, lanza este puntero al tipo de estructura para tener una variable válida.
fuente
Es una utilización de una extensión gcc, las expresiones de las declaraciones . Si ve la macro como algo que devuelve un valor, entonces la última línea sería:
return (struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);
Consulte la página vinculada para obtener una explicación de las declaraciones compuestas. Aquí hay un ejemplo :
int main(int argc, char**argv) { int b; b = 5; b = ({int a; a = b*b; a;}); printf("b %d\n", b); }
La salida es
fuente
macro conatainer_of () en el kernel de Linux -
Cuando se trata de administrar varias estructuras de datos en código, casi siempre necesitará incrustar una estructura en otra y recuperarlas en cualquier momento sin que se le hagan preguntas sobre las compensaciones o límites de la memoria. Digamos que tienes una persona estructura, como se define aquí:
struct person { int age; int salary; char *name; } p;
Al tener solo un puntero sobre la edad o el salario, puede recuperar la estructura completa que envuelve (contiene) ese puntero. Como dice el nombre, la macro container_of se usa para encontrar el contenedor del campo dado de una estructura. La macro se define en include / linux / kernel.hy se parece a lo siguiente:
#define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) * __mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); })
No tenga miedo de los indicadores; solo véalos de la siguiente manera:
Estos son los elementos del fragmento de código anterior:
Consideremos el siguiente contenedor:
struct person { int age; int salary; char *name; };
Ahora, consideremos una de sus instancias, junto con un puntero al miembro de edad:
struct person somebody; [...] int *age_ptr = &somebody.age;
Junto con un puntero al miembro de nombre (age_ptr), puede usar la macro container_of para obtener un puntero a toda la estructura (contenedor) que envuelve este miembro usando lo siguiente:
struct person *the_person; the_person = container_of(age_ptr, struct person, age);
container_of tiene en cuenta el desplazamiento de la edad al comienzo de la estructura para obtener la ubicación correcta del puntero. Si resta el desplazamiento de la edad del campo del puntero age_ptr, obtendrá la ubicación correcta. Esto es lo que hace la última línea de la macro:
(type *)( (char *)__mptr - offsetof(type,member) );
Aplicando esto a un ejemplo real, da lo siguiente:
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);
La macro container_of se usa principalmente en contenedores genéricos en el kernel.
Eso es todo sobre container_of macro en el kernel.
fuente
Un poco de contexto real dice más claro, a continuación use el árbol rojo-negro como ejemplo , que es la forma en que entiendo
container_of
.como
Documentation/rbtree.txt
dice, en el código del kernel de Linux, no es rb_node contiene entrada de datos, sinostruct vm_area_struct
(en archivoinclude/linux/mm_types.h:284
) es tal estructura,en el mismo archivo, hay una macro
rb_entry
que se define como#define rb_entry(ptr, type, member) container_of(ptr, type, member)
claramente,
rb_entry
es lo mismo quecontainer_of
.en la
mm/mmap.c:299
definición de función internabrowse_rb
, hay un uso derb_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 */
ahora está claro, en
container_of(ptr, type, member)
,type
es la estructura del contenedor, aquístruct vm_area_struct
member
es el nombre de un miembro detype
instancia, aquívm_rb
, que es de tiporb_node
,ptr
es un puntero que apuntamember
a unatype
instancia, aquírb_node *nd
.lo
container_of
que es, como en este ejemplo,obj.member
(aquíobj.vm_rb
), devuelva la dirección deobj
.obj.vm_rb
menosoffset between the struct and member
será la dirección del contenedor.include/linux/kernel.h:858
-- definicion decontainer_of
include/linux/rbtree.h:51
-- definicion derb_entry
mm/mmap.c:299
-- uso derb_entry
include/linux/mm_types.h:284
-struct vm_area_struct
Documentation/rbtree.txt:
- Documentación de árbol rojo-negroinclude/linux/rbtree.h:36
-- definicion destruct rb_node
PD
Archivos anteriores están en la versión actual de desarrollo, es decir,
4.13.0-rc7
.file:k
significa kth línea enfile
.fuente
Enlace muy útil para comprender el macro container_of en el kernel de Linux. https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html
fuente
La implementación más simple del contenedor _ de macro se encuentra a continuación, reduce todas las comprobaciones complejas de tipo y funciona
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
ptr dará la dirección del miembro y solo restará la diferencia de compensación y obtendrá la dirección de inicio.
Uso de ejemplo
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; }
fuente