Maison >Tutoriel système >Linux >Modèle de périphérique Linux (3)_Uevent
Uevent fait partie de Kobject et est utilisé pour avertir les programmes de l'espace utilisateur lorsque l'état de Kobject change (comme l'ajout, la suppression, etc.). Après avoir reçu un tel événement, le programme de l'espace utilisateur le gérera en conséquence.
Normalement, ce mécanisme est utilisé pour prendre en charge les appareils enfichables à chaud. Par exemple, lorsqu'une clé USB est insérée, le pilote USB crée dynamiquement une structure de périphérique (y compris le kobject correspondant) utilisée pour représenter la clé USB et informer le programme de l'espace utilisateur. Cela permettra de créer dynamiquement des nœuds de périphériques dans le répertoire /dev/. En outre, ce mécanisme peut également demander à d'autres applications de monter le périphérique de clé USB dans le système, réalisant ainsi une prise en charge dynamique du périphérique.
L'image suivante décrit l'emplacement du module Uevent dans le noyau :
On peut voir que le mécanisme d'Uevent est relativement simple. Lorsqu'un appareil du modèle d'appareil présente un événement qui doit être signalé, l'interface fournie par Uevent sera déclenchée. Une fois que le module Uevent a préparé le format de rapport de l'événement, il peut signaler l'événement à l'espace utilisateur de deux manières : l'une consiste à appeler directement le fichier exécutable de l'espace utilisateur via le module kmod ; l'autre consiste à utiliser la communication netlink ; Le mécanisme permettant de transférer l'événement depuis l'espace noyau est transmis à l'espace utilisateur.
Remarque 1 : Kmod et netlink seront décrits dans d'autres articles, cet article ne les expliquera donc pas en détail.
Le code de Uevent est relativement simple, impliquant principalement deux fichiers : kobject.h et kobject_uevent.c, comme suit :
kobject.h définit les constantes et les structures de données liées à l'événement, comme suit :
1: /* include/linux/kobject.h, line 50 */ 2: enum kobject_action { 3: KOBJ_ADD, 4: KOBJ_REMOVE, 5: KOBJ_CHANGE, 6: KOBJ_MOVE, 7: KOBJ_ONLINE, 8: KOBJ_OFFLINE, 9: KOBJ_MAX 10: };
kobject_action définit le type d'événement, notamment :
«
AJOUTER/Supprimer, Kobject (ou structure de données supérieure) ajoute/supprime des événements.
ONLINE/OFFLINE, l'événement en ligne/hors ligne de Kobject (ou structure de données de couche supérieure), indique en fait s'il est activé ou non.
CHANGE, l'état ou le contenu du Kobject (ou de la structure de données supérieure) change.
MOVE, Kobject (ou structure de données supérieure) change le nom ou change le parent (ce qui signifie que la structure des répertoires est modifiée dans sysfs).
CHANGE, si l'événement que le pilote de périphérique doit signaler n'entre plus dans la portée des événements ci-dessus, ou est un événement personnalisé, vous pouvez utiliser cet événement et transporter les paramètres correspondants.
”
1: /* include/linux/kobject.h, line 31 */ 2: #define UEVENT_NUM_ENVP 32 /* number of env pointers */ 3: #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ 4: 5: /* include/linux/kobject.h, line 116 */ 6: struct kobj_uevent_env { 7: char *envp[UEVENT_NUM_ENVP]; 8: int envp_idx; 9: char buf[UEVENT_BUFFER_SIZE]; 10: int buflen; 11: };
Comme mentionné précédemment, lorsque vous utilisez Kmod pour signaler un événement à l'espace utilisateur, le fichier exécutable dans l'espace utilisateur sera exécuté directement. Dans les systèmes Linux, l'exécution des fichiers exécutables dépend des variables d'environnement, donc kobj_uevent_env est utilisé pour organiser les variables d'environnement lors du rapport de cet événement.
«
envp,指针数组,用于保存每个环境变量的地址,最多可支持的环境变量数量为UEVENT_NUM_ENVP。
envp_idx,用于访问环境变量指针数组的index。
buf,保存环境变量的buffer,最大为UEVENT_BUFFER_SIZE。
buflen,访问buf的变量。
”
1: /* include/linux/kobject.h, line 123 */ 2: struct kset_uevent_ops { 3: int (* const filter)(struct kset *kset, struct kobject *kobj); 4: const char *(* const name)(struct kset *kset, struct kobject *kobj); 5: int (* const uevent)(struct kset *kset, struct kobject *kobj, 6: struct kobj_uevent_env *env); 7: };
kset_uevent_ops是为kset量身订做的一个数据结构,里面包含filter和uevent两个回调函数,用处如下:
“
filter,当任何Kobject需要上报uevent时,它所属的kset可以通过该接口过滤,阻止不希望上报的event,从而达到从整体上管理的目的。
name,该接口可以返回kset的名称。如果一个kset没有合法的名称,则其下的所有Kobject将不允许上报uvent
uevent,当任何Kobject需要上报uevent时,它所属的kset可以通过该接口统一为这些event添加环境变量。因为很多时候上报uevent时的环境变量都是相同的,因此可以由kset统一处理,就不需要让每个Kobject独自添加了。
”
通过kobject.h,uevent模块提供了如下的API(这些API的实现是在”lib/kobject_uevent.c”文件中):
1: /* include/linux/kobject.h, line 206 */ 2: int kobject_uevent(struct kobject *kobj, enum kobject_action action); 3: int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, 4: char *envp[]); 5: 6: __printf(2, 3) 7: int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...); 8: 9: int kobject_action_type(const char *buf, size_t count, 10: enum kobject_action *type);
“
kobject_uevent_env,以envp为环境变量,上报一个指定action的uevent。环境变量的作用是为执行用户空间程序指定运行环境。具体动作如下:
- Recherchez si kobj lui-même ou son parent appartient à un kset, sinon, une erreur sera signalée (Remarque 2 : cela peut expliquer que si un kobject ne rejoint pas un kset, il n'est pas autorisé à signaler un uevent)
- Vérifiez si kobj->uevent_suppress est défini. S'il est défini, ignorez tous les rapports uevent et revenez (Remarque 3 : on peut voir à partir de cela que le rapport uevent de Kobject peut être contrôlé via l'indicateur uevent_suppress de Kobject)
- Si le kset auquel il appartient possède la fonction uevent_ops->filter, appelez cette fonction pour filtrer le rapport (Remarque 4 : cela prend en charge la description de l'interface de filtre dans la section 3.2. Le kset peut filtrer les événements qui ne souhaitent pas être signalés via l'interface de filtre, afin d'atteindre l'effet de gestion global)
.- Déterminez si le kset auquel il appartient a un nom légal (appelé sous-système, qui est différent de la version précédente du noyau), sinon il n'est pas autorisé à signaler uevent
- Allouez un tampon pour stocker les variables d'environnement pour ce rapport (le résultat est enregistré dans le pointeur env) et obtenez les informations de chemin du Kobject dans sysfs (le logiciel de l'espace utilisateur doit y accéder dans sysfs en fonction des informations de chemin)
- Appelez l'interface add_uevent_var (décrite ci-dessous) pour ajouter une action, des informations sur le chemin, un sous-système et d'autres informations au pointeur d'environnement
- Si l'environnement entrant n'est pas vide, analysez la variable d'environnement entrante, appelez également l'interface add_uevent_var et ajoutez-la au pointeur d'environnement
- Si le kset auquel il appartient possède l'interface uevent_ops->uevent, appelez cette interface et ajoutez les variables d'environnement unifiées du kset au pointeur env
- Selon le type d'ACTION, définissez les variables kobj->state_add_uevent_sent et kobj->state_remove_uevent_sent pour enregistrer le statut correct
- Appelez l'interface add_uevent_var et ajoutez le numéro de série au format "SEQNUM=%llu"
- Si "CONFIG_NET" est défini, utilisez netlink pour envoyer l'uevent
- En prenant uevent_helper, le sous-système et le pointeur env avec les variables d'environnement standard (HOME=/, PATH=/sbin:/bin:/usr/sbin:/usr/bin) comme paramètres, appelez la fonction call_usermodehelper fournie par le module kmod et signalez uevent .
Le contenu de uevent_helper est déterminé par l'élément de configuration du noyau CONFIG_UEVENT_HELPER_PATH (situé dans ./drivers/base/Kconfig) (voir lib/kobject_uevent.c, ligne 32). Cet élément de configuration spécifie un programme d'espace utilisateur (ou un script d'utilisation). Utilisé pour analyser l'événement uevent signalé, tel que "/sbin/hotplug".
La fonction de call_usermodehelper est de créer un processus, de prendre uevent comme paramètre et d'exécuter uevent_helper.kobject_uevent a la même fonction que kobject_uevent_env, sauf qu'aucune variable d'environnement n'est spécifiée.
add_uevent_var, copie la variable d'environnement vers le pointeur env sous forme de caractères de formatage (similaires à printf, printk, etc.).
kobject_action_type, convertit l'action de type enum kobject_action en une chaîne.
”
说明:怎么指定处理uevent的用户空间程序(简称uevent helper)?
上面介绍kobject_uevent_env的内部动作时,有提到,Uevent模块通过Kmod上报Uevent时,会通过call_usermodehelper函数,调用用户空间的可执行文件(或者脚本,简称**uevent helper** )处理该event。而该uevent helper的路径保存在**uevent_helper数组中。
可以在编译内核时,通过CONFIG_UEVENT_HELPER_PATH配置项,静态指定uevent helper。但这种方式会为每个event fork一个进程,随着内核支持的设备数量的增多,这种方式在系统启动时将会是致命的(可以导致内存溢出等)。因此只有在早期的内核版本中会使用这种方式,现在内核不再推荐使用该方式。因此内核编译时,需要把该配置项留空。
在系统启动后,大部分的设备已经ready,可以根据需要,重新指定一个uevent helper,以便检测系统运行过程中的热拔插事件。这可以通过把helper的路径写入到"/sys/kernel/uevent_helper”文件中实现。实际上,内核通过sysfs文件系统的形式,将uevent_helper数组开放到用户空间,供用户空间程序修改访问,具体可参考"****./kernel/ksysfs.c”中相应的代码,这里不再详细描述。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!