Definition:
#define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
Signification:
La macro "list_entry" est utilisée pour retrouver un pointeur de struct parent, en utilisant seulement un pointeur de struct interne.
Exemple:
struct __wait_queue { unsigned int flags; struct task_struct * task; struct list_head task_list; }; struct list_head { struct list_head *next, *prev; }; // et avec la définition type: typedef struct __wait_queue wait_queue_t; // nous aurons wait_queue_t *out list_entry(tmp, wait_queue_t, task_list); // où tmp pointe vers list_head
Ainsi, dans ce cas, au moyen du pointeur *tmp [list_head] nous retrouvons le pointeur *out [wait_queue_t].
____________ <---- *out [nous calculons que] |flags | /|\ |task *--> | | |task_list |<---- list_entry | prev * -->| | | | next * -->| | | |____________| ----- *tmp [nous avons ça]
Fichiers:
Fonctions:
Fonctions appelées:
Analyse D'InterCallings:
|sleep_on |init_waitqueue_entry -- |__add_wait_queue | enqueuing request to resource list |list_add | |__list_add -- |schedule --- waiting for request to be executed |__remove_wait_queue -- |list_del | dequeuing request from resource list |__list_del --
Description:
Sous Linux chaque ressource (idéalement un objet partagé par plusieurs utilisateurs et plusieurs processus), a une file pour gérer TOUTES les Tâches qui la demandant.
Cette file s'appelle l"file d'attente" et elle consiste en plusieurs éléments que nous appellerons l"élément de file d'attente":
*** structure de file d'attente [include/linux/wait.h] *** struct __wait_queue { unsigned int flags; struct task_struct * task; struct list_head task_list; } struct list_head { struct list_head *next, *prev; };
Fonctionnement de graphique:
*** élément de file d'attente *** /|\ | <--[préc *, drapeau, tâchesk *, suiv *]--> *** liste de file d'attente *** /|\ /|\ /|\ /|\ | | | | --> <--[tâch1]--> <--[tâch2]--> <--[tâche3]--> .... <--[tâcheN]--> <-- | | |__________________________________________________________________| *** tête de file d'attente *** tâche1 <--[préc *, blocage, suiv *]--> tâcheN
"tête de file d'attente d'attente" pointe vers premier (avec suiv *) et dernier (avec préc *) éléments de la "liste de file d'attente".
Quand un nouvel élément doit être ajouté, "__add_wait_queue" [include/linux/wait.h] est appellé, après quoi la routine générique "list_add" [include/linux/wait.h], est exécutée:
*** function list_add [include/linux/list.h] *** // classic double link list insert static __inline__ void __list_add (struct list_head * new, \ struct list_head * prev, \ struct list_head * next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; }
Pour compléter la description, nous voyons aussi la fonction "__list_del" [include/linux/list.h] appelée par "list_del" [include/linux/list.h] dans "remove_wait_queue" [include/linux/wait.h]:
*** fonction list_del [include/linux/list.h] *** // classic double link list delete static __inline__ void __list_del (struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; }
Une liste type (ou la file) est habituellement gérée en l'allouant dans le Tas (voir le Chap.10 pour la définition de Tas et Pile au sujet et voir où les variables sont assignées). Autrement ici, nous allouons statiquement les données File d'Attente dans une variable locale (Pile), puis la fonction est interrompue par le programme, à la fin, (retournant du programme (returning from scheduling)) effacera la variable locale.
new task <----| task1 <------| task2 <------| | | | | | | |..........| | |..........| | |..........| | |wait.flags| | |wait.flags| | |wait.flags| | |wait.task_|____| |wait.task_|____| |wait.task_|____| |wait.prev |--> |wait.prev |--> |wait.prev |--> |wait.next |--> |wait.next |--> |wait.next |--> |.. | |.. | |.. | |schedule()| |schedule()| |schedule()| |..........| |..........| |..........| |__________| |__________| |__________| Stack Stack Stack