Comprender container_of macro en el kernel de Linux
linux-kernel c-preprocessor (4)
Es una utilización de una extensión de 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);
}
El resultado es
b 25
Cuando estaba explorando el kernel de Linux, encontré una macro container_of
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 qué hace container_of do, pero lo que no entiendo es la última oración, 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. ¿Podría alguien llenar el vacío aquí?
La última oración emitida:
(type *)(...)
un puntero a un type
dado. El puntero se calcula como desplazamiento desde un puntero dev
:
( (char *)__mptr - offsetof(type,member) )
Cuando utiliza el cointainer_of
macro, desea recuperar la estructura que contiene el puntero de un campo dado. 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 en el medio de una estructura (y sabe que es un puntero a los two
archivados [ el nombre del campo en la estructura ]), pero desea recuperar toda la estructura ( numbers
). Entonces, calcula el desplazamiento de los two
archivados en la estructura:
offsetof(type,member)
y resta esta compensación del puntero dado. El resultado es el puntero al inicio de la estructura. Finalmente, coloca este puntero al tipo de estructura para tener una variable válida.
Su ejemplo de uso container_of(dev, struct wifi_device, dev);
podría ser un poco engañoso ya que está mezclando dos espacios de nombres allí.
Mientras que el primer desarrollador en su ejemplo se refiere al nombre del puntero, el segundo desarrollador se refiere al nombre de un miembro de la estructura.
Lo más probable es que esta confusión provoque todo ese dolor de cabeza. De hecho, el parámetro member
en su presupuesto 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
para el miembro this_data
que usaría la macro para obtener un puntero al struct container *my_container
usando:
struct container *my_container;
my_container = container_of(my_ptr, struct container, this_data);
Teniendo en cuenta el desplazamiento de this_data
al comienzo 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 puntero my_ptr
para obtener la ubicación correcta.
Eso es exactamente lo que hace la última línea de la macro.
Un pequeño contexto real dice más claro, debajo del uso del árbol rojo-negro como ejemplo , que es la forma en que entiendo container_of
.
como dice Documentation/rbtree.txt
, en código de núcleo de Linux, no es rb_node contener entrada de datos, sino
Los nodos de datos en un árbol rbree son estructuras que contienen un miembro struct rb_node.
struct vm_area_struct
(en el archivo include/linux/mm_types.h:284
) es una estructura tal,
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 que container_of
.
en mm/mmap.c:299
dentro de la definición de función browse_rb
, hay un uso de 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 */
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 de la instancia detype
, aquívm_rb
, que es de tiporb_node
, -
ptr
es un puntero que señala elmember
de una instancia detype
, aquírb_node *nd
.
qué container_of
hacer es, como en este ejemplo,
- dirección dada de
obj.member
(aquíobj.vm_rb
), devuelva la dirección deobj
. - como una estructura es un bloque de memoria contigua, la dirección de
offset between the struct and member
obj.vm_rb
menosoffset between the struct and member
será la dirección del contenedor.
include/linux/kernel.h:858
- definición de container_of
include/linux/rbtree.h:51
- definición de rb_entry
mm/mmap.c:299
- uso de rb_entry
include/linux/mm_types.h:284
- struct vm_area_struct
Documentation/rbtree.txt:
- Documentación del árbol rojo-negro
include/linux/rbtree.h:36
- definición de struct rb_node
PD
Los archivos anteriores están en la versión de desarrollo actual, es decir, 4.13.0-rc7
.
file:k
significa kth línea en el file
.