Macro conatainer_of() dans le noyau Linux -
Lorsqu'il s'agit de gérer plusieurs structures de données dans un code, vous aurez presque toujours besoin d'intégrer une structure dans une autre et de les récupérer à tout moment sans avoir à répondre à des questions sur les décalages ou les limites de la mémoire. Disons que vous avez une personne struct, telle que définie ici :
struct person {
int age;
int salary;
char *name;
} p;
En n'ayant qu'un pointeur sur l'âge ou le salaire, vous pouvez récupérer la structure entière enveloppant (contenant) ce pointeur. Comme son nom l'indique, la macro container_of est utilisée pour trouver le conteneur du champ donné d'une structure. La macro est définie dans include/linux/kernel.h et ressemble à ce qui suit :
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
N'ayez pas peur des pointeurs ; voyez-les simplement comme suit :
container_of(pointer, container_type, container_field);
Voici les éléments du fragment de code précédent :
- pointeur : Il s'agit du pointeur vers le champ de la structure.
- container_type : Il s'agit du type de structure enveloppant (contenant) le pointeur.
- container_field : Il s'agit du nom du champ auquel pointeur pointe à l'intérieur de la structure
Considérons le conteneur suivant :
struct person {
int age;
int salary;
char *name;
};
Maintenant, considérons une de ses instances, ainsi qu'un pointeur vers le membre age :
struct person somebody;
[...]
int *age_ptr = &somebody.age;
En plus d'un pointeur sur le membre de nom (age_ptr), vous pouvez utiliser la macro container_of afin d'obtenir un pointeur sur la structure entière (container) qui englobe ce membre en utilisant ce qui suit :
struct person *the_person;
the_person = container_of(age_ptr, struct person, age);
container_of prend en compte le décalage de age au début de la structure pour obtenir l'emplacement correct du pointeur. Si vous soustrayez le décalage du champ age du pointeur age_ptr, vous obtiendrez l'emplacement correct. C'est ce que fait la dernière ligne de la macro :
(type *)( (char *)__mptr - offsetof(type,member) );
En appliquant cela à un exemple réel, on obtient ce qui suit :
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 est principalement utilisée dans les conteneurs génériques du noyau.
C'est tout ce qui concerne la macro container_of dans le noyau.